क्या मुझे गुणा या भाग का उपयोग करना चाहिए?


118

यहाँ एक मूर्खतापूर्ण मजेदार सवाल है:

मान लें कि हमें एक सरल ऑपरेशन करना है जहां हमें एक चर के मूल्य का आधा हिस्सा चाहिए। कर रहे हैं आम तौर पर ऐसा करने के दो तरीके:

y = x / 2.0;
// or...
y = x * 0.5;

यह मानते हुए कि हम भाषा के साथ प्रदान किए गए मानक ऑपरेटरों का उपयोग कर रहे हैं, जिनमें से किसी का प्रदर्शन बेहतर है?

मैं अनुमान लगा रहा हूं कि गुणा आम तौर पर बेहतर होता है इसलिए जब मैं कोड करता हूं तो मैं उससे चिपके रहने की कोशिश करता हूं, लेकिन मैं इस बात की पुष्टि करना चाहता हूं।

हालाँकि व्यक्तिगत रूप से मैं Python 2.4-2.5 के जवाब में दिलचस्पी लेता हूं, लेकिन बेझिझक अन्य भाषाओं के लिए भी उत्तर दे सकता हूं! और यदि आप चाहें, तो अन्य प्रशंसक तरीके (जैसे बिटवाइज़ शिफ्ट ऑपरेटर्स का उपयोग करना) को भी बेझिझक पोस्ट करें।


5
क्या आपने एक बेंचमार्क चलाया? यह केवल कोड की एक दर्जन लाइनों के बारे में है। बेंचमार्क चलाने से आपने क्या सीखा? [संकेत: ऐसा करना यहाँ प्रश्न पोस्ट करने से जल्दी होगा।]
एस.लूट

4
महान प्रश्न, जिसने कुछ काफी दिलचस्प उत्तर / चर्चाएं उत्पन्न की हैं। धन्यवाद :)
स्टील्कॉप्टर 21

22
भले ही उन्होंने इसका जवाब बेंचमार्क से सीख लिया हो, लेकिन यह अभी भी एक उपयोगी सवाल है और कुछ दिलचस्प और उपयोगी जवाब दिए हैं। इसके अलावा, मैं चाहता हूं कि लोग इस मुद्दे पर टिके रहें और सवाल-जवाब करने के लिए अप्रासंगिक सलाह देने से बचने के लिए जवाब और टिप्पणी लिखने से बचें। यह क्यों न मानें कि ओपी यह सवाल पूछ रहा है कि लिखित रूप में यह मानने के बजाय कि वह वास्तव में बड़े पैमाने पर पुनर्लेखन की सलाह चाहता है।
केविन व्हाइटफुट

1
गुणा की तुलना में विभाजन बहुत धीमा है। लेकिन कुछ स्मार्ट कंपाइलर्स / VMs डिवीजन को गुणा में बदल देते हैं, इसलिए आपके परीक्षणों के परिणाम (दोनों परीक्षण परीक्षण गुणन) समान होंगे।
इवान कुकिर

4
विषय से थोड़ा हटकर, लेकिन मैं सिर्फ इतना कहना चाहता हूं कि मैं @KevinWhitefoot से कितना सहमत हूं। तकनीकी सवालों के तकनीकी जवाबों के बजाय उपदेशकों से पढ़ने के रूप में निराशा के रूप में कुछ भी नहीं है। आपकी टिप्पणी के लिए धन्यवाद केविन!
जीन-फ्रांस्वा

जवाबों:


78

अजगर:

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 / 2.0'
real    0m26.676s
user    0m25.154s
sys     0m0.076s

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 * 0.5'
real    0m17.932s
user    0m16.481s
sys     0m0.048s

गुणा 33% तेज है

लुआ:

time lua -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
real    0m7.956s
user    0m7.332s
sys     0m0.032s

time lua -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
real    0m7.997s
user    0m7.516s
sys     0m0.036s

=> कोई वास्तविक अंतर नहीं

LuaJIT:

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
real    0m1.921s
user    0m1.668s
sys     0m0.004s

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
real    0m1.843s
user    0m1.676s
sys     0m0.000s

=> यह केवल 5% तेज है

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


बेंचमार्किंग के लिए समय कमांड का उपयोग करने पर टिप के लिए धन्यवाद!
एडमंडिटो

2
आपका निष्कर्ष गलत है। यह अधिक प्रासंगिक हो जाता है क्योंकि JIT / VM बेहतर हो जाता है। वीएम के निचले ओवरहेड की तुलना में विभाजन धीमा हो जाता है। याद रखें कि परिशुद्धता की गारंटी देने के लिए कंपाइलर्स आमतौर पर फ्लोटिंग पॉइंट को ऑप्टिमाइज़ नहीं कर सकते हैं।
रासमस

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

68

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

सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है। हमेशा अनुकूलन के तीन नियमों को याद रखें!

  1. अनुकूलन मत करो।
  2. यदि आप एक विशेषज्ञ हैं, तो नियम # 1 देखें
  3. यदि आप एक विशेषज्ञ हैं और आवश्यकता को उचित ठहरा सकते हैं, तो निम्नलिखित प्रक्रिया का उपयोग करें:

    • इसे कोड अनधिकृत
    • निर्धारित करें कि "फास्ट पर्याप्त" कितनी तेजी से है - ध्यान दें कि उपयोगकर्ता की आवश्यकता / कहानी के लिए उस मीट्रिक की आवश्यकता होती है।
    • गति परीक्षण लिखिए
    • मौजूदा कोड का परीक्षण करें - यदि यह काफी तेज है, तो आप कर रहे हैं।
    • इसे फिर से अनुकूलित करें
    • अनुकूलित कोड का परीक्षण करें। यदि यह मीट्रिक को पूरा नहीं करता है, तो इसे फेंक दें और मूल रखें।
    • यदि यह परीक्षण से मिलता है, तो मूल कोड को टिप्पणियों के रूप में रखें

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


7
यह पूरा नथ उद्धरण नहीं है; देख en.wikipedia.org/wiki/...
जेसन एस

नहीं, कई अलग-अलग स्रोतों से इस विषय पर लगभग 40 अलग-अलग उद्धरण हैं। मैं एक साथ कुछ टुकड़े करता हूं।
बिल के

आपका अंतिम वाक्य यह स्पष्ट नहीं करता है कि # 1 और # 2 नियम लागू करने के लिए, हमें वापस छोड़ते समय, जहां हमने शुरू किया था: हमें यह तय करने की आवश्यकता है कि कौन सी अनुकूलन योग्य हैं और कौन सी नहीं हैं। उत्तर को स्पष्ट करना एक उत्तर नहीं है।
मैट

2
यह वास्तव में आप को भ्रमित करने वाला है? नियम 1 और 2 को हमेशा लागू करें जब तक कि आप वास्तव में क्लाइंट विनिर्देशों को पूरा नहीं करते हैं और सीपीयू की भाषा और कैशिंग विशेषताओं सहित पूरे सिस्टम से परिचित हैं। उस बिंदु पर, केवल 3 में प्रक्रिया का पालन करें, बस यह मत सोचो "अरे, अगर मैं एक चरवाहा को कॉल करने के बजाय स्थानीय रूप से इस चर को कैश करता हूं, तो चीजें जल्दी हो जाएंगी। पहले यह साबित करें कि यह तेजी से पर्याप्त नहीं है, फिर प्रत्येक अनुकूलन को अलग से परीक्षण करें और उन लोगों को बाहर निकालो जो मदद नहीं करते हैं। सभी तरह से भारी दस्तावेज़।
बिल के

49

मुझे लगता है कि यह इतना नाइटपिक हो रहा है कि आप जो भी कोड को अधिक पठनीय बनाते हैं, उसे करना बेहतर होगा। जब तक आप हजारों ऑपरेशन करते हैं, यदि लाखों नहीं, तो कई बार, मुझे संदेह है कि किसी को भी कभी भी अंतर दिखाई देगा।

यदि आपको वास्तव में चुनाव करना है, तो बेंचमार्किंग ही एकमात्र रास्ता है। पता करें कि कौन से फ़ंक्शन आपको समस्याएं दे रहे हैं, फिर पता करें कि फ़ंक्शन में समस्याएँ कहाँ हैं, और उन अनुभागों को ठीक करें। हालांकि, मुझे अभी भी संदेह है कि एक एकल गणितीय ऑपरेशन (यहां तक ​​कि कई बार दोहराया, कई बार) किसी भी अड़चन का कारण होगा।


1
जब मैं रडार प्रोसेसर बनाता था, तो एक ही ऑपरेशन से फर्क पड़ता था। लेकिन हम वास्तविक समय के प्रदर्शन को प्राप्त करने के लिए मशीन कोड को हाथ से अनुकूलित कर रहे थे। बाकी सब चीजों के लिए, मैं सरल और स्पष्ट मतदान करता हूं।
एस.लॉट

मैं कुछ चीजों के लिए अनुमान लगाता हूं, आप एक ही ऑपरेशन की परवाह कर सकते हैं। लेकिन मुझे उम्मीद है कि 99% अनुप्रयोगों में वहाँ से, यह कोई फर्क नहीं पड़ता।
थॉमस ओवेन्स

27
खासकर जब से ओपी पायथन में जवाब की तलाश में था। मुझे कुछ भी संदेह है, जो कि अजगर में दक्षता की मात्रा की आवश्यकता होगी।
एड एस।

4
एक विभाजन संभवतः एक त्रिकोण चौराहे की दिनचर्या में सबसे महंगा ऑपरेशन है, जो कि अधिकांश किरणों का आधार है। यदि आप पारस्परिक को स्टोर करते हैं और विभाजित करने के बजाय गुणा करते हैं, तो आप कई बार स्पीडअप का अनुभव करेंगे।
सोलन

@solinent - हां एक स्पीडअप लेकिन मुझे "कई बार" संदेह है - फ्लोटिंग-पॉइंट डिवीजन और गुणा लगभग 4: 1 से अधिक नहीं होना चाहिए, जब तक कि प्रश्न में प्रोसेसर वास्तव में गुणन के लिए अनुकूलित नहीं है और विभाजन नहीं है।
जेसन एस

39

गुणा अधिक तेज़ है, विभाजन अधिक सटीक है। यदि आपका नंबर 2 की शक्ति नहीं है, तो आप कुछ सटीक खो देंगे:

y = x / 3.0;
y = x * 0.333333;  // how many 3's should there be, and how will the compiler round?

यहां तक ​​कि अगर आप संकलक को सटीक सटीकता के लिए औंधा स्थिर बताते हैं, तो भी जवाब अलग हो सकता है।

x = 100.0;
x / 3.0 == x * (1.0/3.0)  // is false in the test I just performed

गति समस्या केवल C / C ++ या JIT भाषाओं में मायने रखती है, और तब भी जब ऑपरेशन एक अड़चन पर एक लूप में होता है।


यदि आप संपूर्ण संख्याओं में विभाजित कर रहे हैं तो विभाजन सटीक है।
कुर्सी

7
भाजक के साथ फ्लोटिंग-पॉइंट डिवीजन> अंश को कम-ऑर्डर बिट्स में अर्थहीन मूल्यों का परिचय देना चाहिए; विभाजन आमतौर पर सटीकता को कम करता है।
एस.लॉट।

8
@ एस.लॉट: नहीं, यह सच नहीं है। सभी IEEE-754-संगत फ़्लोटिंग पॉइंट कार्यान्वयन को वर्तमान राउंडिंग मोड के संबंध में हर ऑपरेशन के परिणामों को पूरी तरह से (यानी निकटतम फ़्लोटिंग पॉइंट नंबर को) गोल करना चाहिए। पारस्परिक द्वारा गुणा करना हमेशा अधिक त्रुटि का परिचय देने वाला है, कम से कम क्योंकि एक और गोलाई अवश्य होनी चाहिए।
इलेक्ट्रो

1
मुझे पता है कि यह उत्तर 8 साल से अधिक पुराना है, लेकिन यह भ्रामक है; आप परिशुद्धता के महत्वपूर्ण नुकसान के बिना विभाजन कर सकते हैं: y = x * (1.0/3.0);और संकलक आमतौर पर 1/3 संकलन-समय पर गणना करेगा। हां, 1/3 IEEE-754 में पूरी तरह से प्रतिनिधित्व करने योग्य नहीं है, लेकिन जब आप फ्लोटिंग-पॉइंट अंकगणितीय प्रदर्शन कर रहे हैं, तो आप वैसे भी सटीक खो रहे हैं , चाहे आप गुणन या विभाजन कर रहे हों, क्योंकि कम-क्रम बिट्स गोल हैं। यदि आप जानते हैं कि आपकी गणना गोल त्रुटि के प्रति संवेदनशील है, तो आपको यह भी जानना चाहिए कि समस्या से कैसे निपटना है।
जेसन एस

1
@ जैसनस I ने केवल रात भर चलने वाला एक कार्यक्रम छोड़ा, जो 1.0 पर शुरू हुआ और 1 ULP तक गिना गया; मैंने गुणा करने के परिणाम की तुलना (1.0/3.0)विभाजन से की 3.0। मैं 1.0000036666774155 पर गया, और उस स्थान पर 7.3% परिणाम भिन्न थे। मुझे लगता है कि वे केवल 1 बिट से अलग थे, लेकिन जब से IEEE अंकगणित को निकटतम सही परिणाम के लिए राउंड की गारंटी दी गई है, मैं अपने कथन से खड़ा हूं कि विभाजन अधिक सटीक है। क्या अंतर महत्वपूर्ण है आप पर निर्भर है।
मार्क रैनसम

25

यदि आप अपना कोड ऑप्टिमाइज़ करना चाहते हैं लेकिन फिर भी स्पष्ट हैं, तो यह आज़माएँ:

y = x * (1.0 / 2.0);

कंपाइलर को कंपाइल-टाइम पर डिवाइड करने में सक्षम होना चाहिए, इसलिए आपको रन-टाइम पर एक गुणा मिलता है। मुझे उम्मीद है कि y = x / 2.0मामले में सटीकता उसी तरह की होगी ।

जहाँ यह बात हो सकती है कि LOT एम्बेडेड प्रोसेसर में होता है जहाँ फ़्लोटिंग पॉइंट अंकगणित की गणना करने के लिए फ़्लोटिंग-पॉइंट एमुलेशन आवश्यक होता है।


12
अपने आप को सूट करें (और जो कोई भी इसे पूरा करेगा) - यह एम्बेडेड दुनिया में मानक अभ्यास है और उस क्षेत्र में सॉफ्टवेयर इंजीनियर इसे स्पष्ट पाते हैं।
जेसन एस

4
+1 केवल यह महसूस करने के लिए कि कंपाइलर्स फ्लोटिंग पॉइंट ऑपरेशंस को ऑप्टिमाइज़ नहीं कर सकते हैं, हालांकि वे चाहते हैं। वे परिशुद्धता की गारंटी के लिए गुणन में ऑपरेंड के क्रम को भी नहीं बदल सकते (जब तक कि यह एक आराम मोड का उपयोग नहीं करता है)।
रासुस

1
OMG, कम से कम 6 प्रोग्रामर सोच रहे हैं कि प्राथमिक गणित अस्पष्ट है। AFAIK, IEEE 754 गुणा सराहनीय (लेकिन गैर-सहयोगी) है।
मातरिनस

13
शायद आपको बात याद आ रही है। इसका बीजीय शुद्धता से कोई लेना-देना नहीं है। एक आदर्श दुनिया में आपको बस दो से विभाजित करने में सक्षम होना चाहिए: y = x / 2.0;लेकिन वास्तविक दुनिया में, आपको कंपाइलर को कम-महंगी गुणा करने में सक्षम होना पड़ सकता है। शायद यह कम स्पष्ट है कि y = x * (1.0 / 2.0);बेहतर क्यों है, और इसके y = x * 0.5;बजाय यह स्पष्ट होगा । लेकिन 2.0एक को बदलने के लिए 7.0और मैं बहुत बजाय देखना y = x * (1.0 / 7.0);होगा y = x * 0.142857142857;
जेसन एस

3
यह वास्तव में यह स्पष्ट करता है कि अपनी विधि का उपयोग करने के लिए यह अधिक सुपाठ्य (और सटीक) क्यों है।
जुआन मार्टिनेज

21

बस "अन्य भाषाओं" विकल्प के लिए कुछ जोड़ने जा रहा है।
सी: चूंकि यह सिर्फ एक अकादमिक अभ्यास है जो वास्तव में कोई फर्क नहीं पड़ता है, मैंने सोचा कि मैं कुछ अलग योगदान दूंगा।

मैंने बिना किसी अनुकूलन के विधानसभा के लिए संकलित किया और परिणाम को देखा।
कोड:

int main() {

    volatile int a;
    volatile int b;

    asm("## 5/2\n");
    a = 5;
    a = a / 2;

    asm("## 5*0.5");
    b = 5;
    b = b * 0.5;

    asm("## done");

    return a + b;

}

के साथ संकलित gcc tdiv.c -O1 -o tdiv.s -S

2 से विभाजन:

movl    $5, -4(%ebp)
movl    -4(%ebp), %eax
movl    %eax, %edx
shrl    $31, %edx
addl    %edx, %eax
sarl    %eax
movl    %eax, -4(%ebp)

और 0.5 से गुणा:

movl    $5, -8(%ebp)
movl    -8(%ebp), %eax
pushl   %eax
fildl   (%esp)
leal    4(%esp), %esp
fmuls   LC0
fnstcw  -10(%ebp)
movzwl  -10(%ebp), %eax
orw $3072, %ax
movw    %ax, -12(%ebp)
fldcw   -12(%ebp)
fistpl  -16(%ebp)
fldcw   -10(%ebp)
movl    -16(%ebp), %eax
movl    %eax, -8(%ebp)

हालाँकि, जब मैंने उन intएस को बदल दिया double(जो कि अजगर शायद करेगा), मुझे यह मिला:

विभाजन:

flds    LC0
fstl    -8(%ebp)
fldl    -8(%ebp)
flds    LC1
fmul    %st, %st(1)
fxch    %st(1)
fstpl   -8(%ebp)
fxch    %st(1)

गुणन:

fstpl   -16(%ebp)
fldl    -16(%ebp)
fmulp   %st, %st(1)
fstpl   -16(%ebp)

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

एक साइड नोट के रूप में, आप देख सकते हैं कि मेरे कार्यक्रम में main()वापसी हुई है a + b। जब मैं वाष्पशील कीवर्ड को हटा लेता हूं, तो आप कभी भी यह अनुमान नहीं लगा पाएंगे कि असेंबली कैसी दिखती है (प्रोग्राम सेटअप को छोड़कर):

## 5/2

## 5*0.5
## done

movl    $5, %eax
leave
ret

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

अत्यधिक लंबे उत्तर के लिए क्षमा करें।


1
यह "एकल निर्देश" नहीं है। यह बस लगातार मुड़ा हुआ है।
kvanberendonck

5
@kvanberendonck बेशक यह एक एकल निर्देश है। उनकी गणना करें: movl $5, %eax अनुकूलन का नाम महत्वपूर्ण या प्रासंगिक नहीं है। आप सिर्फ चार साल पुराने उत्तर पर कृपालु होना चाहते थे।
कार्सन मायर्स

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

10

सबसे पहले, जब तक आप C या ASSEMBLY में काम नहीं कर रहे हैं, आप शायद एक उच्च स्तर की भाषा में हैं, जहाँ मेमोरी स्टॉल और सामान्य कॉल ओवरहेड्स बहुतायत के बीच के अंतर को पूरी तरह से बौना कर देंगे और अप्रासंगिकता के बिंदु पर विभाजित कर देंगे। तो, बस उस मामले में बेहतर क्या पढ़ता है उठाओ।

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

यदि आप अभी भी उत्सुक हैं, तो निम्न स्तर के अनुकूलन बिंदु से:

डिवाइड में गुणा की तुलना में काफी लंबी पाइपलाइन होती है। इसका मतलब यह है कि परिणाम प्राप्त करने में अधिक समय लगता है, लेकिन यदि आप प्रोसेसर को गैर-निर्भर कार्यों में व्यस्त रख सकते हैं, तो यह आपको किसी भी अधिक लागत से अधिक खर्च नहीं करता है।

कब तक पाइपलाइन अंतर पूरी तरह से हार्डवेयर निर्भर है। पिछले हार्डवेयर का उपयोग मैंने किया था, जैसे कि FPU गुणा के लिए 9 चक्र और FPU विभाजन के लिए 50 चक्र। बहुत लगता है, लेकिन फिर आप एक मेमोरी मिस के लिए 1000 चक्र खो देंगे, ताकि चीजों को परिप्रेक्ष्य में रख सकें।

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

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

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


7

जो भी लिखें वह अधिक स्पष्ट रूप से आपके इरादे को बताता है।

आपके कार्यक्रम के काम करने के बाद, यह पता लगाएं कि क्या धीमा है, और इसे तेज़ करें।

इसे दूसरे तरीके से न करें।


6

आपको जो कुछ भी चाहिए वह करें। पहले अपने पाठक के बारे में सोचें, प्रदर्शन के बारे में तब तक चिंता न करें जब तक आप सुनिश्चित न करें कि आपको प्रदर्शन की समस्या है।

संकलक को आपके लिए प्रदर्शन करने दें।


5

यदि आप पूर्णांक या गैर-फ्लोटिंग पॉइंट प्रकार के साथ काम कर रहे हैं, तो अपने बिटशिफ्टिंग ऑपरेटरों को न भूलें: << >>

    int y = 10;
    y = y >> 1;
    Console.WriteLine("value halved: " + y);
    y = y << 1;
    Console.WriteLine("now value doubled: " + y);

7
यह अनुकूलन स्वचालित रूप से किसी भी आधुनिक संकलक में पर्दे के पीछे किया जाता है।
डस्टिन गेट्ज़

अगर किसी ऑपरेंड (?) का उपयोग करने के लिए शिफ्ट करने योग्य संस्करण है, तो क्या किसी ने जाँच की है (बिट ऑप्स का उपयोग करते हुए)? function mul (a, b) {if (b 2 है) रिटर्न a << 1; अगर (बी 4 है) एक << 2; // ... आदि वापसी एक * बी; } मेरा अनुमान है कि आईएफ इतना महंगा है कि यह कम कुशल होगा।
क्रिस्टोफर लाइटफुट

मैंने जो सोचा था, उसके करीब कहीं भी नहीं छपा; कोई बात नहीं।
क्रिस्टोफर लाइटफुट ऑक्ट

कास्ट ऑपरेशन के लिए एक सामान्य संकलक को काम करना चाहिए; लेकिन यहाँ हम अजगर का उपयोग कर रहे हैं तो मुझे यकीन नहीं है कि अगर इसके स्मार्ट को पता है? (यह होना चाहिए)।
क्रिस्टोफर लाइटफुट

अच्छा शॉर्टकट, सिवाय इसके कि यह तुरंत स्पष्ट नहीं है कि वास्तव में क्या हो रहा है। अधिकांश प्रोग्रामर बिटशिफ्ट ऑपरेटरों को भी नहीं पहचानते हैं।
१०:४२ पर ब्लेज़ेमॉन्गर '

4

वास्तव में एक अच्छा कारण है कि अंगूठे के गुणन के एक सामान्य नियम के रूप में विभाजन की तुलना में तेज होगा। हार्डवेयर में फ्लोटिंग पॉइंट डिवीजन या तो शिफ्ट और सशर्त घटाव एल्गोरिदम (बाइनरी नंबर के साथ "लॉन्ग डिवीजन") के साथ किया जाता है या - इन दिनों अधिक संभावना है - गोल्डस्मैटिड के एल्गोरिथ्म जैसे पुनरावृत्तियों के साथ । प्रति बिट कम से कम एक चक्र को सटीकता के साथ शिफ्ट और घटाना चाहिए (पुनरावृत्तियाँ लगभग समानांतर करना असंभव हैं क्योंकि शिफ्ट और गुणा का जोड़), और पुनरावृत्त एल्गोरिदम प्रति पुनरावृत्ति कम से कम एक गुणा करते हैं। या तो मामले में, यह अत्यधिक संभावना है कि विभाजन अधिक चक्र लेगा। बेशक यह संकलक, डेटा आंदोलन, या परिशुद्धता में quirks के लिए खाता नहीं है। हालांकि, बड़े, हालांकि, अगर आप किसी प्रोग्राम के एक समय संवेदनशील हिस्से में एक आंतरिक लूप को कोड कर रहे हैं, तो लेखन 0.5 * xया 1.0/2.0 * xइसके बजाय x / 2.0एक उचित बात है। "कोड क्या स्पष्ट है" की पैदल सेना बिल्कुल सच है, लेकिन ये तीनों पठनीयता में इतने करीब हैं कि पैदल सेना इस मामले में सिर्फ पांडित्य है।


3

मैंने हमेशा सीखा है कि गुणा अधिक कुशल है।


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

3

गुणा आमतौर पर तेज होता है - निश्चित रूप से कभी धीमा नहीं होता। हालांकि, अगर यह महत्वपूर्ण गति नहीं है, जो भी स्पष्ट है लिखो।


2

फ्लोटिंग-पॉइंट डिवीजन (आम तौर पर) विशेष रूप से धीमा होता है, इसलिए फ्लोटिंग-पॉइंट गुणा भी अपेक्षाकृत धीमी गति से होता है, यह शायद फ़्लोटिंग-पॉइंट डिवीजन की तुलना में तेज़ होता है।

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


2

यह एक प्रश्न का और अधिक हो जाता है जब आप असेंबली में प्रोग्रामिंग कर रहे होते हैं या शायद सी। मैं यह अनुमान लगाता हूं कि अधिकांश आधुनिक भाषाओं के साथ ऐसा अनुकूलन मेरे लिए किया जा रहा है।


2

सावधान रहें "अनुमान लगाने का गुणन आमतौर पर बेहतर होता है, इसलिए जब मैं कोड करता हूं, तो मैं उससे चिपके रहने की कोशिश करता हूं।"

इस विशिष्ट प्रश्न के संदर्भ में, यहां बेहतर का अर्थ "तेज" है। जो बहुत उपयोगी नहीं है।

गति के बारे में सोचना एक गंभीर गलती हो सकती है। गणना के विशिष्ट बीजीय रूप में गहन त्रुटि निहितार्थ हैं।

त्रुटि विश्लेषण के साथ फ्लोटिंग प्वाइंट अंकगणित देखें । फ़्लोटिंग पॉइंट अंकगणित और त्रुटि विश्लेषण में बुनियादी मुद्दे देखें ।

हालांकि कुछ फ़्लोटिंग-पॉइंट मान सटीक हैं, अधिकांश फ़्लोटिंग पॉइंट मान एक अनुमान हैं; वे कुछ आदर्श मूल्य और कुछ त्रुटि हैं। प्रत्येक ऑपरेशन आदर्श मूल्य और त्रुटि मान पर लागू होता है।

सबसे बड़ी समस्या दो लगभग समान संख्या में हेरफेर करने की कोशिश से आती है। सही-सबसे बिट्स (त्रुटि बिट्स) परिणामों पर हावी होने के लिए आते हैं।

>>> for i in range(7):
...     a=1/(10.0**i)
...     b=(1/10.0)**i
...     print i, a, b, a-b
... 
0 1.0 1.0 0.0
1 0.1 0.1 0.0
2 0.01 0.01 -1.73472347598e-18
3 0.001 0.001 -2.16840434497e-19
4 0.0001 0.0001 -1.35525271561e-20
5 1e-05 1e-05 -1.69406589451e-21
6 1e-06 1e-06 -4.23516473627e-22

इस उदाहरण में, आप देख सकते हैं कि जैसे-जैसे मान छोटे होते जाते हैं, लगभग समान संख्याओं के बीच का अंतर गैर-शून्य परिणाम बनाता है जहाँ सही उत्तर शून्य होता है।


1

मैंने कहीं पढ़ा है कि गुणा C / C ++ में अधिक कुशल है; व्याख्या की गई भाषाओं के बारे में कोई विचार नहीं है - अंतर शायद अन्य सभी उपरि के कारण नगण्य है।

जब तक कि यह एक मुद्दा नहीं बन जाता है कि क्या अधिक रख-रखाव / पठनीय है - मुझे इससे नफरत है जब लोग मुझे यह बताते हैं लेकिन यह सच है।


1

मैं सामान्य रूप से गुणा करने का सुझाव दूंगा, क्योंकि आपको यह सुनिश्चित करने के लिए चक्र खर्च करने की आवश्यकता नहीं है कि आपका विभाजक 0. नहीं है। यह लागू नहीं होता है, निश्चित रूप से, यदि आपका भाजक एक स्थिर है।


1

जावा एंड्रॉयड, सैमसंग जीटी-एस 5830 पर आधारित है

public void Mutiplication()
{
    float a = 1.0f;

    for(int i=0; i<1000000; i++)
    {
        a *= 0.5f;
    }
}
public void Division()
{
    float a = 1.0f;

    for(int i=0; i<1000000; i++)
    {
        a /= 2.0f;
    }
}

परिणाम?

Multiplications():   time/call: 1524.375 ms
Division():          time/call: 1220.003 ms

विभाजन गुणन की तुलना में लगभग 20% तेज है (!)


1
यथार्थवादी होने के लिए, आपको परीक्षण करना चाहिए a = i*0.5, न कि a *= 0.5। इस तरह से अधिकांश प्रोग्रामर ऑपरेशन का उपयोग कर रहे हैं।
ब्लेज़ेमॉन्गर

1

जैसा कि पोस्ट # 24 के साथ (गुणा अधिक तेज़ है) और # 30 - लेकिन कभी-कभी वे दोनों ही समझने में आसान होते हैं:

1*1e-6F;

1/1e6F;

~ मुझे लगता है कि दोनों को पढ़ना आसान है, और उन्हें कई बार दोहराना है। इसलिए यह जानना उपयोगी है कि गुणा आमतौर पर तेज होता है।


1

एक अंतर है, लेकिन यह संकलक पर निर्भर है। Vs2003 (c ++) पर सबसे पहले मुझे दोहरे प्रकार (64 बिट फ्लोटिंग पॉइंट) के लिए कोई महत्वपूर्ण अंतर नहीं मिला। हालाँकि, vs2010 पर फिर से परीक्षण चलाने पर, मैंने गुणन के लिए फैक्टर 4 तक तेजी से एक बड़ा अंतर पाया। इसे नीचे ट्रैक करने पर, ऐसा लगता है कि vs2003 और vs2010 अलग-अलग fpu कोड उत्पन्न करता है।

एक पेंटियम 4 पर, 2.8 गीगाहर्ट्ज, vs2003:

  • गुणन: 8.09
  • प्रभाग: 7.97

Xeon W3530, vs2003 पर:

  • गुणन: 4.68
  • प्रभाग: ४.६४

Xeon W3530, बनाम2010 पर:

  • गुणन: 5.33
  • प्रभाग: 21.05

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

18-03-2013 को संपादित करें: vs2010 के लिए अवलोकन


मुझे आश्चर्य है कि अगर किसी कारण से कोई कंपाइलर उदाहरण की n/10.0अभिव्यक्ति के साथ प्रतिस्थापित नहीं कर सकता है (n * c1 + n * c2)? मैं उम्मीद करूंगा कि अधिकांश प्रोसेसरों पर एक डिवीजन दो गुणा और एक डिवीजन से अधिक समय लेगा, और मेरा मानना ​​है कि किसी भी निरंतर द्वारा डिवीजन संकेतित सूत्रीकरण का उपयोग करके सभी मामलों में सही ढंग से गोल परिणाम प्राप्त कर सकता है।
सुपरकैट

1

यहाँ एक मूर्खतापूर्ण मजेदार जवाब है:

एक्स / 2.0 है नहीं के बराबर एक्स * 0.5

मान लीजिए कि आपने यह विधि 22 अक्टूबर 2008 को लिखी है।

double half(double x) => x / 2.0;

अब, 10 साल बाद आपको पता चलता है कि आप इस कोड के कोड को ऑप्टिमाइज़ कर सकते हैं। आपके आवेदन में यह विधि सैकड़ों सूत्रों में संदर्भित है। तो आप इसे बदलते हैं, और एक उल्लेखनीय 5% प्रदर्शन सुधार का अनुभव करते हैं।

double half(double x) => x * 0.5;

क्या कोड को बदलना सही फैसला था? गणित में, दो भाव वास्तव में समतुल्य हैं। कंप्यूटर विज्ञान में, यह हमेशा सच नहीं होता है। कृपया अधिक जानकारी के लिए सटीकता की समस्याओं के प्रभाव को कम से कम पढ़ें । यदि आपके परिकलित मान किसी बिंदु पर हैं - तो अन्य मानों की तुलना में, आप किनारे के मामलों के परिणाम को बदल देंगे। उदाहरण के लिए:

double quantize(double x)
{
    if (half(x) > threshold))
        return 1;
    else
        return -1;
}

निचला रेखा है; एक बार जब आप दोनों में से किसी एक के लिए समझौता कर लेते हैं, तो उससे चिपक जाते हैं!


1
Downvote? अपने विचारों को समझाने वाली टिप्पणी के बारे में कैसे? यह उत्तर निश्चित रूप से 100% प्रासंगिक है।
l33t

कंप्यूटर विज्ञान में, 2 की शक्तियों द्वारा फ्लोटिंग पॉइंट मानों का गुणा / भाग करना दोषरहित है, जब तक कि मूल्य को असामान्य या अधिक नहीं हो जाता है।
सूट्स

चूँकि विभाजन के समय फ्लोटिंग पॉइंट दोषरहित नहीं होता है, तो यह वास्तव में मायने नहीं रखता है यदि आपका कथन सत्य है। हालांकि मुझे बहुत आश्चर्य होगा अगर ऐसा होता।
l33t

1
"विभाजन के समय फ्लोटिंग पॉइंट दोषरहित नहीं होता है" जब आप प्राचीन संकलक के साथ निर्माण कर रहे होते हैं जो कि अवक्षेपित x87 कोड का उत्सर्जन करता है। आधुनिक हार्डवेयर पर सिर्फ फ्लोट / डबल चर होना दोषरहित है, या तो 32 या 64 बिट IEEE 754: en.wikipedia.org/wiki/IEEE_754 IEEE 754 के काम करने के तरीके के कारण, जब आप 2 से विभाजित होते हैं और 0.5 से गुणा करते हैं, तो आप कम हो जाते हैं। प्रतिपादक 1 से, शेष बिट्स (संकेत + मंटिसा) नहीं बदलते हैं। और दोनों 2और 0.5संख्याओं को सटीक रूप से किसी भी नुकसान के बिना IEEE 754 में दर्शाया जा सकता है (उदाहरण के लिए , 0.4या 0.1वे नहीं कर सकते हैं)।
सूट्स

0

ठीक है, अगर हम मानते हैं कि एक ऐड / सबट्रैक ऑपरेशन की लागत 1 है, तो 5 की लागत से गुणा करें, और लागत को लगभग 20 से विभाजित करें।


आपको ये नंबर कहां से मिले? अनुभव? साहसी भावना? इंटरनेट पर लेख? वे विभिन्न डेटा प्रकारों के लिए कैसे बदलेंगे?
kroiz

0

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


-3

तकनीकी रूप से विभाजन जैसी कोई चीज नहीं है, उलटे तत्वों द्वारा सिर्फ गुणा है। उदाहरण के लिए, आप कभी भी 2 से विभाजित नहीं होते हैं, आप वास्तव में 0.5 से गुणा करते हैं।

'डिवीजन' - लेट्स बच्चा अपने आप को कि यह एक पल के लिए मौजूद है - हमेशा कठिन है क्योंकि 'डिवाइड' के लिए है कि गुणा है xद्वारा yमूल्य की गणना करने के लिए एक पहले की जरूरत है y^{-1}ऐसा है कि y*y^{-1} = 1और फिर गुणा करना x*y^{-1}। यदि आप पहले से ही जानते हैं y^{-1}तो इसे गणना नहीं yकरना एक अनुकूलन होना चाहिए।


3
जो पूरी तरह से सिलिकॉन में मौजूद दोनों आदेशों की वास्तविकता की अनदेखी करता है।
NPSF3000

@ NPSF3000 - मैं अनुसरण नहीं करता। इस धारणा के तहत कि दोनों ऑपरेशन मौजूद हैं, यह केवल इस बात पर जोर देता है कि डिवीजन ऑपरेशन में निहित गुणन व्युत्क्रम और गुणन की गणना शामिल है, जो हमेशा केवल एक गुणा करने से कठिन होगा। सिलिकॉन एक कार्यान्वयन विवरण है।
शनिचरी

@ BTyler। यदि दोनों कमांड सिलिकॉन में मौजूद हैं, और दोनों कमांड समान संख्या में चक्र लेते हैं [जैसा कि एक को उम्मीद होगी] तुलनात्मक रूप से कि प्रदर्शन POV से निर्देश कितना जटिल हैं, पूरी तरह से अप्रासंगिक हैं।
एनपीएसएफ ३०००

@ NPSF3000 - लेकिन वे दोनों एक ही संख्या में चक्र नहीं लेते हैं क्योंकि वे गुणा अधिक तेज हैं।
१०'१२
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.