c++ - १८१९२ - टाटा छपरा फर्रुखाबाद एक्सप्रेस




वास्तव में 8192 तत्वों पर लूपिंग करते समय मेरा प्रोग्राम धीमा क्यों है? (2)

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

इससे पहले लागत: 9 पढ़ा, 9 जोड़, 1 विभाजन के बाद लागत: 3 पढ़ा, 3 अतिरिक्त, 1 विभाजन

नमूना खिड़की के बारे में 3x3 बॉक्स के रूप में सोचें जहां आप अलग-अलग कॉलम (1x3) का ट्रैक रखते हैं। एक नया कॉलम जमा करें और सबसे पुराना छोड़ दें।

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

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

प्रश्न में कार्यक्रम से निकाला गया है। मैट्रिक्स img[][] आकार SIZE × SIZE है, और इसे प्रारंभ किया गया है:

img[j][i] = 2 * j + i

फिर, आप एक मैट्रिक्स res[][] बनाते हैं res[][] , और यहां पर प्रत्येक फ़ील्ड आईएमजी मैट्रिक्स में इसके आसपास के 9 फ़ील्ड का औसत होता है। सादगी के लिए सीमा 0 पर छोड़ दी गई है।

for(i=1;i<SIZE-1;i++) 
    for(j=1;j<SIZE-1;j++) {
        res[j][i]=0;
        for(k=-1;k<2;k++) 
            for(l=-1;l<2;l++) 
                res[j][i] += img[j+l][i+k];
        res[j][i] /= 9;
}

कार्यक्रम के लिए यह सब कुछ है। पूर्णता के लिए, यहां पहले क्या आता है। कोई कोड नहीं आता है। जैसा कि आप देख सकते हैं, यह सिर्फ प्रारंभिक है।

#define SIZE 8192
float img[SIZE][SIZE]; // input image
float res[SIZE][SIZE]; //result of mean filter
int i,j,k,l;
for(i=0;i<SIZE;i++) 
    for(j=0;j<SIZE;j++) 
        img[j][i] = (2*j+i)%8196;

असल में, यह प्रोग्राम धीमा होता है जब SIZE 2048 का एक बहु होता है, उदाहरण के लिए निष्पादन समय:

SIZE = 8191: 3.44 secs
SIZE = 8192: 7.20 secs
SIZE = 8193: 3.18 secs

संकलक जीसीसी है। जो मुझे पता है, यह स्मृति प्रबंधन की वजह से है, लेकिन मुझे वास्तव में उस विषय के बारे में बहुत कुछ पता नहीं है, इसलिए मैं यहां पूछ रहा हूं।

यह भी ठीक करने के लिए कैसे अच्छा होगा, लेकिन अगर कोई इन निष्पादन के समय को समझा सकता है तो मैं पहले से ही खुश रहूंगा।

मुझे पहले से ही मॉलोक / फ्री के बारे में पता है, लेकिन समस्या स्मृति की मात्रा नहीं है, यह केवल निष्पादन का समय है, इसलिए मुझे नहीं पता कि इससे कैसे मदद मिलेगी।


निम्नलिखित परीक्षण Visual C ++ कंपाइलर के साथ किए गए हैं क्योंकि इसका उपयोग डिफ़ॉल्ट क्यूटी निर्माता इंस्टॉल (मुझे कोई ऑप्टिमाइज़ेशन ध्वज के साथ लगता है) द्वारा उपयोग किया जाता है। जीसीसी का उपयोग करते समय, रहस्यवादी संस्करण और मेरे "अनुकूलित" कोड के बीच कोई बड़ा अंतर नहीं है। तो निष्कर्ष यह है कि कंपाइलर ऑप्टिमाइज़ेशन इंसानों की तुलना में बेहतर माइक्रो ऑप्टिमाइज़ेशन का ख्याल रखता है (मुझे आखिरी बार)। मैं संदर्भ के लिए मेरा बाकी जवाब छोड़ देता हूं।

इस तरह छवियों को संसाधित करना कुशल नहीं है। एकल आयाम सरणी का उपयोग करना बेहतर है। सभी पिक्सल को प्रोसेस करना एक लूप में किया जाता है। अंक के लिए यादृच्छिक पहुंच का उपयोग कर किया जा सकता है:

pointer + (x + y*width)*(sizeOfOnePixel)

इस विशेष मामले में, क्षैतिज रूप से तीन पिक्सल समूहों के योग की गणना और कैश करना बेहतर होता है क्योंकि इनका उपयोग तीन बार किया जाता है।

मैंने कुछ परीक्षण किए हैं और मुझे लगता है कि यह साझा करने लायक है। प्रत्येक परिणाम पांच परीक्षणों का औसत है।

उपयोगकर्ता 161520 9 द्वारा मूल कोड:

8193: 4392 ms
8192: 9570 ms

रहस्यवादी संस्करण:

8193: 2393 ms
8192: 2190 ms

एक 1 डी सरणी का उपयोग करके दो पास: पहले क्षैतिज रकम के लिए पास, लंबवत योग और औसत के लिए दूसरा। तीन पॉइंटर्स के साथ संबोधित दो पास और केवल इस तरह की वृद्धि:

imgPointer1 = &avg1[0][0];
imgPointer2 = &avg1[0][SIZE];
imgPointer3 = &avg1[0][SIZE+SIZE];

for(i=SIZE;i<totalSize-SIZE;i++){
    resPointer[i]=(*(imgPointer1++)+*(imgPointer2++)+*(imgPointer3++))/9;
}

8193: 938 ms
8192: 974 ms

एक 1 डी सरणी का उपयोग करके दो पास और इस तरह से संबोधित:

for(i=SIZE;i<totalSize-SIZE;i++){
    resPointer[i]=(hsumPointer[i-SIZE]+hsumPointer[i]+hsumPointer[i+SIZE])/9;
}

8193: 932 ms
8192: 925 ms

एक पास कैशिंग क्षैतिज रकम सिर्फ एक पंक्ति आगे है ताकि वे कैश में रहें:

// Horizontal sums for the first two lines
for(i=1;i<SIZE*2;i++){
    hsumPointer[i]=imgPointer[i-1]+imgPointer[i]+imgPointer[i+1];
}
// Rest of the computation
for(;i<totalSize;i++){
    // Compute horizontal sum for next line
    hsumPointer[i]=imgPointer[i-1]+imgPointer[i]+imgPointer[i+1];
    // Final result
    resPointer[i-SIZE]=(hsumPointer[i-SIZE-SIZE]+hsumPointer[i-SIZE]+hsumPointer[i])/9;
}

8193: 599 ms
8192: 652 ms

निष्कर्ष:

  • कई पॉइंटर्स और बस वृद्धि का उपयोग करने के कोई लाभ नहीं (मैंने सोचा कि यह तेजी से होगा)
  • क्षैतिज रकम कैशिंग उन्हें कई बार कंप्यूटिंग से बेहतर है।
  • दो पास तीन बार तेज नहीं है, केवल दो बार।
  • एक एकल पास और मध्यस्थ परिणाम कैशिंग दोनों का उपयोग करके 3.6 गुना तेजी से हासिल करना संभव है

मुझे यकीन है कि यह बेहतर करना संभव है।

नोट कृपया ध्यान दें कि मैंने मिस्टिकल के उत्कृष्ट उत्तर में कैश की समस्या के बजाय सामान्य प्रदर्शन समस्याओं को लक्षित करने के लिए यह उत्तर लिखा था। शुरुआत में यह सिर्फ छद्म कोड था। मुझे टिप्पणियों में परीक्षण करने के लिए कहा गया था ... यहां परीक्षणों के साथ एक पूरी तरह से प्रतिक्रिया प्राप्त संस्करण है।








gcc