अंतिम कार्य के लिए C लूप ऑप्टिमाइज़ेशन सहायता(संकलक अनुकूलन अक्षम के साथ)




loops optimization (2)

इसलिए मेरे कंप्यूटर सिस्टम वर्ग में मेरे अंतिम असाइनमेंट के लिए, हमें लूप्स को मूल की तुलना में तेज़ होने के लिए अनुकूलित करने की आवश्यकता है।

मूल ग्रेड 7 सेकंड से कम है और पूर्ण ग्रेड हमारे लिनक्स सर्वर के साथ 5 सेकंड से कम है। यह कोड जो मेरे पास है, वह लगभग 5.6 सेकंड में मिलता है। मैं सोच रहा हूं कि मुझे इसे तेजी से जाने के लिए किसी तरह से पॉइंटर्स का उपयोग करने की आवश्यकता हो सकती है लेकिन मुझे यकीन नहीं है। किसी को भी मेरे पास कोई सुझाव या विकल्प दे सकता है?

फ़ाइल 50 पंक्तियों या उससे कम की होनी चाहिए और मैं उन टिप्पणियों को अनदेखा कर रहा हूं जिनमें प्रशिक्षक शामिल है।

#include <stdio.h>
#include <stdlib.h>

// You are only allowed to make changes to this code as specified by the comments in it.

// The code you submit must have these two values.
#define N_TIMES     600000
#define ARRAY_SIZE   10000

int main(void)
{
    double  *array = calloc(ARRAY_SIZE, sizeof(double));
    double  sum = 0;
    int     i;

    // You can add variables between this comment ...
    register double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0, sum5 = 0, sum6 = 0, sum7 = 0, sum8 = 0, sum9 = 0;
    register int j;
    // ... and this one.

    printf("CS201 - Asgmt 4 - \n");

    for (i = 0; i < N_TIMES; i++)
    {
        // You can change anything between this comment ...
        for (j = 0; j < ARRAY_SIZE; j += 10)
        {
            sum += array[j];
            sum1 += array[j + 1];
            sum2 += array[j + 2];
            sum3 += array[j + 3];
            sum4 += array[j + 4];
            sum5 += array[j + 5];
            sum6 += array[j + 6];
            sum7 += array[j + 7];
            sum8 += array[j + 8];
            sum9 += array[j + 9];
        }
        // ... and this one. But your inner loop must do the same
        // number of additions as this one does.
    }                   

    // You can add some final code between this comment ...
    sum += sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7 + sum8 + sum9;
    // ... and this one.

    return 0;
}

आप सही रास्ते पर हो सकते हैं, हालांकि आपको इसे निश्चित करने के लिए मापने की आवश्यकता होगी (मेरी सामान्य सलाह को मापने के लिए, अनुमान नहीं लगता है कि यहां थोड़ा अतिरंजित है क्योंकि असाइनमेंट के पूरे बिंदु को मापना है)।

कंपाइलिंग ऑप्टिमाइज़र में बहुत अंतर नहीं दिखेगा क्योंकि वे उस तरह के सामान के बारे में बहुत चालाक हैं लेकिन, क्योंकि हम नहीं जानते कि यह किस ऑप्टिमाइज़ेशन स्तर पर होगा, जिससे आपको काफी सुधार मिल सकता है।

आंतरिक लूप में पॉइंटर्स का उपयोग करने के लिए पहले पॉइंटर वेरिएबल को जोड़ना एक साधारण बात है:

register double *pj;

फिर लूप को बदलकर:

for (pj = &(array[0]); pj < &(array[ARRAY_SIZE]); j++) {
        sum += *j++;
        sum1 += *j++;
        sum2 += *j++;
        sum3 += *j++;
        sum4 += *j++;
        sum5 += *j++;
        sum6 += *j++;
        sum7 += *j++;
        sum8 += *j++;
        sum9 += *j;
    }

यह लूप के भीतर परिवर्धन की मात्रा को समान रखता है (यह मानते हुए कि आप += और ++ को अतिरिक्त ऑपरेटर के रूप में गिन रहे हैं), लेकिन मूल रूप से सरणी इंडेक्स के बजाय पॉइंटर्स का उपयोग करता है।

मेरे सिस्टम पर कोई अनुकूलन 1 नहीं होने से, यह इसे 9.868 सेकंड (सीपीयू समय) से 4.84 सेकंड तक गिरा देता है। आपकी माइलेज भिन्न हो सकती है।

1 अनुकूलन स्तर -O3 , दोनों को 0.001 सेकंड लेने के रूप में सूचित किया जाता है, जैसा कि उल्लेख किया गया है, ऑप्टिमाइज़र बहुत चालाक हैं। हालाँकि, आपको 5+ सेकंड दिखाई दे रहे हैं, मेरा सुझाव है कि इसे अनुकूलन के साथ संकलित नहीं किया गया था।

एक तरफ के रूप में, यह एक अच्छा कारण है कि आमतौर पर आपके कोड को पढ़ने योग्य तरीके से लिखना उचित है और संकलक को इसे तेजी से चलाने में ध्यान रखना चाहिए। जबकि अनुकूलन पर मेरे अल्प प्रयास ने गति को दोगुना कर दिया, -O3 का उपयोग कर इसे कुछ दस हजार गुना तेज गति से चलाया :-)


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

आप हमेशा क्या करेंगे कई तरीकों की कोशिश करते हैं और जांचते हैं कि सबसे तेज़ क्या है। लक्ष्य के रूप में, प्रति चक्र एक चक्र को प्राप्त करने का प्रयास करें या बेहतर।

प्रति पाश पुनरावृत्तियों की संख्या: आप एक साथ 10 रकम जोड़ते हैं। यह हो सकता है कि आपके प्रोसेसर के पास उसके लिए पर्याप्त रजिस्टर नहीं है, या उसके पास अधिक है। मैं 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ... प्रति लूप के लिए समय मापता हूँ।

रकम की संख्या: एक से अधिक राशि होने का मतलब है कि विलंबता आपको काटती नहीं है, बस थ्रूपुट। लेकिन चार या छह से अधिक मददगार नहीं हो सकते हैं। 4 लूप की कोशिश करें, प्रति लूप 4, 8, 12, 16 पुनरावृत्तियों के साथ। या 6 रकम, 6, 12, 18 पुनरावृत्तियों के साथ।

कैशिंग: आप 80,000 बाइट्स के एक सरणी के माध्यम से चल रहे हैं। संभवतः L1 कैश से अधिक है। सरणी को 2 या 4 भागों में विभाजित करें। दो या चार उपरिश्रणों पर चलने वाला एक बाहरी लूप करें, अगला लूप 0 से N_TIMES - 1 तक, और इनर लूप मानों को जोड़ते हुए।

और फिर आप वेक्टर ऑपरेशन, या अपने कोड को मल्टी-थ्रेडिंग, या काम करने के लिए जीपीयू का उपयोग करके देख सकते हैं।

और यदि आप कोई अनुकूलन का उपयोग करने के लिए मजबूर हैं, तो "रजिस्टर" कीवर्ड वास्तव में काम कर सकता है।






debug-mode