लॉग (x) के तेजी से सन्निकटन के बारे में


10

मैंने कुछ समय पहले एक कोड लिखा था जिसमें पुस्तकालय कार्यों का उपयोग किए बिना गणना करने का प्रयास किया गया था । कल, मैं पुराने कोड की समीक्षा कर रहा था, और मैंने इसे जितनी जल्दी हो सके बनाने की कोशिश की, (और सही)। यहाँ मेरा अब तक का प्रयास है:log(x)

const double ee = exp(1);

double series_ln_taylor(double n){ /* n = e^a * b, where a is an non-negative integer */
    double lgVal = 0, term, now;
    int i, flag = 1;

    if ( n <= 0 ) return 1e-300;
    if ( n * ee < 1 )
        n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */

    for ( term = 1; term < n ; term *= ee, lgVal++ );
    n /= term;

    /* log(1 - x) = -x - x**2/2 - x**3/3... */
    n = 1 - n;
    now = term = n;
    for ( i = 1 ; ; ){
        lgVal -= now;
        term *= n;
        now = term / ++i;
        if ( now < 1e-17 ) break;
    }

    if ( flag == -1 ) lgVal = -lgVal;

    return lgVal;
}

यहाँ मैं ऐसा खोजने की कोशिश कर रहा हूँ कि e a अभी n पर है, और फिर मैं n का लघुगणक मान जोड़ता हूँaea , जो 1. से कम है। इस बिंदु पर,log(1-x)के टेलर विस्तार काउपयोग बिना किसी चिंता के किया जा सकता है।nealog(1  x)

मैंने हाल ही में संख्यात्मक विश्लेषण में रुचि पैदा की है, और इसीलिए मैं यह सवाल पूछने में मदद नहीं कर सकता कि यह कोड सेगमेंट कितनी तेजी से चल सकता है, जबकि यह काफी सही है? क्या मुझे कुछ अन्य तरीकों पर स्विच करने की आवश्यकता है, उदाहरण के लिए, इस तरह से जारी अंश का उपयोग करना ?

सी मानक पुस्तकालय के साथ प्रदान की समारोह लगभग 5.1 गुना इस कार्यान्वयन की तुलना में तेजी है।log(x)

अद्यतन 1 : विकिपीडिया में उल्लिखित हाइपरबोलिक आर्कटिक श्रृंखला का उपयोग करते हुए , गणना सी मानक पुस्तकालय लॉग फ़ंक्शन की तुलना में लगभग 2.2 गुना धीमी लगती है। हालाँकि, मैंने बड़े पैमाने पर प्रदर्शन की जाँच नहीं की है, और बड़ी संख्या के लिए, मेरा वर्तमान कार्यान्वयन वास्तव में धीमा प्रतीत होता है। यदि मैं प्रबंधित कर सकता हूं तो मैं अपने दोनों कार्यान्वयन को चेक बाउंड की संख्या के लिए त्रुटि बाउंड और औसत समय के लिए जाँचना चाहता हूँ। यहाँ मेरा दूसरा प्रयास है।

double series_ln_arctanh(double n){ /* n = e^a * b, where a is an non-negative integer */
    double lgVal = 0, term, now, sm;
    int i, flag = 1;

    if ( n <= 0 ) return 1e-300;
    if ( n * ee < 1 ) n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */

    for ( term = 1; term < n ; term *= ee, lgVal++ );
    n /= term;

    /* log(x) = 2 arctanh((x-1)/(x+1)) */
    n = (1 - n)/(n + 1);

    now = term = n;
    n *= n;
    sm = 0;
    for ( i = 3 ; ; i += 2 ){
        sm += now;
        term *= n;
        now = term / i;
       if ( now < 1e-17 ) break;
    }

    lgVal -= 2*sm;

    if ( flag == -1 ) lgVal = -lgVal;
    return lgVal;
}

किसी भी सुझाव या आलोचना की सराहना की है।

1e81e3084e15

double series_ln_better(double n){ /* n = e^a * b, where a is an non-negative integer */
    double lgVal = 0, term, now, sm;
    int i, flag = 1;

    if ( n == 0 ) return -1./0.; /* -inf */
    if ( n < 0 ) return 0./0.;   /* NaN*/
    if ( n < 1 ) n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */

    /* the cutoff iteration is 650, as over e**650, term multiplication would
       overflow. For larger numbers, the loop dominates the arctanh approximation
       loop (with having 13-15 iterations on average for tested numbers so far */

    for ( term = 1; term < n && lgVal < 650 ; term *= ee, lgVal++ );
    if ( lgVal == 650 ){
        n /= term;
        for ( term = 1 ; term < n ; term *= ee, lgVal++ );
    }
    n /= term;

    /* log(x) = 2 arctanh((x-1)/(x+1)) */
    n = (1 - n)/(n + 1);

    now = term = n;
    n *= n;
    sm = 0;

    /* limiting the iteration for worst case scenario, maximum 24 iteration */
    for ( i = 3 ; i < 50 ; i += 2 ){
        sm += now;
        term *= n;
        now = term / i;
        if ( now < 1e-17 ) break;
    }

    lgVal -= 2*sm;

    if ( flag == -1 ) lgVal = -lgVal;

    return lgVal;
}

जवाबों:


17

यह वास्तव में एक आधिकारिक जवाब नहीं है, मुद्दों की एक सूची जो मुझे लगता है कि आपको विचार करना चाहिए, और मैंने आपके कोड का परीक्षण नहीं किया है।

log2.15.1

f(x)doublen12

n1.7976e+308term=infn=11017nterm *= e709.78266108405500745

1030000

मुझे संदेह है कि थोड़े से प्रयास से आप प्रदर्शन रेंज के लिए उस मजबूती के कुछ त्याग कर सकते हैं, उदाहरण के लिए, तर्क सीमा को सीमित करके या थोड़ा सटीक सटीक परिणाम देकर।

3. इस तरह के कोड का प्रदर्शन सीपीयू आर्किटेक्चर पर बहुत कुछ निर्भर कर सकता है। यह एक गहरा और शामिल विषय है, लेकिन इंटेल जैसे सीपीयू निर्माता ऑप्टिमाइज़ेशन गाइड प्रकाशित करते हैं जो आपके कोड और उस पर चल रहे सीपीयू के बीच के विभिन्न इंटरैक्शन को समझाते हैं। कैशिंग अपेक्षाकृत सीधा हो सकता है, लेकिन डेटा निर्भरता के कारण शाखा भविष्यवाणी, अनुदेश-स्तरीय समानता और पाइपलाइन स्टाल जैसी चीजें उच्च-स्तरीय कोड में ठीक से देखना मुश्किल है, लेकिन प्रदर्शन के लिए बहुत मायने रखता है।

x~y~=f~(x~)y=f(x~)सही?)। फ्लोटिंग-पॉइंट राउंडऑफ़ त्रुटियों की उपस्थिति के कारण, टेलर श्रृंखला के अभिसरण को दर्शाने के समान नहीं है।

4.5। सटीकता के लिए एक अप्रयुक्त फ़ंक्शन का परीक्षण करने का एक अच्छा तरीका, इसका मूल्यांकन चार बिलियन में से हर एक पर करना होगा (कम यदि आप तर्क को सही ढंग से कम कर रहे हैं, यहाँ) एकल-सटीक फ़्लोट्स, और मानक लॉग से त्रुटियों की तुलना करें libm। थोड़ा समय लगता है, लेकिन कम से कम यह पूरी तरह से है।

5. क्योंकि आप शुरू से जानते हैं कि युगल की सटीकता, आपके पास एक अनबाउंड लूप होना जरूरी नहीं है: पुनरावृत्तियों की संख्या का पता लगाया जा सकता है (यह लगभग 50 है)। अपने कोड से शाखाओं को निकालने के लिए इसका उपयोग करें, या कम से कम पुनरावृत्तियों की संख्या पहले से निर्धारित करें।

लूप अनरोलिंग के बारे में सभी सामान्य विचार भी लागू होते हैं।

6. टेलर श्रृंखला के अलावा अन्य सन्निकटन तकनीकों का उपयोग करना संभव है। चेबीशेव श्रृंखला (क्लेन्शव पुनरावृत्ति के साथ), पेड सन्निकट और कभी-कभी रूट-फाइंडिंग मेथड जैसे न्यूटन की विधि भी होती है, जब भी आपके फ़ंक्शन को एक सरल फ़ंक्शन (जैसे, प्रसिद्ध डार्कर ट्रिक ) की जड़ के रूप में पुनर्निर्मित किया जा सकता है ।

निरंतर अंश संभवतः बहुत महान नहीं होने जा रहे हैं, क्योंकि वे विभाजन को शामिल करते हैं, जो कि गुणकों / जोड़ो की तुलना में बहुत अधिक महंगा है। आप को देखो, तो _mm_div_ssपर https://software.intel.com/sites/landingpage/IntrinsicsGuide/ , विभाजन 3-5 / 0.5-1 के साथ तुलना में 13-14 चक्र का और 5-14 के throughput विलंबता है, वास्तुकला के आधार पर, / गुणा / जोड़ के लिए। तो सामान्य तौर पर (हमेशा नहीं) यह संभव है कि जितना संभव हो डिवीजनों को खत्म करने की कोशिश करें।

दुर्भाग्य से, गणित नहीं है इस तरह के , यहाँ एक महान गाइड क्योंकि साथ भाव कम फार्मूले जरूरी सबसे तेजी से लोगों को नहीं कर रहे हैं। गणित उदाहरण के लिए विभाजन को दंडित नहीं करता है।

7. फ्लोटिंग-पॉइंट नंबर आंतरिक रूप से में संग्रहीत किए जाते हैंx=m×2em12<m1exfrexp

8. में या (जैसे: https://github.com/JuliaLang/openlibm/blob/master/src/e_log.c ) के logसाथ अपनी तुलना करें । यह अब तक का सबसे आसान तरीका है यह पता लगाने के लिए कि अन्य लोग पहले से क्या पता लगा चुके हैं। सीपीयू निर्माताओं के लिए विशेष रूप से अनुकूलित संस्करण भी हैं , लेकिन आमतौर पर उनके स्रोत कोड प्रकाशित नहीं होते हैं।loglibmopenlibmlibm

बूस्ट :: एसएफ के कुछ विशेष कार्य हैं, लेकिन मूल नहीं हैं। यह लॉग 1 पी के स्रोत को देखने के लिए शिक्षाप्रद हो सकता है, हालांकि: http://www.boost.org/doc/libs/1_58_0/libs/math/doc/html/math_toolkit/powers-log1p.html

वहाँ भी खुले स्रोत मनमाना परिशुद्धता अंकगणितीय पुस्तकालय हैं, जैसे कि एमएफआरई, जो आवश्यक उच्च सटीकता की वजह से लिब्रम से अलग एल्गोरिदम का उपयोग कर सकते हैं।

9. हिघम की सटीकता और संख्यात्मक एल्गोरिदम की स्थिरता संख्यात्मक एल्गोरिदम की त्रुटियों का विश्लेषण करने के लिए एक अच्छा ऊपरी स्तर का परिचय है। स्वयं सन्निकटन एल्गोरिदम के लिए, ट्रेफेथेन द्वारा अनुमोदन सिद्धांत सिद्धांत अभ्यास एक अच्छा संदर्भ है।

10. मुझे पता है कि यह बहुत बार कहा जाता है, लेकिन यथोचित बड़ी सॉफ्टवेयर परियोजनाएं शायद ही कभी एक छोटे से समारोह के रनटाइम पर निर्भर करती हैं। जब तक आप अपने कार्यक्रम को पूरा न कर लें और सुनिश्चित करें कि यह महत्वपूर्ण है, तब तक लॉग के प्रदर्शन के बारे में चिंता करना इतना आम नहीं है


26414e15

1.13e13term

 1e8

1
k=11071lnk

2
frexp x=m×2elnx=eln2+lnm

5

किरिल का जवाब पहले ही बड़ी संख्या में प्रासंगिक मुद्दों पर छू गया था। मैं व्यावहारिक गणित पुस्तकालय डिजाइन अनुभव के आधार पर उनमें से कुछ पर विस्तार करना चाहूंगा। एक नोट अप फ्रंट: गणित लाइब्रेरी डिजाइनर हर प्रकाशित एल्गोरिथम अनुकूलन का उपयोग करते हैं, साथ ही कई मशीन विशिष्ट अनुकूलन भी करते हैं, जो सभी प्रकाशित नहीं होंगे। कोड को अक्सर संकलित कोड के बजाय असेंबली भाषा में लिखा जाएगा। इसलिए यह संभावना नहीं है कि एक सीधा और संकलित कार्यान्वयन समान उच्च गुणवत्ता वाले सेट (सटीकता, विशेष मामलों को संभालने, त्रुटि रिपोर्टिंग, राउंडिंग-मोड समर्थन) को मानते हुए, मौजूदा उच्च-गुणवत्ता वाले गणित पुस्तकालय कार्यान्वयन के 75% से अधिक प्रदर्शन को प्राप्त करेगा।

explogerfcΓ

सटीकता का मूल्यांकन आमतौर पर (तृतीय-पक्ष) उच्च-परिशुद्धता संदर्भ के साथ तुलना करके किया जाता है। एकल-तर्क एकल-सटीक कार्यों को आसानी से आसानी से परीक्षण किया जा सकता है, अन्य कार्यों के लिए (निर्देशित) यादृच्छिक परीक्षण वैक्टर के साथ परीक्षण की आवश्यकता होती है। स्पष्ट रूप से कोई भी सटीक रूप से सटीक संदर्भ परिणामों की गणना नहीं कर सकता है, लेकिन टेबल-मेकर की दुविधा में शोध बताता है कि कई सरल कार्यों के लिए यह लक्ष्य सटीकता के बारे में तीन गुना की सटीकता के साथ एक संदर्भ की गणना करने के लिए पर्याप्त है। उदाहरण के लिए देखें:

विन्सेन्ट लेफ्रे, जीन-मिशेल मुलर, "डबल परिशुद्धता में प्राथमिक क्रियाओं के सही गोलाई के लिए सबसे खराब मामले"। में कंप्यूटर अंकगणित पर कार्यवाही 15 वें आईईईई संगोष्ठी , 2001,111-118)। (ऑनलाइन छाप)

प्रदर्शन के मामले में, किसी को विलंबता के लिए अनुकूलन के बीच अंतर करना पड़ता है (महत्वपूर्ण जब निर्भर संचालन के निष्पादन समय को देखता है), बनाम थ्रूपुट के लिए अनुकूलन (जब स्वतंत्र संचालन के निष्पादन के समय पर विचार करता है)। पिछले बीस वर्षों में, हार्डवेयर समतलीकरण तकनीकों का प्रसार जैसे कि निर्देश स्तर समांतरवाद (जैसे सुपरसर्कर, आउट-ऑफ-ऑर्डर प्रोसेसर), डेटा-लेवल समानता (जैसे SIMD निर्देश), और थ्रेड-लेवल समानता (जैसे हाइपर-थ्रेडिंग,) मल्टी-कोर प्रोसेसर) कम्प्यूटेशनल थ्रूपुट पर अधिक प्रासंगिक मीट्रिक के रूप में जोर देता है।

log(1+x)=p(x)log(x)=2atanh((x1)/(x+1))=p(((x1)/(x+1))2)p

पहले से 25 साल पहले आईबीएम द्वारा शुरू किया गया था, और अब सभी प्रमुख प्रोसेसर आर्किटेक्चर पर उपलब्ध है, बहु- ऐडेड ऑपरेशन ( एफएमए ), आधुनिक गणित पुस्तकालय कार्यान्वयन का एक महत्वपूर्ण निर्माण खंड है। यह राउंडिंग एरर रिडक्शन प्रदान करता है, घटाव रद्दीकरण के खिलाफ सीमित सुरक्षा देता है , और डबल-डबल अंकगणित को बहुत सरल करता है ।

C99log()C99fma()233

#include <math.h>

/* compute natural logarithm

   USE_ATANH == 1: maximum error found: 0.83482 ulp @ 0.7012829191167614
   USE_ATANH == 0: maximum error found: 0.83839 ulp @ 1.2788954397331760
*/
double my_log (double a)
{
    const double LOG2_HI = 0x1.62e42fefa39efp-01; // 6.9314718055994529e-01
    const double LOG2_LO = 0x1.abc9e3b39803fp-56; // 2.3190468138462996e-17
    double m, r, i, s, t, p, f, q;
    int e;

    m = frexp (a, &e);
    if (m < 0.70703125) { // 181/256
        m = m + m;
        e = e - 1;
    }
    i = (double)e;

    /* m in [181/256, 362/256] */

#if USE_ATANH
    /* Compute q = (m-1) / (m+1) */
    p = m + 1.0;
    m = m - 1.0;
    q = m / p;

    /* Compute (2*atanh(q)/q-2*q) as p(q**2), q in [-75/437, 53/309] */
    s = q * q;
    r =             0x1.2f1da230fb057p-3;  // 1.4800574027992994e-1
    r = fma (r, s,  0x1.399f73f934c01p-3); // 1.5313616375223663e-1
    r = fma (r, s,  0x1.7466542530accp-3); // 1.8183580149169243e-1
    r = fma (r, s,  0x1.c71c51a8bf129p-3); // 2.2222198291991305e-1
    r = fma (r, s,  0x1.249249425f140p-2); // 2.8571428744887228e-1
    r = fma (r, s,  0x1.999999997f6abp-2); // 3.9999999999404662e-1
    r = fma (r, s,  0x1.5555555555593p-1); // 6.6666666666667351e-1
    r = r * s;

    /* log(a) = 2*atanh(q) + i*log(2) = LOG2_LO*i + p(q**2)*q + 2q + LOG2_HI*i.
       Use K.C. Ng's trick to improve the accuracy of the computation, like so:
       p(q**2)*q + 2q = p(q**2)*q + q*t - t + m, where t = m**2/2.
    */
    t = m * m * 0.5;
    r = fma (q, t, fma (q, r, LOG2_LO * i)) - t + m;
    r = fma (LOG2_HI, i, r);

#else // USE_ATANH

    /* Compute f = m -1 */
    f = m - 1.0;
    s = f * f;

    /* Approximate log1p (f), f in [-75/256, 106/256] */
    r = fma (-0x1.961d64ddd82b6p-6, f, 0x1.d35fd598b1362p-5); // -2.4787281515616676e-2, 5.7052533321928292e-2
    t = fma (-0x1.fcf5138885121p-5, f, 0x1.b97114751d726p-5); // -6.2128580237329929e-2, 5.3886928516403906e-2
    r = fma (r, s, t);
    r = fma (r, f, -0x1.b5b505410388dp-5); // -5.3431043874398211e-2
    r = fma (r, f,  0x1.dd660c0bd22dap-5); //  5.8276198890387668e-2
    r = fma (r, f, -0x1.00bda5ecdad6fp-4); // -6.2680862565391612e-2
    r = fma (r, f,  0x1.1159b2e3bd0dap-4); //  6.6735934054864471e-2
    r = fma (r, f, -0x1.2489f14dd8883p-4); // -7.1420614809115476e-2
    r = fma (r, f,  0x1.3b0ee248a0ccfp-4); //  7.6918491287915489e-2
    r = fma (r, f, -0x1.55557d3b497c3p-4); // -8.3333481965921982e-2
    r = fma (r, f,  0x1.745d4666f7f48p-4); //  9.0909266480136641e-2
    r = fma (r, f, -0x1.999999d959743p-4); // -1.0000000092767629e-1
    r = fma (r, f,  0x1.c71c70bbce7c2p-4); //  1.1111110722131826e-1
    r = fma (r, f, -0x1.fffffffa61619p-4); // -1.2499999991822398e-1
    r = fma (r, f,  0x1.249249262c6cdp-3); //  1.4285714290377030e-1
    r = fma (r, f, -0x1.555555555f03cp-3); // -1.6666666666776730e-1
    r = fma (r, f,  0x1.999999999759ep-3); //  1.9999999999974433e-1
    r = fma (r, f, -0x1.fffffffffff53p-3); // -2.4999999999999520e-1
    r = fma (r, f,  0x1.555555555555dp-2); //  3.3333333333333376e-1
    r = fma (r, f, -0x1.0000000000000p-1); // -5.0000000000000000e-1

    /* log(a) = log1p (f) + i * log(2) */
    p = fma ( LOG2_HI, i, f);
    t = fma (-LOG2_HI, i, p);
    f = fma ( LOG2_LO, i, f - t);
    r = fma (r, s, f);
    r = r + p;
#endif // USE_ATANH

    /* Handle special cases */
    if (!((a > 0.0) && (a <= 0x1.fffffffffffffp1023))) {
        r = a + a;  // handle inputs of NaN, +Inf
        if (a  < 0.0) r =  0.0 / 0.0; //  NaN
        if (a == 0.0) r = -1.0 / 0.0; // -Inf
    }
    return r;
}

(+1) क्या आप जानते हैं कि आम ओपन-सोर्स इम्प्लीमेंटेशन (जैसे ओपनलिबम) जितने अच्छे होते हैं, उतने ही बेहतर हो सकते हैं या उनके विशेष कार्यों को बेहतर बनाया जा सकता है?
किरिल

1
@Kirill अंतिम मैंने खुले स्रोत कार्यान्वयन को देखा (कई साल पहले), वे FMA के लाभों का फायदा नहीं उठा रहे थे। उस समय आईबीएम पावर और इंटेल इटेनियम एकमात्र आर्किटेक्चर थे, जिसमें ऑपरेशन शामिल था, अब इसके लिए हार्डवेयर समर्थन सर्वव्यापी है। इसके अलावा, टेबल-प्लस-बहुपद सन्निकटन कला की स्थिति में वापस आ गए थे, अब टेबल पक्ष से बाहर हो गए हैं: उच्च ऊर्जा उपयोग में मेमोरी एक्सेस परिणाम, वे वेक्टराइजेशन में हस्तक्षेप कर सकते हैं, और कम्प्यूटेशनल थ्रूपुट मेमोरी थ्रूपुट से अधिक बढ़ गए हैं तालिकाओं से संभावित नकारात्मक प्रदर्शन प्रभाव के परिणामस्वरूप।
njuffa
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.