2014-05-20 5 views
20

मैंने रुबी के सरणी concat() बनाम + ऑपरेशन और concat() का बहुत छोटा प्रदर्शन परीक्षण किया था।रूबी ऐरे कंसैट बनाम + गति?

हालांकि मुझे यह स्पष्ट नहीं है कि concat() इतना तेज़ क्यों है?

क्या कोई यहां सहायता कर सकता है?

इस कोड को मैं प्रयोग किया जाता है:

t = Time.now 
ar = [] 
for i in 1..10000 
ar = ar + [4,5] 
end 
puts "Time for + " + (Time.now - t).to_s 


t = Time.now 
ar = [] 
for i in 1..10000 
ar.concat([4,5]) 
end 
puts "Time for concat " + (Time.now - t).to_s 
+6

FYI करें :) http://www.ruby-doc.org/stdlib-1.9.3/libdoc/benchmark/rdoc /Benchmark.html –

+0

http://www.joelonsoftware.com/articles/fog0000000319.html – Fuser97381

उत्तर

34

Ruby docs के अनुसार, अंतर है:

सरणी # +:

कड़ी - निर्मित एक नई सरणी देता है एक तिहाई सरणी बनाने के लिए एक साथ दो सरणी को जोड़कर।

सरणी # concat:

सरणी # concat: स्वयं को other_ary के तत्वों जोड़ता है।

तो + ऑपरेटर, एक नई सरणी हर बार यह कहा जाता है (जो महंगा है) पैदा करेगा, जबकि concat केवल नए तत्व जोड़ देता है।

13

उत्तर + ऑपरेटर और concat विधियों के रूबी के अंतर्निहित सी कार्यान्वयन में निहित है।

Array#+

rb_ary_plus(VALUE x, VALUE y) 
{ 
    VALUE z; 
    long len, xlen, ylen; 

    y = to_ary(y); 
    xlen = RARRAY_LEN(x); 
    ylen = RARRAY_LEN(y); 
    len = xlen + ylen; 
    z = rb_ary_new2(len); 

    ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR(x)); 
    ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR(y)); 
    ARY_SET_LEN(z, len); 
    return z; 
} 

Array#concat

rb_ary_concat(VALUE x, VALUE y) 
{ 
    rb_ary_modify_check(x); 
    y = to_ary(y); 
    if (RARRAY_LEN(y) > 0) { 
     rb_ary_splice(x, RARRAY_LEN(x), 0, y); 
    } 
    return x; 
} 

आप देख सकते हैं, + ऑपरेटर प्रत्येक सरणी से स्मृति को कॉपी है, तो बनाने और सामग्री के साथ एक तिहाई सरणी लौटने दोनों। concat विधि केवल नए सरणी को मूल में विभाजित कर रही है।

+0

बिल्कुल सही। धन्यवाद! –

+0

+ = के बारे में क्या? क्या यह तकनीकी रूप से #concat जैसा ही है? –

+1

@ रोइस में नोइस, आप ऑपरेंड अभिव्यक्तियों को कुछ तरीकों से लिख सकते हैं। यहां सबसे प्रासंगिक 'x = x + y' है, जो 'x + = y' के बराबर है। जब तक कोई वर्ग विशेष रूप से 'concat' को प्रतिनिधि करने के लिए प्लस ऑपरेटर को ओवरराइट नहीं करता है,' + = '' concat' जैसा बिल्कुल नहीं करेगा क्योंकि यह 'rb_ary_concat' की बजाय' rb_ary_plus' का उपयोग करता है। –

7

यदि आप मानक चलाने के लिए जा रहे हैं, तो प्रीबिल्ट टूल का लाभ उठाएं और परीक्षण को कम से कम जांचें जो आप जानना चाहते हैं।

Fruity है, जो अपनी बेंच मार्किंग के लिए खुफिया के एक बहुत प्रदान करता है के साथ शुरू:

require 'fruity' 

compare do 
    plus { [] + [4, 5] } 
    concat { [].concat([4, 5]) } 
end 
# >> Running each test 32768 times. Test will take about 1 second. 
# >> plus is similar to concat 

जब चीजें काफी निकट के बारे में वास्तव में चिंता मत कर रहे हैं, फल हमें बताओ कि वे "समान" कर रहे हैं।

require 'benchmark' 

N = 10_000_000 
3.times do 
    Benchmark.bm do |b| 
    b.report('plus') { N.times { [] + [4, 5] }} 
    b.report('concat') { N.times { [].concat([4,5]) }} 
    end 
end 
# >>  user  system  total  real 
# >> plus 1.610000 0.000000 1.610000 ( 1.604636) 
# >> concat 1.660000 0.000000 1.660000 ( 1.668227) 
# >>  user  system  total  real 
# >> plus 1.600000 0.000000 1.600000 ( 1.598551) 
# >> concat 1.690000 0.000000 1.690000 ( 1.682336) 
# >>  user  system  total  real 
# >> plus 1.590000 0.000000 1.590000 ( 1.593757) 
# >> concat 1.680000 0.000000 1.680000 ( 1.684128) 

सूचना अलग-अलग समय:

उस बिंदु पर रूबी के अंतर्निहित Benchmark वर्ग में मदद कर सकते

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

+0

केवल चयनित इनपुट पर वास्तविक प्रदर्शन समय सहित अपेक्षाकृत भ्रामक है क्योंकि यह एन का बहुत छोटा मूल्य है जो बहुत ही कम 'अंतर' पैदा करता है। – user2864740

+0

(मूल परीक्षण वास्तव में 'अप्रत्याशित साइड इफेक्ट' द्वारा प्रदर्शन अंतर को बेहतर दिखाते हैं।) – user2864740

1

ओपी का प्रश्न, जैसा कि अन्य उत्तरों में उल्लेख किया गया है, दो ऑपरेटरों की तुलना कर रहा है जो विभिन्न उद्देश्यों को पूरा करते हैं। एक, concat, जो मूल सरणी (mutates) को मूल सरणी, और + है जो विनाशकारी (शुद्ध कार्यात्मक, कोई उत्परिवर्तन) नहीं है।

मैं यहां एक और तुलनीय परीक्षण की तलाश में आया, उस समय महसूस नहीं कर रहा था, कि कंसट विनाशकारी था। यदि यह दो पूर्ण रूप से कार्यात्मक, गैर-विनाशकारी परिचालनों की तुलना करने के लिए दूसरों के लिए उपयोगी है, तो यहां सरणी विस्तार (array1 + array2) बनाम सरणी विस्तार ([*array1, *array2]) का बेंचमार्क है। दोनों, जहां तक ​​मुझे पता है, परिणामस्वरूप 3 सरणी बनाई जा रही हैं: 2 इनपुट सरणी, 1 नया परिणामी सरणी।

संकेत: + जीतता है।

कोड

# a1 is a function producing a random array to avoid caching 
a1 = ->(){ [rand(10)] } 
a2 = [1,2,3] 
n = 10_000_000 
Benchmark.bm do |b| 
    b.report('expand'){ n.times{ [*a1[], *a2] } } 
    b.report('add'){ n.times{ a1[]+a2 } } 
end 

परिणाम

user  system  total  real 
expand 9.970000 0.170000 10.140000 (10.151718) 
add 7.760000 0.020000 7.780000 ( 7.792146) 
संबंधित मुद्दे