2012-10-16 10 views
8

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

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

अगर वहाँ महत्वपूर्ण ओवरहेड है, मैं इतना के रूप में कार्य ही है और तो इस बात का एक MEX बनाने के लिए लूप जोड़ने के लिए कोड के पुनर्गठन के बारे में सोच रहा हूँ। ऐसा करने से पहले, मैं इस पर खर्च किए गए समय को उचित ठहराने के लिए अपनी धारणा को मान्य करना चाहता हूं।

अद्यतन:

मैं @ angainor के सुझाव की कोशिश की, और निम्न कोड के साथ बनाया donothing.m:

function nothing = donothing(dummy) %#codegen 
nothing = dummy; 
end 

फिर, मैं एक MEX समारोह इस से donothing_mex के रूप में बनाया है, और निम्नलिखित कोड की कोशिश की :

tic; 
for i=1:1000000 
    donothing_mex(5); 
end 
toc; 

परिणाम यह था कि समारोह में दस लाख कॉल लगभग 9 सेकंड लग गए। यह हमारे उद्देश्यों के लिए एक महत्वपूर्ण ओवरहेड नहीं है, इसलिए अब मुझे लगता है कि मैं अकेले बुलाए गए फ़ंक्शन को मैक्स में बदल दूंगा। हालांकि, एक लूप से फ़ंक्शन को कॉल करना जो दस लाख बार निष्पादित करता है, यह पीछे की ओर एक सुंदर बेवकूफ विचार प्रतीत होता है, क्योंकि यह प्रदर्शन महत्वपूर्ण कोड है, इसलिए लूप को मैक्स फ़ंक्शन में ले जाना अभी भी पुस्तकों में है, लेकिन बहुत कम प्राथमिकता के साथ।

+1

निश्चित रूप से लूप को कोड पर ले जाएं। इसे मैक्स कोड की केवल 2 या 3 अतिरिक्त लाइनों की आवश्यकता होनी चाहिए और आपको 9 से 9 सेकंड्स बचाएंगे। – twerdster

+2

@ एंजैनॉर के उत्तर पर आपकी टिप्पणियों के आधार पर, आपके द्वारा उठाए जा रहे दृष्टिकोण में [XY समस्या] का अत्याचार है (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), उदाहरण के लिए, एमएक्स जिसे आप एक प्रदर्शन समस्या हल करने के लिए बनाना चाहते हैं * हो सकता है * मैटलैब में बस एक तेज़ समाधान हो सकता है, सिर्फ एक जिसे आपने पहले नहीं सोचा था। क्या आप शायद उस लूप में जो गणना करना चाहते हैं उसका सार पोस्ट कर सकते हैं? –

+0

@RodyOldenhuis यह भी एक अच्छा मुद्दा है। [समयपूर्व अनुकूलन सभी बुराइयों की जड़ है] (http://shreevatsa.wordpress.com/2008/05/16/premature-optimization-is-the-root-of-all-evil/);) – angainor

उत्तर

5

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

MATLAB में लगभग हर चीज इस उच्च स्तरीय भाषा की प्रकृति के कारण कुछ ओवरहेड से जुड़ा हुआ है। जब तक आपके पास कोई कोड न हो, जिसे आप निश्चित रूप से पूरी तरह से जेआईटी के साथ संकलित कर चुके हैं (लेकिन फिर आपको एक मेक्स फ़ाइल की आवश्यकता नहीं है :)) तो आपके पास एक ओवरहेड का विकल्प है ..

तो समेट - I एमएक्स कॉलिंग ओवरहेड से बहुत डर नहीं होगा।

संपादित जैसा कि अक्सर यहाँ और कहीं और सुना है, केवल उचित बात किसी विशेष मामले में क्या करने के लिए पाठ्यक्रम बेंचमार्क की है और अपने स्वयं के लिए जाँच। आप आसानी से एक छोटी सी MEX समारोह लिख कर MEX कॉल भूमि के ऊपर का अनुमान कर सकते हैं:

#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[ ], int nrhs, const mxArray *prhs[ ]) 
{  
} 

अपने कंप्यूटर पर आप

tic; for i=1:1000000; mexFun; end; toc 
Elapsed time is 2.104849 seconds. 

मिल MEX प्रति कॉल 2 ई-6s भूमि के ऊपर है कि।अपना कोड जोड़ें, इसे समय दें और देखें, अगर ओवरहेड स्वीकार्य स्तर पर है, या नहीं।

जैसा कि एंड्रयू जंक ने नीचे उल्लेख किया है (धन्यवाद!), एमएक्स फ़ंक्शन ओवरहेड जाहिर है कि आप एमएक्स फ़ंक्शन में भेजे गए तर्कों की संख्या पर निर्भर करते हैं। यह एक छोटा सा निर्भरता है, लेकिन यह नहीं है:

a = ones(1000,1); 
tic; for i=1:1000000; mexFun(a); end; toc 
Elapsed time is 2.41 seconds. 

यह a के आकार से संबंधित नहीं है:

a = ones(1000000,1); 
tic; for i=1:1000000; mexFun(a); end; toc 
Elapsed time is 2.41805 seconds. 

लेकिन यह तर्क की संख्या से संबंधित है

a = ones(1000000,1); 
b = ones(1000000,1); 
tic; for i=1:1000000; mexFun(a, b); end; toc 
Elapsed time is 2.690237 seconds. 

तो आप इसे अपने परीक्षणों में ध्यान में रखना चाहेंगे।

+0

अंदर कोड वास्तव में बहुत छोटा है - मूल रूप से, यह सिर्फ एक लेवेनशेटिन दूरी कैलक्यूलेटर फ़ंक्शन है। – sundar

+0

@ सुंदार मैं आपको बिल्कुल बता नहीं सकता कि आप क्या उम्मीद कर सकते हैं, क्योंकि मुझे आपके द्वारा सौदा किए गए कोड और समस्याओं को नहीं पता है। यदि आप बहुत अधिक समय निवेश नहीं करना चाहते हैं, तो आप एक छोटी मेक्स फ़ाइल क्यों नहीं लिखते हैं, जो दो संख्याओं को जोड़ने से ज्यादा कुछ नहीं करता है, या 'x + 1' देता है, और ओवरहेड को मापता है? जैसा कि मैंने कहा, यह स्थिर है, लेकिन स्पष्ट रूप से आपके हार्डवेयर/ओएस/matlab संस्करण पर निर्भर करता है। आप इसे अपने सी कोड के प्रदर्शन में जोड़ पाएंगे और देखेंगे कि MATLAB + MEX में क्या अपेक्षा की जानी चाहिए। – angainor

+0

मैंने ओवरहेड मापन के साथ सवाल अपडेट किया है, और अब लूप को आगे बढ़ने का फैसला किया है। मैं कल तक इंतजार करूंगा अगर अन्य उत्तरों आते हैं, और उसके बाद आपका जवाब स्वीकार करेंगे। – sundar

2

आपको बिना किसी हिचकिचाहट के मैक्स फ़ाइल के अंदर लूप को स्थानांतरित करना चाहिए। नीचे दिया गया उदाहरण लूप में वर्चुअल रिक्त कार्य इकाई के लिए 1000 गुना गति का प्रदर्शन करता है। स्पष्ट रूप से लूप के लिए काम की मात्रा में परिवर्तन के रूप में यह गति कम हो जाएगी।

मेक्स समारोह आंतरिक पाश के बिना:

#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[ ], int nrhs, const mxArray *prhs[ ]) 
{  
    int i=1;  
    plhs[0] = mxCreateDoubleScalar(i); 
} 

मैटलैब में कहा जाता है:

tic;for i=1:1000000;donothing();end;toc 
Elapsed time is 3.683634 seconds. 
आंतरिक पाश के साथ

मेक्स समारोह:

#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[ ], int nrhs, const mxArray *prhs[ ]) 
{  
    int M = mxGetScalar(prhs[0]); 
    plhs[0] = mxCreateNumericMatrix(M, 1, mxDOUBLE_CLASS, mxREAL); 
    double* mymat = mxGetPr(plhs[0]); 
    for (int i=0; i< M; i++) 
     mymat[i] = M-i; 
} 

यहाँ अंतर का एक उदाहरण है

मैटलैब में कहा जाता है:

tic; a = donothing(1000000); toc 
Elapsed time is 0.003350 seconds. 
+1

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

+0

बेशक यदि आप मैक्स फ़ाइल में अधिक काम करते हैं तो यह आपका अपवाद गायब हो सकता है लेकिन यह अप्रासंगिक है। प्रश्न मैक्स फ़ाइल ओवरहेड को संदर्भित किया गया। मैंने दिखाया कि यदि आप मैटलैब में लूप में एक मैक्स फ़ाइल 1e6 बार कॉल करते हैं, तो मैक्स फ़ाइल में एक लूप के अंदर समान कार्यक्षमता को कॉल करने के विरोध में आप मैक्स फ़ाइल कॉल ओवरहेड पीड़ित होंगे। मुझे यकीन नहीं है कि आप अन्यथा क्यों सोचेंगे। – twerdster

+0

मैं इसके साथ बहस नहीं कर रहा हूं। मैंने पूर्ण ओवरहेड अनुमान भी दिए हैं। ओपी ने स्पष्ट रूप से कहा कि वह मैक्स फ़ाइल में लूप को स्थानांतरित करने का फैसला करने से पहले वह यह सुनिश्चित करना चाहता है कि यह प्रयास के लायक है या नहीं। आपके अनुसार, क्या यह हमेशा इसके लायक है? क्योंकि मुझे लगता है कि यह 1 पर निर्भर करता है) लूप के 1 पुनरावृत्ति में वह काम करता है और 2) लूप संरचना को मेक्स फ़ंक्शन में स्थानांतरित करने के लिए आवश्यक प्रोग्रामिंग कार्य की मात्रा। यह सच नहीं है कि ऐसा करने के लिए केवल 2 और लाइनें होती हैं। आपके उदाहरण में 2 लाइनें लगती हैं। – angainor

2

खैर, यह मैं Matlab में यह कर सकते हैं सबसे तेज है:

%#eml 
function L = test(s,t) 

    m = numel(s); 
    n = numel(t); 

    % trivial cases 
    if m==0 && n==0 
     L = 0; return; end 
    if n==0 
     L = m; return; end 
    if m==0 
     L = n; return; end 

    % non-trivial cases 
    M = zeros(m+1,n+1);  
    M(:,1) = 0:m; 

    for j = 2:n+1 
     for i = 2:m+1 
      M(i,j) = min([ 
       M(i-1,j) + 1 
       M(i,j-1) + 1 
       M(i-1,j-1) + (s(i-1)~=t(j-1)); 
       ]); 
     end 
    end 

    L = min(M(end,:)); 

end 

आप इस संकलन और कुछ परीक्षण चला सकते हैं? (कुछ अजीब कारणों से, संकलन मेरी स्थापना पर काम करने में विफल रहता है ...) शायद %#eml से %#codegen को पहले बदलें, अगर आपको लगता है कि यह आसान है।

नोट: सी संस्करण के लिए, आपको फॉर-लूप का भी आदान-प्रदान करना चाहिए, ताकि j पर लूप आंतरिक हो।

इसके अलावा, row1 और row2 दृष्टिकोण बहुत अधिक मेमोरी कुशल है। यदि आप वैसे भी संकलन करने जा रहे हैं, तो मैं उस दृष्टिकोण का उपयोग करूंगा।

+0

यह मेरे कोड की लगभग एक सटीक प्रति है (सिवाय इसके कि हमें तुच्छ मामलों की आवश्यकता नहीं है)। % # कोडल के विपरीत% # eml क्या करता है? लूप इंटरचेंजिंग के बारे में अच्छा सुझाव, धन्यवाद (मैं अनुमान लगा रहा हूं क्योंकि सी पंक्ति-प्रमुख है, इंटरचेंजिंग इसे और अधिक कैश-अनुकूल बनाती है, क्या यह सही है?) पंक्ति 1 के लिए, पंक्ति 2 दृष्टिकोण, मेमोरी कुशल भी तेज़ी से इसका मतलब होगा? – sundar

+0

@sundar: '% # eml' मैटलैब को एम्बेडेड मैटलैब का उपयोग करने के लिए कहता है, जो 'एम' का एक सबसेट है जो मैटलैब सीधे मशीन कोड पर संकलित कर सकता है। '% # codegen' का उपयोग सी-कोड उत्पन्न करने के लिए किया जाता है (और मैंने कभी इसका उपयोग नहीं किया, क्योंकि यह R2010b में नहीं है :)। हां, सी पंक्ति प्रमुख है। यह अक्सर सी-परिवार और फोरट्रान-परिवार भाषाओं के बीच अनुवाद का एक बड़ा बाधा है। 'पंक्ति 1, पंक्ति 2' दृष्टिकोण: यह संभवतः केवल थोड़ा तेज़ होगा, यह केवल कम स्मृति का उपयोग करेगा, जो बड़े तारों की तुलना करते समय प्रासंगिक हो सकता है। इसके अलावा, सलाह का एक शब्द: *** हमेशा *** किसी भी मामूली मामलों को लागू करें !! वे ** ** जल्द या बाद में, इरादा या नहीं होगा। –

+0

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

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