2010-12-09 11 views
19

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

क्या कोई मुझे बता सकता है कि स्ट्रिंग्स के ऐरे में शामिल होने के लिए यह अधिक कुशल (संसाधन और निष्पादन-वार) है या नहीं: उन पर शामिल हों या रुबी प्रोग्रामिंग भाषा में आवश्यक स्ट्रिंग को संयोजित करने के लिए?

धन्यवाद।

उत्तर

29

यह अपने आप Benchmark वर्ग के साथ प्रयास करें।

require "benchmark" 

n = 1000000 
Benchmark.bmbm do |x| 
    x.report("concatenation") do 
    foo = "" 
    n.times do 
     foo << "foobar" 
    end 
    end 

    x.report("using lists") do 
    foo = [] 
    n.times do 
     foo << "foobar" 
    end 
    string = foo.join 
    end 
end 

यह निम्न उत्पादन का उत्पादन:

Rehearsal ------------------------------------------------- 
concatenation 0.300000 0.010000 0.310000 ( 0.317457) 
using lists  0.380000 0.050000 0.430000 ( 0.442691) 
---------------------------------------- total: 0.740000sec 

        user  system  total  real 
concatenation 0.260000 0.010000 0.270000 ( 0.309520) 
using lists  0.310000 0.020000 0.330000 ( 0.363102) 

तो ऐसा लगता है कि संयोजन की तरह एक छोटे से इस मामले में तेजी से होता है। अपने उपयोग के मामले में अपने सिस्टम पर बेंचमार्क।

+6

+1 बेंचमार्क! बेंचमार्क! बेंचमार्क! बेंचमार्क हमारा दोस्त है! :-) –

+1

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

+3

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

4

हां, यह वही सिद्धांत है। मुझे एक प्रोजेक्टउलर पहेली याद है जहां मैंने इसे दोनों तरीकों से आजमाया, कॉलिंग को कॉल करना बहुत तेज़ है।

आप रूबी स्रोत की जाँच करते हैं, तो शामिल होने के सी में सभी कार्यान्वित किया जाता है, यह तार (कोई मध्यवर्ती वस्तु निर्माण, कोई कचरा संग्रहण) श्रृंखलाबद्ध की तुलना में बहुत तेजी से हो रहा है:

/* 
* call-seq: 
*  array.join(sep=$,) -> str 
* 
* Returns a string created by converting each element of the array to 
* a string, separated by <i>sep</i>. 
*  
*  [ "a", "b", "c" ].join  #=> "abc" 
*  [ "a", "b", "c" ].join("-") #=> "a-b-c" 
*/ 

static VALUE 
rb_ary_join_m(argc, argv, ary) 
    int argc; 
    VALUE *argv; 
    VALUE ary; 
{ 
    VALUE sep; 

    rb_scan_args(argc, argv, "01", &sep); 
    if (NIL_P(sep)) sep = rb_output_fs; 

    return rb_ary_join(ary, sep); 
} 

जहां rb_ary_join है :

VALUE rb_ary_join(ary, sep) 
    VALUE ary, sep; 
{ 
    long len = 1, i; 
    int taint = Qfalse; 
    VALUE result, tmp; 

    if (RARRAY(ary)->len == 0) return rb_str_new(0, 0); 
    if (OBJ_TAINTED(ary) || OBJ_TAINTED(sep)) taint = Qtrue; 

    for (i=0; i<RARRAY(ary)->len; i++) { 
    tmp = rb_check_string_type(RARRAY(ary)->ptr[i]); 
    len += NIL_P(tmp) ? 10 : RSTRING(tmp)->len; 
    } 
    if (!NIL_P(sep)) { 
    StringValue(sep); 
    len += RSTRING(sep)->len * (RARRAY(ary)->len - 1); 
    } 
    result = rb_str_buf_new(len); 
    for (i=0; i<RARRAY(ary)->len; i++) { 
    tmp = RARRAY(ary)->ptr[i]; 
    switch (TYPE(tmp)) { 
     case T_STRING: 
     break; 
     case T_ARRAY: 
     if (tmp == ary || rb_inspecting_p(tmp)) { 
     tmp = rb_str_new2("[...]"); 
     } 
     else { 
     VALUE args[2]; 

     args[0] = tmp; 
     args[1] = sep; 
     tmp = rb_protect_inspect(inspect_join, ary, (VALUE)args); 
     } 
     break; 
     default: 
     tmp = rb_obj_as_string(tmp); 
    } 
    if (i > 0 && !NIL_P(sep)) 
     rb_str_buf_append(result, sep); 
    rb_str_buf_append(result, tmp); 
    if (OBJ_TAINTED(tmp)) taint = Qtrue; 
    } 

    if (taint) OBJ_TAINT(result); 
    return result; 
} 
+2

दिलचस्प, लेकिन @Mladen Jablanović बेंचमार्क यह धीमा दिखाता है। –

+1

@Mladen Jablanović अपने बेंचमार्क के बीच में दोनों तार और एक सरणी बना रहा है ... यह जवाब बस बात कर रहा है _only_ 'join' विधि के बारे में - मान लीजिए कि ये चीज़ें पहले से मौजूद हैं। – wulftone

8

अजीब बात है, बेंच मार्किंग आश्चर्य की बात परिणाम देता है (जब तक कि मैंने कुछ गलत कर रहा हूँ):

require 'benchmark' 

N = 1_000_000 
Benchmark.bm(20) do |rep| 

    rep.report('+') do 
    N.times do 
     res = 'foo' + 'bar' + 'baz' 
    end 
    end 

    rep.report('join') do 
    N.times do 
     res = ['foo', 'bar', 'baz'].join 
    end 
    end 

    rep.report('<<') do 
    N.times do 
     res = 'foo' << 'bar' << 'baz' 
    end 
    end 
end 

[email protected]:~/dev/rb$ ruby concat.rb 
          user  system  total  real 
+      1.760000 0.000000 1.760000 ( 1.791334) 
join     2.410000 0.000000 2.410000 ( 2.412974) 
<<     1.380000 0.000000 1.380000 ( 1.376663) 

join पता चला है सबसे धीमी होने के लिए देता है। इसे सरणी बनाने के साथ करना पड़ सकता है, लेकिन वही है जो आपको वैसे भी करना होगा।

ओह Btw,

[email protected]:~/dev/rb$ ruby -v 
ruby 1.9.1p378 (2010-01-10 revision 26273) [i486-linux] 
+1

मुझे लगता है कि अंतर शायद सरणी से लंबी स्ट्रिंग बनाने के बजाय, लूप में छोटे सरणी बनाने के कारण। आपका बेंचमार्क और ऊपर वाला एक अलग परिदृश्य परीक्षण कर रहा है। – jwarchol

+1

यूप। अधिकांश वास्तविक दुनिया के मामलों में आईएमएचओ, हम लाखों की बजाय तारों के मुट्ठी भर से जुड़े हुए हैं। –

3

मैं बस इसके बारे में पढ़ रहा था। अटैचड एक लिंक है जो इसके बारे में बात कर रहा है।

Building-a-String-from-Parts

मैं क्या समझ से, अजगर और जावा तार में, सरणियों के विपरीत अपरिवर्तनीय वस्तुओं रहे हैं, जबकि रूबी में दोनों तार और सरणियों एक दूसरे के रूप में के रूप में परिवर्तनशील हैं। String.concat या < < विधि का उपयोग करके स्ट्रिंग बनाम Array.join बनाने के बीच गति में न्यूनतम अंतर हो सकता है लेकिन यह एक बड़ी समस्या प्रतीत नहीं होता है।

मुझे लगता है कि लिंक यह मेरे द्वारा किए गए बहुत बेहतर समझाएगा।

धन्यवाद,

मार्टिन

+0

यह उत्तर मेरे लिए सबसे उपयोगी था (विशेष रूप से लिंक), लेकिन मुझे बेंचमार्क को सबसे सटीक उत्तर के रूप में चुनना है। – exiquio

0

" समस्या एक पूरे के रूप में डेटा के ढेर है।अपनी पहली स्थिति में, उनके पास दो प्रकार के डेटा स्टॉकपिलिंग थे: (1) निश्चित सीएसवी फ़ाइल में प्रत्येक पंक्ति के लिए एक अस्थायी स्ट्रिंग, निश्चित उद्धरण और ऐसी चीजों के साथ, और (2) विशाल स्ट्रिंग जिसमें सब कुछ शामिल है। यदि प्रत्येक स्ट्रिंग 1k है और वहाँ 5000 पंक्तियों ...

परिदृश्य एक हैं: थोड़ा तार से एक बड़ा स्ट्रिंग निर्माण

अस्थायी तार: 5 megs (5,000k) बड़े पैमाने पर स्ट्रिंग: 5 megs (5,000k) कुल: 10 मेग्स (10,000k) डेव की बेहतर लिपि ने सरणी के लिए भारी स्ट्रिंग को बदल दिया। उन्होंने अस्थायी तारों को रखा, लेकिन उन्हें एक सरणी में संग्रहित किया। सरणी केवल प्रत्येक स्ट्रिंग के पूर्ण आकार की बजाय 5000 * आकार (VALUE) की लागत समाप्त कर देगी। और आम तौर पर, एक VALUE चार बाइट है।

परिदृश्य दो: एक सरणी में तार भंडारण

तार: 5 megs (5,000k) भारी सरणी: 20k

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

http://viewsourcecode.org/why/hacking/theFullyUpturnedBin.html

^यह अंत की तरह ही मैं अजगर में करने के लिए सिखाया गया था जब तक आपरेशन में देरी करने के लिए वास्तव में स्मृति/कचरा संग्रहण के लिए प्रदर्शन में बेहतर है। कारण शुरू है कि आप में से एक बहुत बड़ा हिस्सा मिलता है अंत की ओर आवंटन और वस्तुओं की तत्काल रिलीज।

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