C ++ में लंबे समीकरणों को लागू करते समय मैं एक उच्च-स्तरीय दृष्टिकोण के माध्यम से प्रदर्शन में सुधार कैसे कर सकता हूं


92

मैं कुछ इंजीनियरिंग सिमुलेशन विकसित कर रहा हूं। इसमें सामग्री जैसे रबर में तनाव की गणना करने के लिए इस समीकरण जैसे कुछ लंबे समीकरणों को लागू करना शामिल है:

T = (
    mu * (
            pow(l1 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a
            * (
                pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
                - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1
            ) * pow(l1 * l2 * l3, 0.1e1 / 0.3e1) / l1
            - pow(l2 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l1 / 0.3e1
            - pow(l3 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l1 / 0.3e1
        ) / a
    + K * (l1 * l2 * l3 - 0.1e1) * l2 * l3
) * N1 / l2 / l3

+ (
    mu * (
        - pow(l1 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l2 / 0.3e1
        + pow(l2 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a
        * (
            pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
            - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1
        ) * pow(l1 * l2 * l3, 0.1e1 / 0.3e1) / l2
        - pow(l3 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l2 / 0.3e1
    ) / a
    + K * (l1 * l2 * l3 - 0.1e1) * l1 * l3
) * N2 / l1 / l3

+ (
    mu * (
        - pow(l1 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l3 / 0.3e1
        - pow(l2 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l3 / 0.3e1
        + pow(l3 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a
        * (
            pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
            - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1
        ) * pow(l1 * l2 * l3, 0.1e1 / 0.3e1) / l3
    ) / a
+ K * (l1 * l2 * l3 - 0.1e1) * l1 * l2
) * N3 / l1 / l2;

मैं गलतियों से बचने के लिए (और थकाऊ बीजगणित के साथ समय बचाने के लिए) C ++ कोड उत्पन्न करने के लिए मेपल का उपयोग करता हूं। चूंकि इस कोड को हजारों (यदि लाखों नहीं) बार निष्पादित किया जाता है, तो प्रदर्शन एक चिंता का विषय है। दुर्भाग्य से गणित केवल इतना सरल है; लंबे समीकरण अपरिहार्य हैं।

इस कार्यान्वयन को अनुकूलित करने के लिए मैं क्या दृष्टिकोण अपना सकता हूं? मैं उच्च-स्तरीय रणनीतियों की तलाश कर रहा हूं जो मुझे ऐसे समीकरणों को लागू करते समय लागू करना चाहिए, ऊपर दिखाए गए उदाहरण के लिए आवश्यक रूप से विशिष्ट अनुकूलन नहीं।

मैं g ++ का उपयोग करके संकलन कर रहा हूं --enable-optimize=-O3

अपडेट करें:

मुझे पता है कि बहुत सारे दोहराया अभिव्यक्ति हैं, मैं इस धारणा का उपयोग कर रहा हूं कि संकलक इन को संभाल लेंगे; मेरे परीक्षण अब तक यह सुझाव है कि यह करता है।

l1, l2, l3, mu, a, K सभी सकारात्मक वास्तविक संख्याएँ हैं (शून्य नहीं)।

मैंने l1*l2*l3एक समान चर के साथ प्रतिस्थापित किया है J:। इससे प्रदर्शन को बेहतर बनाने में मदद मिली।

के pow(x, 0.1e1/0.3e1)साथ प्रतिस्थापित करना cbrt(x)एक अच्छा सुझाव था।

यह सीपीयू पर चलाया जाएगा, निकट भविष्य में यह संभवतः जीपीयू पर बेहतर तरीके से चलेगा, लेकिन अभी यह विकल्प उपलब्ध नहीं है।


32
अच्छी तरह से पहली बात जो मन में आती है (जब तक कि कंपाइलर इसे स्वयं ऑप्टिमाइज़ नहीं करेगा) उन सभी pow(l1 * l2 * l3, -0.1e1 / 0.3e1)को एक चर के साथ बदलना है ... आपको अपने कोड को बेंचमार्क करने की आवश्यकता है यह सुनिश्चित करने के लिए कि यह तेज़ या धीमी गति से चलता है, हालांकि।
सिंगरऑफइनफॉल

6
साथ ही इसे और अधिक पठनीय बनाने के लिए कोड को प्रारूपित करें - सुधार के लिए संभावनाओं की पहचान करने में मदद कर सकता है।
एड हील

26
क्यों सभी डाउनवोट और वोट बंद करने के लिए? आप में से जो संख्यात्मक या वैज्ञानिक प्रोग्रामिंग पसंद नहीं करते हैं, उनके लिए अन्य प्रश्न देखें। यह एक अच्छा प्रश्न है जो इस साइट के अनुकूल है। Scicomp साइट अभी भी बीटा है; प्रवास अच्छा विकल्प नहीं है। कोड समीक्षा साइट को पर्याप्त Sciomp आँखें नहीं मिलती हैं। ओपी ने वैज्ञानिक कंप्यूटिंग में बहुत बार क्या किया है: एक सांकेतिक गणित कार्यक्रम में एक समस्या का निर्माण, प्रोग्राम को कोड उत्पन्न करने के लिए कहें, और परिणाम को स्पर्श न करें क्योंकि उत्पन्न कोड एक गड़बड़ है।
डेविड हेमेन

6
@David Hammen कोड समीक्षा साइट को पर्याप्त Sciomp आँखें नहीं मिलती हैं - एक चिकन और अंडे की समस्या की तरह लग रहा है, और एक मानसिकता जो सीआर को ऐसी आँखों को प्राप्त करने में मदद नहीं कर रही है। वही scicomp बीटा साइट को बंद करने के विचार पर लागू होता है क्योंकि यह बीटा है - अगर सभी ने ऐसा ही सोचा, तो बढ़ने के लिए एकमात्र साइट Stack Overflow होगी।
मैथ्यू गुइंडन

13
इस सवाल पर यहाँ
नाथनऑलिवर

जवाबों:


88

सारांश संपादित करें

  • मेरे मूल उत्तर में केवल इस बात का उल्लेख किया गया था कि इस कोड में बहुत सारी प्रतिरूपित गणनाएँ थीं और कई शक्तियों में 1/3 के कारक शामिल थे। उदाहरण के लिए, के pow(x, 0.1e1/0.3e1)रूप में ही है cbrt(x)
  • मेरा दूसरा सम्पादन सिर्फ गलत था, और मेरी तीसरी गलती इस पर हुई। यह वही है जो लोग 'एम' अक्षर से शुरू होने वाले प्रतीकात्मक गणित कार्यक्रमों से ओरेकल जैसे परिणामों को बदलने से डरते हैं। मैंने उन एडिट्स को स्ट्राइक (यानी, स्ट्राइक ) किया है और उन्हें इस उत्तर के वर्तमान संशोधन के निचले हिस्से में धकेल दिया है। हालाँकि, मैंने उन्हें नहीं हटाया। मैं मानव हूं। हमारे लिए गलती करना आसान है।
  • मेरे चौथे संपादन ने एक बहुत ही कॉम्पैक्ट अभिव्यक्ति विकसित की है जो प्रश्न में जटिल अभिव्यक्ति का सही प्रतिनिधित्व करती है यदि मानकों l1, l2, और l3सकारात्मक वास्तविक संख्या हैं और अगर aएक गैर शून्य वास्तविक संख्या है। (हम अभी तक इन गुणांक की विशिष्ट प्रकृति के बारे में ओपी से सुनना चाहते हैं। समस्या की प्रकृति को देखते हुए, ये उचित धारणाएं हैं।)
  • यह इन समस्याओं को सरल बनाने की सामान्य समस्या का जवाब देने का प्रयास करता है।

पहली चीजें पहले

मैं गलतियों से बचने के लिए C ++ कोड उत्पन्न करने के लिए मेपल का उपयोग करता हूं।

मेपल और गणितज्ञ कभी-कभी स्पष्ट याद करते हैं। इससे भी महत्वपूर्ण बात यह है कि मेपल और गणितज्ञ के उपयोगकर्ता कभी-कभी गलतियाँ करते हैं। "कभी-कभी" के स्थान पर "अक्सर", या शायद यहां तक ​​कि "लगभग हमेशा" के स्थान पर, शायद कभी-कभी निशान के करीब होता है।

आप मेपल को सवाल में मापदंडों के बारे में बताकर उस अभिव्यक्ति को सरल बनाने में मदद कर सकते थे। उदाहरण के लिए, मुझे संदेह है कि l1, l2और l3सकारात्मक वास्तविक संख्याएं हैं और aयह एक गैर-शून्य वास्तविक संख्या है। अगर ऐसा है, तो इसे बताएं। वे प्रतीकात्मक गणित कार्यक्रम आमतौर पर मान लेते हैं कि हाथ जटिल हैं। डोमेन को प्रतिबंधित करने से प्रोग्राम को ऐसी धारणाएँ बनाने में मदद मिलती है जो जटिल संख्याओं में मान्य नहीं हैं।


सांकेतिक गणित कार्यक्रमों से उन बड़ी गड़बड़ियों को कैसे सरल बनाया जाए (यह संपादित करें)

प्रतीकात्मक गणित कार्यक्रम आम तौर पर विभिन्न मापदंडों के बारे में जानकारी प्रदान करने की क्षमता प्रदान करते हैं। उस क्षमता का उपयोग करें, खासकर यदि आपकी समस्या में विभाजन या घातांक शामिल है। हाथ में उदाहरण में, आप मेपल यह कह रही है कि द्वारा कि अभिव्यक्ति को आसान बनाने में मदद की सकता है l1, l2और l3कर रहे हैं सकारात्मक वास्तविक संख्या और कहा कि aएक गैर शून्य वास्तविक संख्या है। अगर ऐसा है, तो इसे बताएं। वे प्रतीकात्मक गणित कार्यक्रम आमतौर पर मान लेते हैं कि हाथ जटिल हैं। डोमेन को प्रतिबंधित करने से प्रोग्राम को x b x = (ab) x जैसे अनुमान लगाने की सुविधा मिलती है । यह तभी है aऔर bसकारात्मक वास्तविक संख्या हैं और अगर xवास्तविक है। यह जटिल संख्याओं में मान्य नहीं है।

अंततः, वे प्रतीकात्मक गणित कार्यक्रम एल्गोरिदम का पालन करते हैं। इसके साथ मदद करें। कोड जनरेट करने से पहले विस्तार, संग्रह, और सरलीकरण के साथ खेलने का प्रयास करें। इस मामले में, आप उन शर्तों को एकत्र कर सकते हैं जिनमें एक कारक शामिल होता है muऔर एक कारक शामिल होता है K। अपने "सरलतम रूप" के लिए एक अभिव्यक्ति को कम करना एक कला का एक सा रहता है।

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


विशिष्ट प्रश्न के बारे में

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

अगला, आप बार-बार गणना करके बहुत सारे सीपीयू बर्बाद कर रहे हैं। जब तक आपने सक्षम नहीं किया है -ffast-math, जो कंपाइलर को IEEE फ्लोटिंग पॉइंट के कुछ नियमों को तोड़ने देता है, तो कंपाइलर आपके लिए उस एक्सप्रेशन को सरल नहीं करेगा। यह बदले में वही करेगा जो आपने इसे करने के लिए कहा था। कम से कम, आपको l1 * l2 * l3उस गड़बड़ की गणना करने से पहले गणना करनी चाहिए ।

अंत में, आप बहुत से कॉल कर रहे हैं pow, जो बेहद धीमा है। ध्यान दें कि उनमें से कई कॉल फॉर्म (l1 * l2 * l3) (1/3) के हैं । उनमें से कई कॉल powएकल कॉल के साथ की जा सकती हैं std::cbrt:

l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;

इसके साथ,

  • X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)बन जाता है X * l123_pow_1_3
  • X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)बन जाता है X / l123_pow_1_3
  • X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)बन जाता है X * l123_pow_4_3
  • X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)बन जाता है X / l123_pow_4_3


मैपल स्पष्ट याद किया।
उदाहरण के लिए, लिखने का एक बहुत आसान तरीका है

(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)

यह मानते हुए कि l1, l2और l3जटिल संख्याओं के बजाय वास्तविक हैं, और यह कि वास्तविक घनमूल (सिद्धांत जटिल जड़ के बजाय) निकाले जाने हैं, उपरोक्त घटाता है

2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))

या

2.0/(3.0 * l123_pow_1_3)

के cbrt_l123बजाय का उपयोग l123_pow_1_3, प्रश्न में बुरा अभिव्यक्ति को कम कर देता है

l123 = l1 * l2 * l3; 
cbrt_l123 = cbrt(l123);
T = 
  mu/(3.0*l123)*(  pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
                 + pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
                 + pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
 +K*(l123-1.0)*(N1+N2+N3);

हमेशा डबल चेक करें, लेकिन हमेशा की तरह सरल भी करें।


यहाँ ऊपर आने में मेरे कुछ कदम हैं:

// Step 0: Trim all whitespace.
T=(mu*(pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l1-pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l1/0.3e1-pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l1/0.3e1)/a+K*(l1*l2*l3-0.1e1)*l2*l3)*N1/l2/l3+(mu*(-pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l2/0.3e1+pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l2-pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l2/0.3e1)/a+K*(l1*l2*l3-0.1e1)*l1*l3)*N2/l1/l3+(mu*(-pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l3/0.3e1-pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l3/0.3e1+pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l3)/a+K*(l1*l2*l3-0.1e1)*l1*l2)*N3/l1/l2;

// Step 1:
//   l1*l2*l3 -> l123
//   0.1e1 -> 1.0
//   0.4e1 -> 4.0
//   0.3e1 -> 3
l123 = l1 * l2 * l3;
T=(mu*(pow(l1*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l1-pow(l2*pow(l123,-1.0/3),a)*a/l1/3-pow(l3*pow(l123,-1.0/3),a)*a/l1/3)/a+K*(l123-1.0)*l2*l3)*N1/l2/l3+(mu*(-pow(l1*pow(l123,-1.0/3),a)*a/l2/3+pow(l2*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l2-pow(l3*pow(l123,-1.0/3),a)*a/l2/3)/a+K*(l123-1.0)*l1*l3)*N2/l1/l3+(mu*(-pow(l1*pow(l123,-1.0/3),a)*a/l3/3-pow(l2*pow(l123,-1.0/3),a)*a/l3/3+pow(l3*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l3)/a+K*(l123-1.0)*l1*l2)*N3/l1/l2;

// Step 2:
//   pow(l123,1.0/3) -> cbrt_l123
//   l123*pow(l123,-4.0/3) -> pow(l123,-1.0/3)
//   (pow(l123,-1.0/3)-pow(l123,-1.0/3)/3) -> 2.0/(3.0*cbrt_l123)
//   *pow(l123,-1.0/3) -> /cbrt_l123
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T=(mu*(pow(l1/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l1-pow(l2/cbrt_l123,a)*a/l1/3-pow(l3/cbrt_l123,a)*a/l1/3)/a+K*(l123-1.0)*l2*l3)*N1/l2/l3+(mu*(-pow(l1/cbrt_l123,a)*a/l2/3+pow(l2/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l2-pow(l3/cbrt_l123,a)*a/l2/3)/a+K*(l123-1.0)*l1*l3)*N2/l1/l3+(mu*(-pow(l1/cbrt_l123,a)*a/l3/3-pow(l2/cbrt_l123,a)*a/l3/3+pow(l3/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l3)/a+K*(l123-1.0)*l1*l2)*N3/l1/l2;

// Step 3:
//   Whitespace is nice.
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  (mu*( pow(l1/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l1
       -pow(l2/cbrt_l123,a)*a/l1/3
       -pow(l3/cbrt_l123,a)*a/l1/3)/a
   +K*(l123-1.0)*l2*l3)*N1/l2/l3
 +(mu*(-pow(l1/cbrt_l123,a)*a/l2/3
       +pow(l2/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l2
       -pow(l3/cbrt_l123,a)*a/l2/3)/a
   +K*(l123-1.0)*l1*l3)*N2/l1/l3
 +(mu*(-pow(l1/cbrt_l123,a)*a/l3/3
       -pow(l2/cbrt_l123,a)*a/l3/3
       +pow(l3/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l3)/a
   +K*(l123-1.0)*l1*l2)*N3/l1/l2;

// Step 4:
//   Eliminate the 'a' in (term1*a + term2*a + term3*a)/a
//   Expand (mu_term + K_term)*something to mu_term*something + K_term*something
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  (mu*( pow(l1/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l1
       -pow(l2/cbrt_l123,a)/l1/3
       -pow(l3/cbrt_l123,a)/l1/3))*N1/l2/l3
 +K*(l123-1.0)*l2*l3*N1/l2/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l2/3
       +pow(l2/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l2
       -pow(l3/cbrt_l123,a)/l2/3))*N2/l1/l3
 +K*(l123-1.0)*l1*l3*N2/l1/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l3/3
       -pow(l2/cbrt_l123,a)/l3/3
       +pow(l3/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l3))*N3/l1/l2
 +K*(l123-1.0)*l1*l2*N3/l1/l2;

// Step 5:
//   Rearrange
//   Reduce l2*l3*N1/l2/l3 to N1 (and similar)
//   Reduce 2.0/(3.0*cbrt_l123)*cbrt_l123/l1 to 2.0/3.0/l1 (and similar)
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  (mu*( pow(l1/cbrt_l123,a)*2.0/3.0/l1
       -pow(l2/cbrt_l123,a)/l1/3
       -pow(l3/cbrt_l123,a)/l1/3))*N1/l2/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l2/3
       +pow(l2/cbrt_l123,a)*2.0/3.0/l2
       -pow(l3/cbrt_l123,a)/l2/3))*N2/l1/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l3/3
       -pow(l2/cbrt_l123,a)/l3/3
       +pow(l3/cbrt_l123,a)*2.0/3.0/l3))*N3/l1/l2
 +K*(l123-1.0)*N1
 +K*(l123-1.0)*N2
 +K*(l123-1.0)*N3;

// Step 6:
//   Factor out mu and K*(l123-1.0)
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  mu*(  ( pow(l1/cbrt_l123,a)*2.0/3.0/l1
         -pow(l2/cbrt_l123,a)/l1/3
         -pow(l3/cbrt_l123,a)/l1/3)*N1/l2/l3
      + (-pow(l1/cbrt_l123,a)/l2/3
         +pow(l2/cbrt_l123,a)*2.0/3.0/l2
         -pow(l3/cbrt_l123,a)/l2/3)*N2/l1/l3
      + (-pow(l1/cbrt_l123,a)/l3/3
         -pow(l2/cbrt_l123,a)/l3/3
         +pow(l3/cbrt_l123,a)*2.0/3.0/l3)*N3/l1/l2)
 +K*(l123-1.0)*(N1+N2+N3);

// Step 7:
//   Expand
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  mu*( pow(l1/cbrt_l123,a)*2.0/3.0/l1*N1/l2/l3
      -pow(l2/cbrt_l123,a)/l1/3*N1/l2/l3
      -pow(l3/cbrt_l123,a)/l1/3*N1/l2/l3
      -pow(l1/cbrt_l123,a)/l2/3*N2/l1/l3
      +pow(l2/cbrt_l123,a)*2.0/3.0/l2*N2/l1/l3
      -pow(l3/cbrt_l123,a)/l2/3*N2/l1/l3
      -pow(l1/cbrt_l123,a)/l3/3*N3/l1/l2
      -pow(l2/cbrt_l123,a)/l3/3*N3/l1/l2
      +pow(l3/cbrt_l123,a)*2.0/3.0/l3*N3/l1/l2)
 +K*(l123-1.0)*(N1+N2+N3);

// Step 8:
//   Simplify.
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  mu/(3.0*l123)*(  pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
                 + pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
                 + pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
 +K*(l123-1.0)*(N1+N2+N3);


गलत जवाब, जानबूझकर विनम्रता के लिए रखा गया

ध्यान दें कि यह त्रस्त है। यह गलत है।

अपडेट करें

मैपल स्पष्ट याद किया। उदाहरण के लिए, लिखने का एक बहुत आसान तरीका है

(pow (l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow (l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3.11

यह मानते हुए कि l1, l2और l3जटिल संख्याओं के बजाय वास्तविक हैं, और यह कि असली घन जड़ (सिद्धांत जटिल जड़ के बजाय) निकाले जाने हैं, उपरोक्त शून्य को कम कर देता है। शून्य की यह गणना कई बार दोहराई जाती है।

दूसरा अपडेट

अगर मैंने गणित सही किया है ( कोई गारंटी नहीं है कि मैंने गणित सही किया है), प्रश्न में बुरा अभिव्यक्ति कम हो जाती है

l123 = l1 * l2 * l3; 
cbrt_l123_inv = 1.0 / cbrt(l123);
nasty_expression =
    K * (l123 - 1.0) * (N1 + N2 + N3) 
    - (  pow(l1 * cbrt_l123_inv, a) * (N2 + N3) 
       + pow(l2 * cbrt_l123_inv, a) * (N1 + N3) 
       + pow(l3 * cbrt_l123_inv, a) * (N1 + N2)) * mu / (3.0*l123);

उपरोक्त मानता है कि l1, l2और l3सकारात्मक वास्तविक संख्याएँ हैं।


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

3
@ डेडप्लिकेटर - फ्लोटिंग पॉइंट के साथ नहीं। जब तक कोई असुरक्षित गणित अनुकूलन नहीं करता है (उदाहरण के लिए, -ffast-mathgcc या क्लैग के साथ निर्दिष्ट करके ), संकलक के pow(x,-1.0/3.0)बराबर होने पर भरोसा नहीं कर सकता है x*pow(x,-4.0/3.0)। उत्तरार्द्ध कम हो सकता है जबकि पहले नहीं हो सकता है। फ्लोटिंग पॉइंट मानक के अनुरूप होने के लिए, कंपाइलर को उस गणना को शून्य पर अनुकूलित नहीं करना चाहिए।
डेविड हैमेन

ठीक है, मैं जो कुछ भी मतलब था उससे बहुत अधिक महत्वाकांक्षी हैं।
Deduplicator

1
@Deduplicator: जैसा कि मैंने एक अन्य उत्तर पर टिप्पणी की : आपको CS -fno-math-errno++ समान powकॉल के लिए g ++ की आवश्यकता है । (जब तक कि शायद यह साबित न हो जाए कि पाव को गलत तरीके से सेट करने की आवश्यकता नहीं होगी?)
पीटर कॉर्ड्स

1
@ लेफ्टी - वाल्टर के जवाब पर बहुत कुछ करो। यह तेजी से एक अच्छा सौदा है। इन सभी उत्तरों के साथ एक संभावित मुद्दा है, जो संख्यात्मक रद्द है। अपने मान लिया जाये N1, N2और N3गैर नकारात्मक कर रहे हैं, में से एक 2*N_i-(N_j+N_k)नकारात्मक हो जाएगा, एक सकारात्मक हो जाएगा, और अन्य बीच में कहीं हो जाएगा। यह आसानी से संख्यात्मक रद्द समस्याओं में परिणाम कर सकता है।
डेविड हैमेन

32

ध्यान देने वाली पहली बात यह है कि powयह वास्तव में महंगा है, इसलिए आपको जितना संभव हो इससे छुटकारा पाना चाहिए। अभिव्यक्ति के माध्यम से स्कैन मैं के कई repetitions देखने pow(l1 * l2 * l3, -0.1e1 / 0.3e1)और pow(l1 * l2 * l3, -0.4e1 / 0.3e1)। इसलिए मैं प्री-कंप्यूटिंग से एक बड़े लाभ की उम्मीद करूंगा:

 const double c1 = pow(l1 * l2 * l3, -0.1e1 / 0.3e1);
const double c2 = boost::math::pow<4>(c1);

जहां मैं बूस्ट पॉव फ़ंक्शन का उपयोग कर रहा हूं ।

इसके अलावा, आप powघातांक के साथ कुछ और है a। यदि aइंटेगर है और संकलक समय पर जाना जाता है, तो आप boost::math::pow<a>(...)आगे प्रदर्शन प्राप्त करने के लिए उन लोगों को भी बदल सकते हैं । मैं भी जैसे शब्दों को बदलने के लिए सुझाव है कि a / l1 / 0.3e1साथ a / (l1 * 0.3e1)के रूप में गुणा तेजी से तो प्रभाग है।

अंत में, यदि आप g ++ का उपयोग करते हैं, तो आप उस -ffast-mathध्वज का उपयोग कर सकते हैं जो अनुकूलक को समीकरणों को बदलने में अधिक आक्रामक होने की अनुमति देता है। इस बारे में पढ़ें कि यह झंडा वास्तव में क्या करता है , क्योंकि इसके साइड इफेक्ट्स हैं।


5
हमारे कोड में, कोड का उपयोग -ffast-mathकरने से कोड अस्थिर हो जाता है या गलत उत्तर दे सकता है। हमारे पास इंटेल कंपाइलर्स के साथ एक समान मुद्दा है और -fp-model preciseविकल्प का उपयोग करना है, अन्यथा कोड या तो उड़ा देता है या गलत उत्तर देता है। इसलिए -ffast-mathइसे गति दे सकता हूं, लेकिन मैं आपके लिंक किए गए प्रश्न में सूचीबद्ध दुष्प्रभावों के अलावा, उस विकल्प के साथ बहुत सावधानी से आगे बढ़ने की सलाह दूंगा।
tpg2114

2
@ tpg2114: मेरे परीक्षण के अनुसार, आपको-fno-math-errnopow लूप से बाहर जाने के लिए समान कॉलों को फहराने में सक्षम होने के लिए केवल g ++ की आवश्यकता है । यह सबसे कोड के लिए -फास्ट-गणित का सबसे कम "खतरनाक" हिस्सा है।
पीटर कॉर्ड्स

1
@PeterCordes दिलचस्प परिणाम हैं! जब हम वास्तव में थोड़ा कम परिशुद्धता के साथ कर सकते हैं तो काफी प्रदर्शन बढ़ जाता है टिप्पणी में उल्लेखित हैक का उपयोग करके हमने pow बहुत धीमी और समाप्त होने के साथ मुद्दे भी उठाए हैं dlsym
tpg2114

क्या GCC यह नहीं समझ पाएगी कि pow एक शुद्ध कार्य है? शायद यह अंतर्निहित ज्ञान है।
usr

6
@usr: यह सिर्फ बात है, मुझे लगता है। powहै नहीं , क्योंकि यह सेट करने के लिए माना जाता है एक शुद्ध समारोह, मानक के अनुसार, errnoकुछ परिस्थितियों में। इस तरह के झंडे स्थापित -fno-math-errnoकरने का कारण यह नहीं है सेट करना errno(इस प्रकार मानक का उल्लंघन करना), लेकिन फिर यह एक शुद्ध कार्य है और इसे इस तरह से अनुकूलित किया जा सकता है।
नैट एल्ड्रेडगे

20

वाह, क्या एक अभिव्यक्ति का नरक है। मेपल के साथ अभिव्यक्ति का निर्माण वास्तव में एक उप-विषयक विकल्प था। परिणाम केवल अपठनीय है।

  1. परिवर्तनशील नाम (एल 1, एल 2, एल 3 नहीं, बल्कि ऊंचाई, चौड़ाई, गहराई, अगर ऐसा है, तो उन्हें चुना जाता है)। तब आपके लिए अपने स्वयं के कोड को समझना आसान हो जाता है।
  2. सबमर्स की गणना करें, कि आप कई बार उपयोग करते हैं, अपफ्रंट और बोलने वाले नामों के साथ परिणामों को चर में संग्रहीत करते हैं।
  3. आप उल्लेख करते हैं, कि अभिव्यक्ति का मूल्यांकन कई बार किया जाता है। मुझे लगता है, आंतरिक सबसे अधिक लूप में केवल कुछ पैरामीटर भिन्न होते हैं। उस लूप से पहले सभी अपरिवर्तनीय सबटर्म्स की गणना करें। दूसरे आंतरिक लूप के लिए दोहराएं और तब तक जब तक कि सभी अपरिवर्तनीय लूप के बाहर न हों।

सैद्धांतिक रूप से संकलक आपके लिए वह सब करने में सक्षम होना चाहिए, लेकिन कभी-कभी यह नहीं हो सकता - जैसे कि जब लूप नेस्टिंग विभिन्न संकलन इकाइयों में कई कार्यों पर फैलता है। वैसे भी, यह आपको बेहतर पठनीय, समझने योग्य और रखरखाव योग्य कोड देगा।


8
"संकलक को यह करना चाहिए, लेकिन कभी-कभी यह नहीं होता है", यहां कुंजी है। पठनीयता के अलावा, बिल्कुल।
जेवियर

3
यदि कंपाइलर को कुछ करने की आवश्यकता नहीं है, तो यह मानना ​​कि लगभग हमेशा गलत है।
edmz

4
पुन: बोलने वाले चर नाम चुनें - जब आप गणित कर रहे होते हैं तो कई बार यह अच्छा नियम लागू नहीं होता है। जब कोड को एक वैज्ञानिक पत्रिका में एक एल्गोरिथ्म को लागू करने वाला माना जाता है, तो मैं कोड में प्रतीकों को वास्तव में उन लोगों के रूप में देखता हूं जो जर्नल में उपयोग किए जाते हैं। आमतौर पर, इसका मतलब है कि बहुत छोटे नाम, संभवतः एक सबस्क्रिप्ट के साथ।
डेविड हेमेन

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

3
@DavidHammen: ठीक है, उस स्थिति में, एक अक्षर वाले हैं "बोल नाम"। उदाहरण के लिए, जब एक 2-आयामी कार्तीय में ज्यामिति कर समन्वय प्रणाली, xऔर yकर रहे हैं नहीं व्यर्थ ही अक्षर चर, वे पूरे कर रहे हैं शब्द एक सटीक परिभाषा और एक अच्छी तरह से और व्यापक रूप से समझा अर्थ के साथ।
जोर्ग डब्ल्यू मित्तग

17

डेविड हैमेन का जवाब अच्छा है, लेकिन अभी भी इष्टतम से बहुत दूर है। आइए उसकी अंतिम अभिव्यक्ति के साथ जारी रखें (यह लिखते समय)

auto l123 = l1 * l2 * l3;
auto cbrt_l123 = cbrt(l123);
T = mu/(3.0*l123)*(  pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
                   + pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
                   + pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
  + K*(l123-1.0)*(N1+N2+N3);

जिसे आगे भी अनुकूलित किया जा सकता है। विशेष रूप से, हम कुछ गणितीय पहचानों का दोहन cbrt()करने के लिए कॉल टू एंड वन कॉल से बच सकते pow()हैं। चलिए फिर से कदम से कदम मिलाते हैं।

// step 1 eliminate cbrt() by taking the exponent into pow()
auto l123 = l1 * l2 * l3;
auto athird = 0.33333333333333333 * a; // avoid division
T = mu/(3.0*l123)*(  (N1+N1-N2-N3)*pow(l1*l1/(l2*l3),athird)
                   + (N2+N2-N3-N1)*pow(l2*l2/(l1*l3),athird)
                   + (N3+N3-N1-N2)*pow(l3*l3/(l1*l2),athird))
  + K*(l123-1.0)*(N1+N2+N3);

ध्यान दें कि मैंने भी आदि 2.0*N1को अनुकूलित किया है N1+N1, हम केवल दो कॉल के साथ कर सकते हैं pow()

// step 2  eliminate one call to pow
auto l123 = l1 * l2 * l3;
auto athird = 0.33333333333333333 * a;
auto pow_l1l2_athird = pow(l1/l2,athird);
auto pow_l1l3_athird = pow(l1/l3,athird);
auto pow_l2l3_athird = pow_l1l3_athird/pow_l1l2_athird;
T = mu/(3.0*l123)*(  (N1+N1-N2-N3)* pow_l1l2_athird*pow_l1l3_athird
                   + (N2+N2-N3-N1)* pow_l2l3_athird/pow_l1l2_athird
                   + (N3+N3-N1-N2)/(pow_l1l3_athird*pow_l2l3_athird))
  + K*(l123-1.0)*(N1+N2+N3);

चूंकि कॉल pow()यहां अब तक के सबसे महंगे ऑपरेशन हैं, इसलिए उन्हें जितना संभव हो उतना कम करने के लिए लायक है (अगला महंगा ऑपरेशन कॉल था cbrt(), जिसे हमने समाप्त कर दिया)।

यदि किसी संयोग aसे पूर्णांक है, तो कॉल को (प्लस पूर्णांक शक्तियों) powकॉल के लिए अनुकूलित किया जा सकता है cbrt, या यदि athirdआधा पूर्णांक है, तो हम sqrt(प्लस पूर्णांक शक्तियों) का उपयोग कर सकते हैं । इसके अलावा, अगर किसी भी संयोग l1==l2से l1==l3या l2==l3एक या दोनों कॉल को powसमाप्त किया जा सकता है। तो, यह विशेष मामलों के रूप में इन पर विचार करने के लायक है अगर ऐसे मौके वास्तविक रूप से मौजूद हों।


@gnat मैं आपके संपादन की सराहना करता हूं (मैंने खुद ऐसा करने के बारे में सोचा था), लेकिन इसे उचित पाया होगा, अगर डेविड का जवाब भी इस पर लिंक होगा। क्यों आप डेविड के जवाब को भी उसी तरह से संपादित नहीं करते हैं?
वाल्टर

1
मैंने केवल इसलिए संपादित किया क्योंकि मैंने आपको स्पष्ट रूप से इसका उल्लेख करते हुए देखा था; मैंने डेविड के उत्तर को फिर से पढ़ा और वहाँ पर आपके उत्तर का संदर्भ नहीं मिला। मैं संपादन से बचने की कोशिश करता हूं, जहां यह 100% स्पष्ट नहीं है कि सामान मैं लेखक के इरादों को जोड़ता हूं
gnat

1
@Walter - मेरा जवाब अब आपके लिए लिंक।
डेविड हैमेन

1
यह निश्चित रूप से मुझे नहीं था। मैंने कुछ दिन पहले आपके जवाब को गलत बताया। मुझे अपने उत्तर पर एक बेतरतीब फ्लाईबी डाउनवोट भी मिला। सामान कभी-कभी ही होता है।
डेविड हैमेन

1
आप और मैं ने एक-एक पलटी खाई है। सवाल पर सभी नीचे देखो! अब तक, प्रश्न में 16 डाउनवोट प्राप्त हुए हैं। यह भी 80 upvotes प्राप्त किया है कि उन सभी downvoters ऑफसेट से अधिक है।
डेविड हैमेन

12
  1. "कितने कई" है?
  2. इसमें कितना समय लगता है?
  3. क्या इस सूत्र के पुनर्गणना के बीच सभी पैरामीटर बदल जाते हैं? या आप कुछ पूर्व-गणना मूल्यों को कैश कर सकते हैं?
  4. मैंने उस सूत्र का एक मैनुअल सरलीकरण करने का प्रयास किया है, यह जानना चाहेगा कि क्या यह कुछ बचाता है?

    C1 = -0.1e1 / 0.3e1;
    C2 =  0.1e1 / 0.3e1;
    C3 = -0.4e1 / 0.3e1;
    
    X0 = l1 * l2 * l3;
    X1 = pow(X0, C1);
    X2 = pow(X0, C2);
    X3 = pow(X0, C3);
    X4 = pow(l1 * X1, a);
    X5 = pow(l2 * X1, a);
    X6 = pow(l3 * X1, a);
    X7 = a / 0.3e1;
    X8 = X3 / 0.3e1;
    X9 = mu / a;
    XA = X0 - 0.1e1;
    XB = K * XA;
    XC = X1 - X0 * X8;
    XD = a * XC * X2;
    
    XE = X4 * X7;
    XF = X5 * X7;
    XG = X6 * X7;
    
    T = (X9 * ( X4 * XD - XF - XG) / l1 + XB * l2 * l3) * N1 / l2 / l3 
      + (X9 * (-XE + X5 * XD - XG) / l2 + XB * l1 * l3) * N2 / l1 / l3 
      + (X9 * (-XE - XF + X6 * XD) / l3 + XB * l1 * l2) * N3 / l1 / l2;

[जोड़ा] मैंने पिछले तीन-लाइनों के फार्मूले पर कुछ और काम किया है और इसे इस सुंदरता के लिए तैयार किया है:

T = X9 / X0 * (
      (X4 * XD - XF - XG) * N1 + 
      (X5 * XD - XE - XG) * N2 + 
      (X5 * XD - XE - XF) * N3)
  + XB * (N1 + N2 + N3)

मुझे अपना काम दिखाने दो, कदम दर कदम:

T = (X9 * (X4 * XD - XF - XG) / l1 + XB * l2 * l3) * N1 / l2 / l3 
  + (X9 * (X5 * XD - XE - XG) / l2 + XB * l1 * l3) * N2 / l1 / l3 
  + (X9 * (X5 * XD - XE - XF) / l3 + XB * l1 * l2) * N3 / l1 / l2;


T = (X9 * (X4 * XD - XF - XG) / l1 + XB * l2 * l3) * N1 / (l2 * l3) 
  + (X9 * (X5 * XD - XE - XG) / l2 + XB * l1 * l3) * N2 / (l1 * l3) 
  + (X9 * (X5 * XD - XE - XF) / l3 + XB * l1 * l2) * N3 / (l1 * l2);

T = (X9 * (X4 * XD - XF - XG) + XB * l1 * l2 * l3) * N1 / (l1 * l2 * l3) 
  + (X9 * (X5 * XD - XE - XG) + XB * l1 * l2 * l3) * N2 / (l1 * l2 * l3) 
  + (X9 * (X5 * XD - XE - XF) + XB * l1 * l2 * l3) * N3 / (l1 * l2 * l3);

T = (X9 * (X4 * XD - XF - XG) + XB * X0) * N1 / X0 
  + (X9 * (X5 * XD - XE - XG) + XB * X0) * N2 / X0 
  + (X9 * (X5 * XD - XE - XF) + XB * X0) * N3 / X0;

T = X9 * (X4 * XD - XF - XG) * N1 / X0 + XB * N1 
  + X9 * (X5 * XD - XE - XG) * N2 / X0 + XB * N2
  + X9 * (X5 * XD - XE - XF) * N3 / X0 + XB * N3;


T = X9 * (X4 * XD - XF - XG) * N1 / X0 
  + X9 * (X5 * XD - XE - XG) * N2 / X0
  + X9 * (X5 * XD - XE - XF) * N3 / X0
  + XB * (N1 + N2 + N3)

2
यह ध्यान देने योग्य है, हुह? :) फोरट्रान, IIRC, कुशल सूत्र गणना के लिए डिज़ाइन किया गया था ("सूत्र" सूत्र के लिए है)।
व्लाद फेन्सटीन

अधिकांश F77 कोड मैंने देखे हैं जैसे कि (जैसे, BLAS और NR)। बहुत खुश फोरट्रान 90-> 2008 मौजूद है :)
काइल कानोस

हाँ। यदि आप एक सूत्र का अनुवाद कर रहे हैं, तो FormulaTRANslation से बेहतर तरीका क्या हो सकता है?
ब्रायन ड्रमंड

1
आपका 'अनुकूलन' गलत जगह पर हमला करता है। महंगे बिट्स कॉल हैं std::pow(), जिनमें से आपके पास अभी भी आवश्यक से 6, 3 गुना अधिक है। दूसरे शब्दों में, आपका कोड संभव से 3 गुना धीमा है।
वाल्टर

7

यह थोड़ा उलट हो सकता है, लेकिन मैंने वास्तव में हॉर्नर फॉर्म का उपयोग करके बहुपद (ऊर्जा कार्यों के प्रक्षेप) के लिए अच्छा स्पीडअप पाया है, जो मूल ax^3 + bx^2 + cx + dरूप से फिर से लिखता है d + x(c + x(b + x(a)))। यह बहुत बार-बार कॉल करने pow()से बचेगा और आपको अलग-अलग कॉल करने pow(x,6)और pow(x,7)केवल करने के बजाय मूर्खतापूर्ण चीजें करने से रोकता हैx*pow(x,6)

यह सीधे आपकी वर्तमान समस्या पर लागू नहीं होता है, लेकिन यदि आपके पास पूर्णांक बहुपदों में पूर्णांक शक्तियाँ हैं तो यह मदद कर सकता है। आपको संख्यात्मक स्थिरता और अतिप्रवाह के मुद्दों के लिए बाहर देखना पड़ सकता है क्योंकि संचालन का क्रम उसके लिए महत्वपूर्ण है (हालांकि सामान्य तौर पर मुझे लगता है कि हॉर्नर फॉर्म इसके लिए मदद करता है, क्योंकि x^20और xआमतौर पर अलग-अलग परिमाण के कई आदेश हैं)।

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


बहुरंगी रूप बहुपद कोडिंग के लिए बहुत मानक है और इस सवाल का कोई प्रासंगिकता नहीं है।
वाल्टर

1
यह सच है कि उसका उदाहरण दिया जा सकता है, लेकिन आप देखेंगे कि उसने कहा "इस प्रकार के समीकरण।" मुझे लगा कि उत्तर उपयोगी होगा यदि पोस्टर में उनके सिस्टम में कोई बहुपद था। मैंने विशेष रूप से देखा है कि सीएएस कार्यक्रमों जैसे कि गणितज्ञ और मेपल के लिए कोड जनरेटर आपको तब तक हॉर्नर रूप नहीं देते हैं जब तक कि आप विशेष रूप से इसके लिए नहीं पूछते हैं; वे उस तरह से डिफ़ॉल्ट होते हैं जैसे आप आमतौर पर एक मानव के रूप में एक बहुपद लिखते हैं।
neocpp

3

ऐसा लगता है कि आपके पास बहुत सारे दोहराए जाने वाले ऑपरेशन हैं।

pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
pow(l1 * l2 * l3, -0.4e1 / 0.3e1)

आप उन लोगों की पूर्व-गणना कर सकते हैं ताकि आप बार-बार powफ़ंक्शन को कॉल नहीं कर रहे हैं जो महंगा हो सकता है।

आप पहले से कैलेटेट भी कर सकते हैं

l1 * l2 * l3

जैसा कि आप उस शब्द का बार-बार उपयोग करते हैं।


6
मैं शर्त लगाता हूं कि आशावादी पहले से ही आपके लिए ऐसा करता है ... हालांकि यह कम से कम कोड को अधिक पठनीय बनाता है।
कारोली होर्वाथ

मैंने ऐसा किया, लेकिन इसने चीजों को गति नहीं दी। मुझे यह लगा क्योंकि कंपाइलर ऑप्टिमाइज़ेशन पहले से ही इसकी देखभाल कर रहा था।

स्टोरिंग l1 * l2 * l3 चीजों को गति देता है, हालांकि यह सुनिश्चित नहीं है कि संकलक अनुकूलन के साथ क्यों

क्योंकि कंपाइलर कभी-कभी कुछ अनुकूलन नहीं कर सकता है, या उन्हें अन्य विकल्पों के साथ संघर्ष में नहीं मिल सकता है।
जेवियर 13

1
वास्तव में, कंपाइलर को उन ऑप्टिमाइज़ेशन को नहीं करना चाहिए जब तक -ffast-mathकि सक्षम नहीं किया जाता है, और जैसा कि @ tpg2114 द्वारा एक टिप्पणी में कहा गया है, कि अनुकूलन बेहद अस्थिर परिणाम पैदा कर सकता है।
डेविड हैमेन

0

यदि आपके पास एक Nvidia CUDA ग्राफ़िक्स कार्ड है, तो आप ग्राफ़िक्स कार्ड की गणना को ऑफ़लोड करने पर विचार कर सकते हैं - जो कि कम्प्यूटेशनल रूप से जटिल गणनाओं के लिए अधिक उपयुक्त है।

https://developer.nvidia.com/how-to-cuda-c-cpp

यदि नहीं, तो आप गणना के लिए कई थ्रेड्स पर विचार करना चाह सकते हैं।


10
यह जवाब हाथ में सवाल के लिए रूढ़िवादी है। जबकि GPU में बहुत सारे और बहुत सारे प्रोसेसर होते हैं, वे CPU के साथ एम्बेडेड FPU की तुलना में काफी धीमे होते हैं। GPU के साथ एक एकल सीरियल गणना करना एक बड़ा नुकसान है। सीपीयू को पाइपलाइन को जीपीयू में भरना है, धीमे GPU के लिए उस एकल कार्य को करने के लिए प्रतीक्षा करें, और फिर परिणाम को अनलोड करें। जबकि जीपीयू बिल्कुल शानदार है जब हाथ में समस्या बड़े पैमाने पर समानांतर होती है, जब वे धारावाहिक कार्यों को करने की बात करते हैं तो वे पूर्ण रूप से अत्याचारी होते हैं।
डेविड हैमेन

1
मूल प्रश्न में: "जैसा कि इस कोड को कई बार निष्पादित किया जाता है, प्रदर्शन एक चिंता का विषय है।" यह "कई" से एक अधिक है। ऑप थ्रेडेड तरीके से गणना भेज सकता है।
उपयोगकर्ता 3791372

0

किसी भी संयोग से, आप प्रतीकात्मक रूप से गणना की आपूर्ति कर सकते हैं। यदि वेक्टर ऑपरेशन होते हैं, तो आप वास्तव में ब्लास या लैपैक का उपयोग करके जांच कर सकते हैं जो कुछ मामलों में समानांतर में संचालन चला सकते हैं।

यह बोधगम्य है (ऑफ-टॉपिक होने के जोखिम पर?) हो सकता है कि आप अजगर को सुन्न और / या डांट के साथ उपयोग कर सकें। यह संभव था कि आपकी गणना अधिक पठनीय हो।


0

जैसा कि आपने स्पष्ट रूप से उच्च स्तरीय अनुकूलन के बारे में पूछा है, यह अलग-अलग C ++ कंपाइलर की कोशिश करने के लायक हो सकता है। आजकल, कंपाइलर बहुत जटिल अनुकूलन जानवर हैं और सीपीयू विक्रेता बहुत शक्तिशाली और विशिष्ट अनुकूलन को लागू कर सकते हैं। लेकिन कृपया ध्यान दें, उनमें से कुछ मुफ्त नहीं हैं (लेकिन मुफ्त शैक्षणिक कार्यक्रम हो सकता है)।

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

मैंने देखा है कि कोड स्निपेट 2 के कारक द्वारा संकलक गति में भिन्न होते हैं, केवल संकलक (पाठ्यक्रम के पूर्ण अनुकूलन के साथ) बदलकर। लेकिन आउटपुट की पहचान की जाँच के बारे में पता होना चाहिए। आक्रामक अनुकूलन के लिए अलग-अलग आउटपुट हो सकते हैं, जो कुछ ऐसा है जिससे आप निश्चित रूप से बचना चाहते हैं।

शुभ लाभ!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.