2012-12-13 6 views
5

एक for -loop इस तरह बंटवारे में भूमि के ऊपर क्या है, कई for -loops इस तरह मेंबंटवारे एक में भूमि के ऊपर के लिए लूप के लिए-छोरों कई में क्या है, कुल काम के अंदर ही है तो क्या होगा?

int i; 

for (i = 0; i < exchanges; i++) 
{ 
    // some code 
    // some more code 
    // even more code 
} 

?

int i; 

for (i = 0; i < exchanges; i++) 
{ 
    // some code 
} 

for (i = 0; i < exchanges; i++) 
{ 
    // some more code 
} 

for (i = 0; i < exchanges; i++) 
{ 
    // even more code 
} 

कोड प्रदर्शन के प्रति संवेदनशील है, लेकिन बाद कर पठनीयता काफी में सुधार होगा। (यदि यह मायने रखता है, तो कोई अन्य लूप, परिवर्तनीय घोषणाएं, या फ़ंक्शन कॉल, कुछ लूप के भीतर कुछ एक्सेसर्स के लिए सहेजते हैं।)

मैं बिल्कुल निम्न स्तर के प्रोग्रामिंग गुरु नहीं हूं, इसलिए यह होगा और भी बेहतर हो सकता है अगर किसी को बुनियादी संचालन, जैसे की तुलना में प्रदर्शन हिट अप आकलन कर सकता है "प्रत्येक अतिरिक्त for -loop दो int आवंटन के बराबर का खर्च आएगा।" लेकिन, अगर मैं इतना आसान नहीं हूं, तो मैं समझता हूं (और आश्चर्यचकित नहीं होगा)।

बहुत धन्यवाद, अग्रिम में।

+0

जो मंच पर निर्भर करता है। यदि कैश शामिल है, तो गिरावट महत्वपूर्ण हो सकती है –

+0

भी देखें http://www.agner.org/optimize/ – queen3

+8

इसका एकमात्र उत्तर यह है: प्रोफ़ाइल। – moswald

उत्तर

12

अक्सर खेलने के कई कारक होते हैं ...,

उदाहरण के लिए (नीचे पूर्ण परीक्षण कोड) लगभग एक 2x धीमी गति से नीचे में निम्नलिखित पाश परिणाम बंटवारे:

for (int c = 0; c < size; c++){ 
    data[c] *= 10; 
    data[c] += 7; 
    data[c] &= 15; 
} 

और यह लगभग स्पष्ट करते हुए कहा है और यह दोनो तरीकों का प्रदर्शन करने में आसान है आप एक बार के बजाय 3 बार के माध्यम से लूप करने की जरूरत है और आप के बजाय 1.

दूसरी ओर पूरे सरणी पर 3 गुजरता बनाने के लिए, अगर आप इस सवाल पर एक नज़र के बाद से: Why are elementwise additions much faster in separate loops than in a combined loop?

for(int j=0;j<n;j++){ 
    a1[j] += b1[j]; 
    c1[j] += d1[j]; 
} 

स्मृति संरेखण के कारण कभी-कभी सच होता है।


इससे क्या लेना है?

बहुत कुछ भी हो सकता है। किसी भी तरह से हमेशा तेज नहीं होता है और यह लूप के अंदर क्या है इस पर निर्भर करता है।

और इस तरह, यह निर्धारित करना कि ऐसा अनुकूलन प्रदर्शन में वृद्धि करेगा या नहीं, आमतौर पर परीक्षण-और-त्रुटि होती है। पर्याप्त अनुभव के साथ आप काफी आत्मविश्वास (शिक्षित) अनुमान लगा सकते हैं। लेकिन सामान्य रूप से, कुछ भी उम्मीद है।

"प्रत्येक अतिरिक्त फॉर-लूप को दो int आवंटन के बराबर खर्च करना होगा।"

आप सही है कि यह इतना आसान नहीं है कर रहे हैं। असल में यह इतना जटिल है कि संख्याओं का अधिक मतलब नहीं है। एक लूप पुनरावृत्ति एक संदर्भ में एक्स चक्र ले सकती है, लेकिन Out-of-order Execution और डेटा निर्भरताओं जैसे कई कारकों के कारण वाई चक्र दूसरे में हो सकते हैं।

न केवल प्रदर्शन संदर्भ-निर्भर है, बल्कि यह विभिन्न प्रोसेसर के साथ भी भिन्न होता है।

#include <time.h> 
#include <iostream> 
using namespace std; 

int main(){ 

    int size = 10000; 
    int *data = new int[size]; 


    clock_t start = clock(); 

    for (int i = 0; i < 1000000; i++){ 
#ifdef TOGETHER 
     for (int c = 0; c < size; c++){ 
      data[c] *= 10; 
      data[c] += 7; 
      data[c] &= 15; 
     } 
#else 
     for (int c = 0; c < size; c++){ 
      data[c] *= 10; 
     } 
     for (int c = 0; c < size; c++){ 
      data[c] += 7; 
     } 
     for (int c = 0; c < size; c++){ 
      data[c] &= 15; 
     } 
#endif 
    } 

    clock_t end = clock(); 
    cout << (double)(end - start)/CLOCKS_PER_SEC << endl; 

    system("pause"); 
} 

आउटपुट (एक पाश): 4.08 सेकंड
आउटपुट (3 छोरों): 7.17 सेकंड

4

प्रोसेसर निर्देशों को कूदने के लिए डेटा निर्देशों का उच्च अनुपात होना पसंद करते हैं।
शाखा निर्देश निर्देशक पाइपलाइन और पुनः लोड करने के लिए अपने प्रोसेसर को मजबूर कर सकते हैं।

निर्देश पाइपलाइन के पुनः लोड करने के आधार पर, पहली विधि तेज होगी, लेकिन महत्वपूर्ण नहीं है। आप विभाजित करके कम से कम 2 नए शाखा निर्देश जोड़ देंगे।

लूप को अनलॉक करना एक तेज़ अनुकूलन है। पाश unrolling पाश के शीर्ष करने के शाखाओं में से पहले लूप के अंदर और निर्देशों के प्रदर्शन से शाखा निर्देश के लिए डेटा निर्देश के अनुपात में सुधार के लिए प्रयास करता है।

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

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

+0

यह सबसे सही उत्तर है। –

+0

1. कूद निर्देशों की संख्या घटाएं। अनलोल करें, कम से कम संभव लूप की संख्या है। 2. कैश निर्देश को संरेखित नहीं करते हैं, तो कैश आपके डेटा को संरेखित करें। 3. एल 1 कैश में डेटा लाने के लिए उपलब्ध होने पर प्रीफेच निर्देशों का उपयोग करें, इसलिए कम कैश मिस है। –

-3

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

+1

-1 यह आसान नहीं है। कई संदर्भों में, लूप ओवरहेड छोटा है। दूसरों में, यह बड़ा है। – Yakk

+0

मैं आपसे सहमत हूं। यह एकल लूप लेने वाले रनों की संख्या पर निर्भर करता है। – liquidcoder

2

यह आपको एक दे देंगे


यहाँ परीक्षण कोड है एक संस्करण दूसरे संस्करण की तुलना में तेज़ है या नहीं, इसका अच्छा संकेत।

#include <array> 
#include <chrono> 
#include <iostream> 
#include <numeric> 
#include <string> 

const int iterations = 100; 

namespace 
{ 
    const int exchanges = 200; 

    template<typename TTest> 
    void Test(const std::string &name, TTest &&test) 
    { 
     typedef std::chrono::high_resolution_clock Clock; 
     typedef std::chrono::duration<float, std::milli> ms; 

     std::array<float, iterations> timings; 

     for (auto i = 0; i != iterations; ++i) 
     { 
      auto t0 = Clock::now(); 

      test(); 

      timings[i] = ms(Clock::now() - t0).count(); 
     } 

     auto avg = std::accumulate(timings.begin(), timings.end(), 0)/iterations; 
     std::cout << "Average time, " << name << ": " << avg << std::endl; 
    } 
} 

int main() 
{ 
    Test("single loop", 
     []() 
     { 
      for (auto i = 0; i < exchanges; ++i) 
      { 
       // some code 
       // some more code 
       // even more code 
      } 
     }); 

    Test("separated loops", 
     []() 
     { 
      for (auto i = 0; i < exchanges; ++i) 
      { 
       // some code 
      } 

      for (auto i = 0; i < exchanges; ++i) 
      { 
       // some more code 
      } 

      for (auto i = 0; i < exchanges; ++i) 
      { 
       // even more code 
      } 
     }); 
} 
संबंधित मुद्दे