2010-03-20 21 views
6

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

निम्नलिखित दो छोटी लिपियों में, मैंने 100,000 के फैक्टोरियल की गणना करने के लिए अलग-अलग लूप के लिए और लूप के लिए कोशिश की है। जिस समय लूप में 57 मिनट 17 सेकेंड लग गए, जबकि लूप समकक्ष के लिए 1 घंटे 7 मिनट 54 सेकंड लगे।

स्क्रिप्ट है कि जब तक पाश:

use strict; 
use warnings; 
use bigint; 

my $now = time; 

my $n = shift; 
my $s = 1; 

while(1){ 
$s *= $n; 
$n--; 
last if $n==2; 
} 

print $s*$n; 
$now = time - $now; 
printf("\n\nTotal running time: %02d:%02d:%02d\n\n", int($now/3600), 
      int(($now % 3600)/60), int($now % 60)); 

स्क्रिप्ट पाश के लिए है:

use strict; 
use warnings; 
use bigint; 

my $now = time; 

my $n =shift; 
my $s=1; 

for (my $i=2; $i<=$n;$i++) { 
$s = $s*$i; 
} 

print $s; 
$now = time - $now; 
printf("\n\nTotal running time: %02d:%02d:%02d\n\n", int($now/3600), 
      int(($now % 3600)/60), int($now % 60)); 
+3

आप शायद गलत चीज़ को अनुकूलित करने का प्रयास कर रहे हैं। मैं सोच रहा हूं कि आपको क्यों लगता है कि भाषा का यह विशेष हिस्सा इतना महत्वपूर्ण है? – Ether

+0

उन्हें प्रत्येक बार चलाएं, और संभवतः वे लगभग – CaffGeek

+0

@ चाड के बारे में औसत होंगे, असल में मैंने कोड को दो बार पहले से ही जांच लिया है। उन्होंने एक ही नौकरी खत्म करने के लिए अलग-अलग समय लगाए। मुझे लगता है कि @ जेनाथन लेफ्लर के चित्रण कोड के साथ स्पष्टीकरण बहुत अच्छी समझ में आता है। – Mike

उत्तर

8

लूप समकक्ष नहीं हैं, और आप मुख्य रूप से बड़े पैकेज को थ्रैश कर रहे हैं और इसका for बनाम while प्रति से कोई लेना देना नहीं है।

जबकि लूप नोटेशन '$s *= $i' का उपयोग करता है लेकिन लूप के लिए '$s = $s * $i' का उपयोग करता है। यह दिखाने के लिए काफी आसान है कि ये समान नहीं हैं। इसके अलावा, एक पाश मायने रखता है; दूसरा गिना जाता है। यह प्रभावित करता है कि संख्याओं को गुणा करने के लिए कितने बड़े हैं। यह दूसरा क्रम प्रभाव है - लेकिन पूरी तरह से नगण्य नहीं है।

[अद्यतन: उप-दूसरे समय के साथ कोड के केवल एक संस्करण को दिखाने के लिए संशोधित किया गया। यह सोचने के लिए एक कमरा है कि प्रिंटिंग समय गणना से बाहर रखा जाना चाहिए; जो चीजों को गड़बड़ कर देता है, इसलिए मुझे परेशान नहीं किया गया है। मैंने पिछले संस्करण में बग तय कर दिया है: लूप 4 लूप 3 जैसा ही था - अब यह नहीं है। मैंने आउटपुट स्वरूपण को भी बढ़ा दिया है (हालांकि उप-सेकेंड हैंडलिंग में सुधार किया जा सकता है - पाठक के लिए एक अभ्यास), और बेहतर 'प्रगति रिपोर्टिंग' है।]

पर एक मैक मिनी (हिमपात तेंदुए 10.6.2) समय परिणाम थे:

Count up $s *= $i:  00:00:12.663337 
Count up $s = $s * $i: 00:00:20.686111 
Count down $s *= $i:  00:00:14.201797 
Count down $s = $s * $i: 00:00:23.269874 

स्क्रिप्ट:

use Time::HiRes qw(gettimeofday); 
use strict; 
use warnings; 
use bigint; 
use constant factorial_of => 13000; 

sub delta_t 
{ 
    my($tag, $t1, $t2) = @_; 
    my($d) = int($t2 - $t1); 
    my($f) = ($t2 - $t1) - $d; 
    my($s) = sprintf("%.6f", $f); 
    $s =~ s/^0//; 
    printf "%-25s %02d:%02d:%02d%s\n", 
      $tag, int($d/3600), int(($d % 3600)/60), int($d % 60), $s; 
} 

my $t1 = gettimeofday; 

{ 
    my $n = factorial_of; 
    my $s = 1; 
    for (my $i = 2; $i <= $n; $i++) 
    { 
     $s *= $i; 
    } 
    print "$s\n: Loop 1\n"; 
} 

my $t2 = gettimeofday; 
delta_t('Count up $s *= $i:',  $t1, $t2); 

{ 
    my $n = factorial_of; 
    my $s = 1; 
    for (my $i = 2; $i <= $n; $i++) 
    { 
     $s = $s * $i; 
    } 
    print "$s\n: Loop 2\n"; 
} 

my $t3 = gettimeofday; 
delta_t('Count up $s *= $i:',  $t1, $t2); 
delta_t('Count up $s = $s * $i:', $t2, $t3); 

{ 
    my $n = factorial_of; 
    my $s = 1; 
    for (my $i = $n; $i > 1; $i--) 
    { 
     $s *= $i; 
    } 
    print "$s\n: Loop 3\n"; 
} 

my $t4 = gettimeofday; 
delta_t('Count up $s *= $i:',  $t1, $t2); 
delta_t('Count up $s = $s * $i:', $t2, $t3); 
delta_t('Count down $s *= $i:',  $t3, $t4); 

{ 
    my $n = factorial_of; 
    my $s = 1; 
    for (my $i = $n; $i > 1; $i--) 
    { 
     $s = $s * $i; 
    } 
    print "$s\n: Loop 4\n"; 
} 

my $t5 = gettimeofday; 
delta_t('Count up $s *= $i:',  $t1, $t2); 
delta_t('Count up $s = $s * $i:', $t2, $t3); 
delta_t('Count down $s *= $i:',  $t3, $t4); 
delta_t('Count down $s = $s * $i:', $t4, $t5); 

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

use Time::HiRes qw(gettimeofday); 
use Digest::MD5 qw(md5_hex); 
use strict; 
use warnings; 
use bigint; 
use constant factorial_of => 13000; 

my ($s, $i); 

my $l1 = sub {my($n) = @_; for ($i = 2; $i <= $n; $i++) { $s *= $i;  }}; 
my $l2 = sub {my($n) = @_; for ($i = 2; $i <= $n; $i++) { $s = $s * $i; }}; 
my $l3 = sub {my($n) = @_; for ($i = $n; $i > 1; $i--) { $s *= $i;  }}; 
my $l4 = sub {my($n) = @_; for ($i = $n; $i > 1; $i--) { $s = $s * $i; }}; 
my $l5 = sub {my($n) = @_; $i = 2; while ($i <= $n) { $s *= $i;  $i++; }}; 
my $l6 = sub {my($n) = @_; $i = 2; while ($i <= $n) { $s = $s * $i; $i++; }}; 
my $l7 = sub {my($n) = @_; $i = $n; while ($i > 1) { $s *= $i;  $i--; }}; 
my $l8 = sub {my($n) = @_; $i = $n; while ($i > 1) { $s = $s * $i; $i--; }}; 

sub timer 
{ 
    my($n, $code, $tag) = @_; 
    my $t1 = gettimeofday; 
    $s = 1; 
    &$code(factorial_of); 
    my $t2 = gettimeofday; 
    my $md5 = md5_hex($s); 
    printf "Loop %d: %-33s %09.6f (%s)\n", $n, $tag, $t2 - $t1, $md5; 
} 

my $count = 1; 
timer($count++, $l1, 'for - Count up $s *= $i:'); 
timer($count++, $l2, 'for - Count up $s = $s * $i:'); 
timer($count++, $l3, 'for - Count down $s *= $i:'); 
timer($count++, $l4, 'for - Count down $s = $s * $i:'); 
timer($count++, $l5, 'while - Count up $s *= $i:'); 
timer($count++, $l6, 'while - Count up $s = $s * $i:'); 
timer($count++, $l7, 'while - Count down $s *= $i:'); 
timer($count++, $l8, 'while - Count down $s = $s * $i:'); 

उदाहरण आउटपुट (MD5 चेकसम लाइन तोड़ने से बचने के लिए संकुचित - पूरा मूल्य 584b3ab832577fd1390970043efc0ec8 है):

Loop 1: for - Count up $s *= $i:  12.853630 (584b3ab8...3efc0ec8) 
Loop 2: for - Count up $s = $s * $i: 20.854735 (584b3ab8...3efc0ec8) 
Loop 3: for - Count down $s *= $i:  14.798155 (584b3ab8...3efc0ec8) 
Loop 4: for - Count down $s = $s * $i: 23.699913 (584b3ab8...3efc0ec8) 
Loop 5: while - Count up $s *= $i:  12.972428 (584b3ab8...3efc0ec8) 
Loop 6: while - Count up $s = $s * $i: 21.192956 (584b3ab8...3efc0ec8) 
Loop 7: while - Count down $s *= $i:  14.555620 (584b3ab8...3efc0ec8) 
Loop 8: while - Count down $s = $s * $i: 23.790795 (584b3ab8...3efc0ec8) 

मैं। संबंधित 'फॉर' लूप पर 'थोड़ी' लूप के लिए लगातार एक छोटा (< 1%) जुर्माना देखें, लेकिन मेरे पास अच्छा स्पष्टीकरण नहीं है यह।

+0

@ जोनाथन लेफ्लर, बहुत बहुत धन्यवाद! आपका चित्रण कोड मेरे लिए बहुत ही निर्देशक है। धन्यवाद :) – Mike

+0

@ जोहानन, अद्यतन कोड के लिए धन्यवाद। मैंने हमेशा सोचा कि $ s * = $ i 'और' $ s = $ s * $ i 'और $ i ++ और $ i-- अलग-अलग शिष्टाचार में एक ही काम कर रहे थे पर मैं गलत था।इसे इंगित करने के लिए बहुत बहुत धन्यवाद :) अब मैंने स्क्रिप्ट के लिए बनाम बदल दिया है और अब मुझे मिल गया है: मेरा $ अब = समय; मेरा $ एन = शिफ्ट; मेरा $ i = 2; मेरा $ s = 1; (; $ i <= $ n; $ i ++) { $ s * = $ i; } और मेरा $ एन = शिफ्ट; मेरा $ i = 2; मेरा $ s = 1; जबकि ($ i <= $ n) { $ s * = $ n; $ i ++; } वे समान दिखते हैं। परिणाम: जबकि तेज़ है। मुझे यकीन नहीं है लेकिन क्या मेरे प्रयोग डिजाइन में कुछ गड़बड़ है? मैंने आपका कोड चलाया है, परिणाम धीमा है जबकि परिणाम था। – Mike

+1

@ माइक: अवशिष्ट मुद्दों के बारे में मुझे अच्छा अनुभव नहीं है। मुख्य बिंदु हैं (1) समस्या प्राथमिक रूप से 'बिगिंट' में है और (2) यह संभावना है कि अवशिष्ट 'जबकि बनाम' मतभेद पर्ल बाइट कोड में गहरे दफन किए जाते हैं। मुझे समय में कुछ भिन्नता मिली - ज्यादातर 0.1 सेकंड या उससे अधिक के क्रम तक, जब तक कोई बैकअप चल रहा था (टाइम कैचिन टू टाइम कैप्सूल); मैंने परीक्षा परीक्षण के लिए असहज बनाने के लिए इतना बड़ा नहीं होने के कारण समझदार समय प्राप्त करने के लिए एक बड़ी संख्या प्राप्त करने के लिए 13000 का चयन किया (उदाहरण के लिए 1 घंटा बहुत लंबा है)। –

5

मैं नीचे हैरान गिर किया जाएगा अगर वहाँ वास्तव में था, जबकि बीच और छोरों के लिए किसी भी "असली" अंतर । यह मानते हुए कि वे "सटीक" एक ही चीज़ कर रहे थे, उन्हें दुभाषिया द्वारा अनुकूलित किया जाना चाहिए ताकि वे कम या ज्यादा समान हों।

मैं डर दूंगा कि अंतर शायद अन्य प्रक्रियाओं से अधिक कुछ नहीं था जो दो निष्पादन के दौरान संसाधनों के लिए अलग-अलग झुकाव कर रहे थे।

यदि कोई अंतर होता है, तो The Sad Tragedy of Micro-Optimization Theater में पकड़े न जाएं।

+0

@ मॉरिनर, मैंने अभी आपके द्वारा सुझाए गए लेख को समाप्त कर लिया है। मैं जो बिंदु बना रहा हूं उसे देखता हूं, धन्यवाद। – Mike

5

बेंचमार्किंग की एक कुंजी को सरल बनाना है। मुद्दा यह है कि for बनाम while की गति है। लेकिन प्रयोग में कई अनावश्यक जटिलताओं शामिल हैं।

  • दो लूप उतने ही नहीं हैं जितना वे हो सकते हैं। एक $s *= $n का उपयोग करता है और दूसरा $s = $s * $i (जोनाथन लेफ्लर बताता है) का उपयोग करता है। एक $n-- का उपयोग करता है और दूसरा $i++ का उपयोग करता है (जो जानता है कि वे गति में भिन्न हैं या नहीं?)।

  • यदि हमें for बनाम while में रुचि है, तो bigint शामिल करने की कोई आवश्यकता नहीं है। यह केवल विषय को भ्रमित करता है। विशेष रूप से, आपकी while स्क्रिप्ट केवल एक bigint ऑब्जेक्ट ($s) पर निर्भर करती है, जबकि आपकी for स्क्रिप्ट उनमें से दो का उपयोग करती है ($s और $i)। यह मुझे आश्चर्य नहीं करता कि for स्क्रिप्ट धीमी है।

संभव के रूप में समान होने के लिए अपने छोरों पुनर्लेखन, factorials काफी छोटा रखें ताकि आप bigint उपयोग करने के लिए नहीं है, और Benchmark मॉड्यूल का उपयोग करें। फिर आप for बनाम while की एक उचित हेड-टू-हेड रेस चला सकते हैं। आपको यह देखने के लिए उत्सुकता होगी कि आपको क्या मिल रहा है।

+1

@ एफएम, मेरा प्रयोग इतना खराब डिजाइन किया गया था कि परिणाम से मेरा अनुमान लगभग पोस्ट किए गए प्रश्न के लगभग पूरी तरह से अप्रासंगिक था। यह कुल विफलता थी। खैर, वैसे भी मुझे इन निर्देशक टिप्पणियों को छोड़ने के लिए धन्यवाद। ऐसा लगता है कि मैं हमेशा आपसे एक चीज़ या दो सीख सकता हूं :) – Mike

+1

@ माइक अपने आप पर बहुत कठिन मत बनो। बेंचमार्किंग मुश्किल है, और अनुभवी प्रोग्रामर भी उन्हें स्थापित करने में गलतियां करते हैं। उदाहरण के लिए: http://stackoverflow.com/questions/1083269/is-perls-unpack-ever-faster-than-substr और http://stackoverflow.com/questions/1960779/why-does-perls-tr-n जाओ-धीमी और धीमी-एज-लाइन लंबाई-वृद्धि हुई है। आपका बेंचमार्क त्रुटिपूर्ण हो सकता है, लेकिन सवाल सफल रहा क्योंकि आपने कुछ उपयोगी चीजें सीखी हैं। :) – FMc

संबंधित मुद्दे