2011-05-08 10 views
10

मैं कुछ प्रयोग कर रहा हूं और यहां कुछ ऐसा पाया गया है।हास्केल - लूप के लिए कुशल समकक्ष?

int main() 
{ 
    for(int i = 0; 
     i < 1000000; 
     ++i) 
    {} 
} 

और निम्नलिखित हास्केल कार्यक्रम: निम्नलिखित सी कार्यक्रम पर विचार करें

import System.IO 

loop :: Int -> IO() 
loop n = if 0 == n then return() else loop (n-1) 

main = loop 1000000 

यहाँ ऊपर सी कार्यक्रम के लिए time के उत्पादन में है:

real 0m0.003s 
user 0m0.000s 
sys 0m0.000s 

... और के लिए हास्केल प्रोग्राम:

real 0m0.028s 
user 0m0.027s 
sys 0m0.000s 

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

सी संस्करण

real 0m0.024s 
user 0m0.023s 
sys 0m0.000s 

हास्केल संस्करण

real 0m0.245s 
user 0m0.247s 
sys 0m0.000s 

आप देख सकते हैं, हास्केल: यहाँ 10000000 करने के लिए सेट पुनरावृत्तियों की संख्या के साथ कार्यक्रमों दोनों के लिए time के आउटपुट कर रहे हैं, कार्यक्रम 10 गुना धीमा है।

प्रश्न है: for लस्क को हास्केल में कुशल विकल्प क्या है? जैसा कि हमने अभी देखा है, सरल रिकर्सन प्रोग्राम को लगभग 10 गुना धीमा कर देता है (और यह शायद पूंछ रिकर्सन ऑप्टिमाइज़ेशन के साथ होता है)।

+1

जिज्ञासा से बाहर, विधानसभा अनुकूलित ग कार्यक्रम पूरी तरह से से उत्पादित कोड पाश को दूर करता है? – shuttle87

+0

पीएफटी, 'जबकि' लूप। ओह। : पी –

+0

@ शटल 87 के साथ -ओ 3 ध्वज लूप पूरी तरह से हटा दिया जाता है, इसके बिना यह नहीं करता है। –

उत्तर

22

, आप अपने सी कोड का अनुवाद करता हूँ इसके लिए,

main = go 0 
    where 
     go :: Int -> IO() 
     go i | i < 1000000 = go (i+1) 
      | otherwise = return() 

कौन सा ghc खाली कार्यक्रम को अनुकूलित करता है। यह एक रजिस्टर में अंतिम मूल्य ले जाता है, इसके खिलाफ तुलना, और () रिटर्न:

Main_zdwa_info: 
    cmpq $1000000, %r14   # imm = 0xF4240 
    movl $1000000, %eax   # imm = 0xF4240 
    cmovlq %rax, %r14 
    movq (%rbp), %rax 
    movl $ghczmprim_GHCziUnit_Z0T_closure+1, %ebx 
    jmpq *%rax # TAILCALL 

जो जब रन:

$ time ./A 
./A 0.00s user 0.00s system 88% cpu 0.004 total 

कोई समय लगता है।


सामान्य में, हालांकि, GHC equivalent loops to e.g. GCC उत्सर्जन करता है, पूंछ पुनरावर्ती कार्यों के लिए। विशेष रूप से, आप अपने अंकों के बेंचमार्क को सर्वोत्तम परिणामों के लिए ghc -O2 -fllvm के साथ संकलित करना चाहते हैं। यदि आप अपने प्रोग्राम को अनुकूलित नहीं करना चाहते हैं, तो ghc आपके द्वारा निर्दिष्ट प्रोग्राम को खुशी से निष्पादित करेगा, जो इस मामले में, बहुत ही अनावश्यक काम शामिल करता है जिसे उच्च अनुकूलन स्तर पर हटा दिया जाएगा।

हास्केल कार्यक्रमों के निम्न स्तर के प्रदर्शन का विश्लेषण करने के बारे में अधिक जानकारी के लिए, जाँच RWH ch25.

+0

यह खराब कोड की तरह दिखता है। एक अनावश्यक कदम है। मेरा मतलब है, या तो लूप को पूरी तरह से खत्म करें या इसे रखें। इसके 3 निर्देशों को न बचाएं। – augustss

+0

मुख्य अभी भी एक आईओ() वापस करना है। – muhmuhten

6

लूपिंग संरचनाओं के लिए, आपको अक्सर बाहरी कार्य स्तर पर पुनरावृत्ति के बजाय जीएचसी स्पॉट अनुकूलन में सहायता के लिए वर्कर/रैपर शैली में लिखना होगा।

ग्रिगोरी Javadyan एक टिप्पणी है कि मूल संस्करण -O3 पर बाहर अनुकूलित हो जाता है में कहा है, मैं उम्मीद थी इस संस्करण -O2 में पता चला हो जाता है: सबसे पहले

import System.IO 

loop :: Int -> IO() 
loop n = go n 
    where 
    go n | n <= 0 = return() 
    go n   = go (n-1) 

main = loop 1000000 
संबंधित मुद्दे