मैटलैब में स्पष्ट लूप की तुलना में अरफुन काफी धीमा हो सकता है। क्यों?


105

निम्नलिखित के लिए सरल गति परीक्षण पर विचार करें arrayfun:

T = 4000;
N = 500;
x = randn(T, N);
Func1 = @(a) (3*a^2 + 2*a - 1);

tic
Soln1 = ones(T, N);
for t = 1:T
    for n = 1:N
        Soln1(t, n) = Func1(x(t, n));
    end
end
toc

tic
Soln2 = arrayfun(Func1, x);
toc

मेरी मशीन पर (लिनक्स टकसाल 12 पर मतलाब 2011 बी), इस परीक्षण का आउटपुट है:

Elapsed time is 1.020689 seconds.
Elapsed time is 9.248388 seconds.

क्या?!? arrayfun, जबकि स्पष्ट रूप से एक क्लीनर दिखने वाला समाधान, परिमाण धीमे का एक क्रम है। यहाँ क्या हो रहा है?

इसके अलावा, मैंने इसके लिए एक समान शैली का परीक्षण किया cellfunऔर इसे स्पष्ट लूप की तुलना में लगभग 3 गुना धीमा पाया। फिर, यह परिणाम मेरी अपेक्षा के विपरीत है।

मेरा सवाल है: क्यों arrayfunऔर cellfunइतने धीमे हैं? और यह देखते हुए, क्या उन्हें उपयोग करने के लिए कोई अच्छा कारण हैं (कोड को अच्छा दिखने के अलावा अन्य)?

नोट: मैं arrayfunयहाँ मानक संस्करण के बारे में बात कर रहा हूँ , समानांतर प्रसंस्करण टूलबॉक्स से GPU संस्करण नहीं।

संपादित करें: बस स्पष्ट होने के लिए, मुझे पता है कि Func1ऊपर ओली द्वारा बताया गया है। मैंने केवल इसे चुना क्योंकि यह वास्तविक प्रश्न के प्रयोजनों के लिए एक सरल गति परीक्षण देता है।

EDIT: ग्रंजेटा के सुझाव के बाद, मैंने फिर से परीक्षण किया feature accel off। परिणाम हैं:

Elapsed time is 28.183422 seconds.
Elapsed time is 23.525251 seconds.

दूसरे शब्दों में, यह प्रतीत होता है कि अंतर का एक बड़ा हिस्सा यह है कि जेआईटी त्वरक स्पष्ट forलूप को गति देने की तुलना में बहुत बेहतर काम करता है arrayfun। यह मुझे अजीब लगता है, क्योंकि arrayfunवास्तव में अधिक जानकारी प्रदान करता है, अर्थात, इसके उपयोग से पता चलता है कि कॉल का क्रम Func1मायने नहीं रखता है। इसके अलावा, मैंने नोट किया कि क्या जेआईटी त्वरक चालू या बंद है, मेरा सिस्टम केवल एक सीपीयू का उपयोग करता है ...


10
सौभाग्य से, "मानक समाधान" सबसे तेजी से दूर रहता है: टिक; 3 * x ^ 2 + 2 * एक्स 1। toc बीता हुआ समय 0.030662 सेकंड है।
ओली

4
@ मुझे लगता है कि मुझे अनुमान होना चाहिए था कि कोई इसे इंगित करेगा और एक ऐसे फ़ंक्शन का उपयोग करेगा जिसे वेक्टर नहीं किया जा सकता है :-)
कॉलिन टी बोवर्स

3
मुझे यह देखने में दिलचस्पी होगी कि JIT त्वरक बंद होने पर यह समय कैसे बदलता है। कमांड 'फीचर एक्सिलेंस ऑफ' को निष्पादित करें और फिर अपने परीक्षण को फिर से चलाएँ।
grungetta

@grungetta दिलचस्प सुझाव। मैंने कुछ टिप्पणियों के साथ परिणाम को प्रश्न में जोड़ दिया है।
कॉलिन टी बोवर्स

जवाबों:


101

आप अपने कोड के अन्य संस्करणों को चलाकर विचार प्राप्त कर सकते हैं। अपने लूप में एक फ़ंक्शन का उपयोग करने के बजाय, गणनाओं को स्पष्ट रूप से लिखने पर विचार करें

tic
Soln3 = ones(T, N);
for t = 1:T
    for n = 1:N
        Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
    end
end
toc

मेरे कंप्यूटर पर गणना करने का समय:

Soln1  1.158446 seconds.
Soln2  10.392475 seconds.
Soln3  0.239023 seconds.
Oli    0.010672 seconds.

अब, जबकि पूरी तरह से 'सदिश' समाधान स्पष्ट रूप से सबसे तेज़ है, आप देख सकते हैं कि प्रत्येक x प्रविष्टि के लिए बुलाए जाने वाले फ़ंक्शन को परिभाषित करना एक बहुत बड़ा ओवरहेड है। बस स्पष्ट रूप से गणना लिखने से हमें कारक 5 स्पीडअप मिला। मुझे लगता है कि यह दिखाता है कि MATLABs JIT संकलक इनलाइन फ़ंक्शन का समर्थन नहीं करता है । वहाँ gnovice द्वारा जवाब के अनुसार, यह वास्तव में एक अनाम एक के बजाय एक सामान्य कार्य लिखने के लिए बेहतर है। कोशिश करो।

अगला चरण - आंतरिक लूप को हटाएं (वेक्टर करें):

tic
Soln4 = ones(T, N);
for t = 1:T
    Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc

Soln4  0.053926 seconds.

एक अन्य कारक 5 स्पीडअप: उन बयानों में कुछ ऐसा है जो आपको MATLAB में लूप से बचना चाहिए ... या क्या वास्तव में है? इस पर एक नजर है

tic
Soln5 = ones(T, N);
for n = 1:N
    Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc

Soln5   0.013875 seconds.

'पूरी तरह से' सदिश संस्करण के करीब। Matlab कॉलम कॉलम-वार को संग्रहीत करता है। आपको हमेशा (जब संभव हो) अपने कम्प्यूटेशंस को 'कॉलम-वार' करने के लिए तैयार करना चाहिए।

अब हम वापस सोलन 3 जा सकते हैं। लूप ऑर्डर 'पंक्ति-वार' है। इसे बदलने की सुविधा देता है

tic
Soln6 = ones(T, N);
for n = 1:N
    for t = 1:T
        Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
    end
end
toc

Soln6  0.201661 seconds.

बेहतर है, लेकिन अभी भी बहुत खराब है। सिंगल लूप - अच्छा। डबल लूप - खराब। मुझे लगता है कि MATLAB ने लूप के प्रदर्शन को बेहतर बनाने के लिए कुछ अच्छे काम किए, लेकिन फिर भी लूप ओवरहेड है। यदि आपके अंदर कुछ भारी काम होगा, तो आप ध्यान नहीं देंगे। लेकिन चूंकि यह गणना मेमोरी बैंडविड्थ से बंधी है, आप लूप ओवरहेड देखते हैं। और आप और भी स्पष्ट रूप से वहाँ Func1 कॉलिंग के ओवरहेड देखेंगे।

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

tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc

Soln7  0.016786 seconds.

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

तो एक साधारण लूप संरचना की तुलना में अरफुन धीमा क्यों है? दुर्भाग्य से, हमारे लिए यह सुनिश्चित करना असंभव है, क्योंकि कोई स्रोत कोड उपलब्ध नहीं है। आप केवल अनुमान लगा सकते हैं कि चूंकि arrayfun एक सामान्य उद्देश्य फ़ंक्शन है, जो सभी प्रकार के विभिन्न डेटा संरचनाओं और तर्कों को संभालता है, यह आवश्यक नहीं है कि सरल मामलों में बहुत तेज़ हो, जिसे आप सीधे लूप घोंसले के रूप में व्यक्त कर सकते हैं। ओवरहेड कहां से आता है हम नहीं जान सकते। क्या बेहतर क्रियान्वयन से ओवरहेड से बचा जा सकता है? शायद नहीं। लेकिन दुर्भाग्य से केवल एक चीज जो हम कर सकते हैं वह है मामलों की पहचान करने के लिए प्रदर्शन का अध्ययन, जिसमें यह अच्छी तरह से काम करता है, और वे, जहां यह नहीं है।

अपडेट करें क्योंकि इस परीक्षण का निष्पादन समय कम है, विश्वसनीय परिणाम प्राप्त करने के लिए मैंने परीक्षणों के चारों ओर एक लूप जोड़ा:

for i=1:1000
   % compute
end

नीचे दिए गए कुछ समय:

Soln5   8.192912 seconds.
Soln7  13.419675 seconds.
Oli     8.089113 seconds.

आप देखते हैं कि सरणीफुन अभी भी खराब है, लेकिन कम से कम परिमाण के तीन आदेश वेक्टरकृत समाधान से भी बदतर हैं। दूसरी ओर, स्तंभ-वार संगणना के साथ एक एकल लूप पूरी तरह से सदिश संस्करण के रूप में तेज़ है ... यह सब एक सीपीयू पर किया गया था। Soln5 और Soln7 के लिए परिणाम नहीं बदलते हैं अगर मैं 2 कोर पर स्विच करता हूं - Soln5 में मुझे इसे समानांतर करने के लिए एक parfor का उपयोग करना होगा। स्पीडअप के बारे में भूल जाओ ... Soln7 समानांतर में नहीं चलता है क्योंकि arrayfun समानांतर में नहीं चलता है। दूसरी ओर ओलिस वेक्टरकृत संस्करण:

Oli  5.508085 seconds.

9
बहुत बढ़िया जवाब! और matlab केंद्रीय सभी के लिंक बहुत दिलचस्प रीड प्रदान करते हैं। बहुत धन्यवाद।
कोलिन टी बोवर्स

यह एक अच्छा विश्लेषण है।
एच। मस्टर

और एक दिलचस्प अपडेट! यह उत्तर बस :-)
कोलिन टी बोवर्स

3
बस एक छोटी सी टिप्पणी; MATLAB 6.5 में वापस, cellfunMEX- फ़ाइल के रूप में लागू किया गया था (इसके साथ सी स्रोत कोड उपलब्ध है)। यह वास्तव में काफी सीधा था। बेशक यह केवल 6 हार्ड-कोडित कार्यों में से एक को लागू करने का समर्थन करता था (आप एक फ़ंक्शन हैंडल पास नहीं कर सकते थे, केवल एक स्ट्रिंग जिसमें एक फ़ंक्शन नाम है)
अमरो

1
अरफुन + फंक्शन हैंडल = स्लो! भारी कोड में उनसे बचें।
यवन

-8

कि क्योंकि!!!!

x = randn(T, N); 

gpuarrayटाइप नहीं है ;

आपको बस इतना करना है

x = randn(T, N,'gpuArray');

2
मुझे लगता है कि आपको @angainor द्वारा प्रश्न और उत्कृष्ट उत्तर को थोड़ा और ध्यान से पढ़ने की आवश्यकता है। इससे कोई लेना-देना नहीं है gpuarray। यह लगभग निश्चित रूप से क्यों इस जवाब को नीचा दिखाया गया है।
कॉलिन टी बोवर्स

@ कोलिन - मैं सहमत हूँ कि अंगरेज अधिक गहन हैं, लेकिन उत्तर में 'gpuArray' का उल्लेख नहीं है। मुझे लगता है कि 'gpuArray' यहाँ एक अच्छा योगदान है (यदि इसकी सही है)। साथ ही, सवाल यह है कि "यहाँ क्या हो रहा है?" , तो मुझे लगता है कि इसने डेटा को वेक्टर करने और जीपीयू से अलग करने जैसे अतिरिक्त तरीकों के लिए दरवाजा खोल दिया। मैं इस उत्तर की सवारी कर रहा हूं क्योंकि यह भविष्य के आगंतुकों के लिए मूल्य जोड़ सकता है। अगर मैंने गलत कॉल किया तो मेरी माफ़ी।
jww

1
आप इस तथ्य को भी भूल जाते हैं कि gpuarrayयह केवल एनवीडिया ग्राफिक्स कार्ड के लिए समर्थित है। क्या उनके पास ऐसा हार्डवेयर नहीं होना चाहिए, तब आपकी सलाह (या कमी) व्यर्थ है। -1
रायरेंग

दूसरी ओर, gpuarray matlab सदिश प्रोग्रामिंग का हल्का कृपाण है।
MrIO
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.