2016-01-12 3 views
10

this पृष्ठ पर यह कहता है कि push!() और append!() बहुत प्रभावी हैं।पुश कितने कुशल हैं!() और जूलिया में तरीकों को जोड़ना?

मेरा सवाल यह है कि वे वास्तव में कितने कुशल हैं?

अर्थात्

यदि एक अंतिम सरणी के आकार को जानता है, यह अभी भी सरणी preallocate या यह संवर्द्धित बढ़ रही append!()/push!() का उपयोग कर बस के रूप में कुशल हो जाएगा करने के लिए तेजी से होता है?

अब इस मामले पर विचार करें जब एक अंतिम सरणी का आकार नहीं जानता है। उदाहरण के लिए, एकाधिक एरे को 1 बड़े सरणी में विलय करना (इसे A पर कॉल करें)। इस लक्ष्य को हासिल करने के लिए

दो तरीके:

  1. append!()A करने के लिए प्रत्येक सरणी, जिसका आकार पूर्व आबंटित नहीं किया गया है आईएनजी।
  2. मर्ज किए गए सरणी A के अंतिम आकार को खोजने के लिए प्रत्येक सरणी के पहले योग आयाम। फिर A प्रीलोकेट करें और प्रत्येक सरणी की सामग्री पर प्रतिलिपि बनाएँ।

इस मामले में कौन सा अधिक कुशल होगा?

+1

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

+0

निश्चित रूप से आपको जूलिया की जांच करने की सलाह देगा, खासकर यदि आपको पायथन पसंद है। – aberdysh

+0

आपको धन्यवाद देगा –

उत्तर

10

इस तरह के एक प्रश्न का उत्तर आमतौर पर है: "यह निर्भर करता है"। उदाहरण के लिए, आप किस आकार सरणी को बनाने की कोशिश कर रहे हैं? सरणी का तत्व-प्रकार क्या है?

लेकिन यदि आप एक ह्युरिस्टिक के बाद हैं, तो एक साधारण गति परीक्षण क्यों नहीं चलाते? उदाहरण के लिए, निम्नलिखित स्निपेट:

function f1(N::Int) 
    x = Array(Int, N) 
    for n = 1:N 
     x[n] = n 
    end 
    return(x) 
end 

function f2(N::Int) 
    x = Array(Int, 0) 
    for n = 1:N 
     push!(x, n) 
    end 
    return(x) 
end 

f1(2) 
f2(2) 

N = 5000000000 
@time f1(N) 
@time f2(N) 

पता चलता है कि push! उपयोग के बारे में 6 बार पूर्व के आवंटन की तुलना में धीमी है। यदि आप कम चरणों के साथ बड़े ब्लॉक जोड़ने के लिए append! का उपयोग कर रहे थे, तो गुणक लगभग निश्चित रूप से कम होगा।

जब इन नंबरों की व्याख्या, "क्या !? 6 गुना धीमी !?" के घुटने झटका प्रतिक्रिया का विरोध। इस संख्या को संदर्भ में रखा जाना चाहिए कि आपके पूरे कार्यक्रम/फ़ंक्शन/सबराउटिन के लिए सरणी निर्माण कितना महत्वपूर्ण है। उदाहरण के लिए, यदि सरणी इमारत अपनी दिनचर्या का रन-टाइम का केवल 1% (सबसे विशिष्ट दिनचर्या के लिए, सरणी इमारत ज्यादा 1% से कम होंगे), तो 100 सेकंड के लिए अपनी दिनचर्या रन, 1 सेकंड खर्च कर रहा है, तो शामिल निर्माण सरणी 6 सेकंड प्राप्त करने के लिए 6 से गुणा करें। 99 सेकंड + 6 सेकंड = 105 सेकंड। इसलिए, प्री-आवंटन के बजाय push! का उपयोग करके आपके पूरे कार्यक्रम के रनटाइम को 5% तक बढ़ा दिया जाता है। जब तक आप उच्च आवृत्ति व्यापार में काम नहीं करते हैं, तो आप शायद इसके बारे में परवाह नहीं करेंगे।

अपने आप के लिए, मेरे सामान्य नियम यह है: अगर मैं कर सकते हैं पूर्व आवंटित आसानी से, तो मैं पहले से आवंटित। लेकिन अगर push! दिनचर्या बहुत कोड के लिए आसान, शुरू कीड़े के निचले संभावना के साथ, और कम उचित सरणी आकार पूर्व निर्धारित करने के लिए कोशिश कर रहा है चारों ओर खिलवाड़ करता है, तो मैं push! एक दूसरे सोचा के बिना उपयोग करें।

अंतिम नोट: यदि आप वास्तव में कैसे काम करता है push! की बारीकियों को देखने के लिए चाहते हैं, आप के बाद से julia source सिर्फ एक ccall लपेटता सी दिनचर्या में तल्लीन करने की आवश्यकता होगी।

अद्यतन: ओपी टिप्पणी में push! के बीच का अंतर और MATLAB में array(end+1) = n की तरह एक ऑपरेशन पर सवाल उठाया। मैंने हाल ही में MATLAB में कोड नहीं किया है, लेकिन मैं अपनी मशीन पर एक प्रतिलिपि रखता हूं क्योंकि मेरे सभी पुराने कागजात के लिए कोड MATLAB में है। मेरा वर्तमान संस्करण R2014a है। मेरी समझ यह है कि MATLAB के इस संस्करण में, सरणी के अंत में जोड़ने से संपूर्ण सरणी को फिर से आवंटित किया जाएगा। इसके विपरीत, जूलिया में push! काम करता है, मेरे सर्वोत्तम ज्ञान के लिए, .NET में सूचियों की तरह। वेक्टर के आवंटित स्मृति को गतिशील रूप से ब्लॉक में जोड़ा जाता है क्योंकि वेक्टर के आकार बढ़ते हैं। यह बड़े आवंटन की मात्रा को बड़े पैमाने पर कम करता है जिसे करने की आवश्यकता है, हालांकि मेरी समझ यह है कि कुछ पुनः आवंटन अभी भी जरूरी है (मुझे इस बिंदु पर सही होने में खुशी है)। तो push! को मैटलैब में एक सरणी जोड़ने से अधिक काम करना चाहिए।

N = 10000000; 
tic 
x = ones(N, 1); 
for n = 1:N 
    x(n) = n; 
end 
toc 


N = 10000000; 
tic 
x = []; 
for n = 1:N 
    x(end+1) = n; 
end 
toc 

मैं:

Elapsed time is 0.407288 seconds. 
Elapsed time is 1.802845 seconds. 

5 बार मंदी के बारे में तो, तो हम निम्नलिखित MATLAB कोड चला सकते हैं। समय पद्धति में लागू चरम गैर-कठोरता को देखते हुए, कोई यह कहने का लुत्फ उठा सकता है कि यह जूलिया मामले के बराबर है। लेकिन प्रतीक्षा करें, अगर हम जूलिया में व्यायाम को N = 10000000 के साथ फिर से चलाते हैं, तो समय 0.01 और 0.07 सेकंड होते हैं। MATLAB संख्याओं के लिए इन संख्याओं की परिमाण में तीव्र अंतर मुझे हुड के तहत वास्तव में क्या हो रहा है, इस बारे में दावा करने के बारे में बहुत परेशान करता है, और क्या MATLAB में 5-बार की मंदी की तुलना में 6 गुना मंदी की तुलना करना वैध है या नहीं जूलिया। असल में, अब मैं अपनी गहराई से बाहर हूं। हो सकता है कि कोई ऐसा व्यक्ति जो मैडलब वास्तव में हुड के तहत करता है, और अधिक अंतर्दृष्टि प्रदान कर सकता है। जूलिया के बारे में, मैं सी-कोडर का अधिकतर नहीं हूं, इसलिए मुझे संदेह है कि मुझे स्रोत के माध्यम से देखने में बहुत अंतर्दृष्टि मिलेगी (जो सार्वजनिक रूप से उपलब्ध है, MATLAB के विपरीत)।

+0

यह दावा करता है कि यह" अधिक कुशल "जैसा प्रतीत नहीं होता है। जब मैंने इसे पढ़ा तो मुझे बहुत उत्साहित हो गया कि वे किसी भी तरह से मैथवर्क्स में मेमोरी आवंटन/कचरा संग्रह को संभालने के लिए अलग-अलग, अधिक कुशल दृष्टिकोण के साथ आए, लेकिन ऐसा लगता है कि जूलिया के 'ऐपेंड!() '/' धक्का दें!() 'विधियों की तुलना में तेज़ हैं मैटलैब समकक्ष सामान्य रूप से जूलिया की गति के कारण है। – aberdysh

+1

पिछली बार जब मैंने मैटलैब में कोड किया था तो 'पुश!' और 'संलग्न' के बराबर नहीं था! यदि आप इसे विकसित करना चाहते हैं तो आपको पूरे वेक्टर को फिर से आवंटित करना होगा। माना जाता है, यह थोड़ी देर पहले था। यह स्पष्ट रूप से * धक्का से ज्यादा * धीमा था! मैं वास्तव में इस संबंध में किसी भी नई कार्यक्षमता Matlab पर बहुत अधिक टिप्पणी नहीं दे सकता। लेकिन मैं अपने मूल दावे से खड़ा हूं: * अधिकांश * व्यावहारिक समस्याओं के लिए, सरणी निर्माण में 6 गुना गुणक शायद ही ध्यान देने योग्य होगा। –

+0

स्पष्ट करने के लिए, MATLAB के जूलिया के धक्का के बराबर!() 'मेरा मतलब था' सरणी (अंत + 1) = वैल', मुझे लगता है कि इस तरह के वाक्यविन्यास के लिए समर्थन थोड़ी देर के लिए MATLAB में मौजूद था, और हां, यह वास्तव में पुनः- आवंटन, लेकिन आपको क्या लगता है कि 'पुश!()' या 'संलग्न करें!()' जूलिया में फिर से आवंटन नहीं करते? क्या 6x ओवरहेड के पीछे कारण नहीं है? – aberdysh

11

push! हमेशा एक पूर्व आवंटित सरणी में डालने, की तुलना में धीमी हो सकता है अगर कोई अन्य कारण से push! (1) तत्व सम्मिलित करता है, बस के रूप में जब आप ऐसा मैन्युअल रूप से करना, और (2) सरणी की लंबाई वृद्धि कर देता है के लिए । दो ऑपरेशन एक से अधिक तेज़ नहीं हो सकते हैं, जब कोई दो का हिस्सा होता है।

हालांकि, जैसा कि अन्य उत्तरों में बताया गया है कि अंतर अक्सर चिंतित होने के लिए इतना बड़ा नहीं होता है। आंतरिक रूप से (पिछली बार मैंने कोड की जांच की), जूलिया एक बढ़ती-ए-फैक्टर-ऑफ-2 रणनीति का उपयोग करती है, ताकि आपको केवल log2(N) पुनर्वितरण की आवश्यकता हो।

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

+0

कड़ाई से बोलते हुए, मुझे नहीं लगता कि 'पुश!' एक नया तत्व डालता है "जैसे आप मैन्युअल रूप से करते हैं"। मेरा मानना ​​है कि सी ++ (और संभवतः जूलिया भी) प्रत्येक सरणी के अंतिम तत्व के ठीक पहले स्मृति स्थान पर एक विशेष सूचक संग्रहीत करता है, और 'धक्का!' सीधे उस स्मृति स्थान में लिखता है। दूसरी ओर, पूर्व-प्रारंभिक सरणी में डालने के माध्यम से, कहें, 'arr [5] = 1' पॉइंटर को' arr' के पहले तत्व में ले जाता है और वांछित लेखन स्थान पर पॉइंटर प्राप्त करने के लिए 5 जोड़ता है। तो 'धक्का!' के लिए एक कम सूचक अंकगणितीय ऑपरेशन की आवश्यकता है ... – tparker

+0

... यह निश्चित रूप से सभी ओवरहेड को दूर करने की संभावना नहीं है जो 'धक्का!' मैन्युअल सम्मिलन से अधिक है, लेकिन फिर भी यह 'धक्का!' के लिए तर्कसंगत रूप से संभव है कम सूचक अंकगणितीय परिचालन करके मैन्युअल प्रविष्टि से तेज़ हो। – tparker

+0

@tparker, 'push @ (a, x) 'पर' @ code_llvm' आज़माएं और' store_at_end! (A, x) = (@inbounds a [end] = x; a) '। – tholy

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