সংযুক্ত লুপের তুলনায় পৃথক লুপগুলিতে কেন এলিমিটাইজ সংযোজনগুলি বেশি দ্রুত হয়?


2244

ধরুন a1, b1, c1, এবংd1 গাদা মেমরি এবং আমার সংখ্যাসূচক কোডে বিন্দু নিম্নলিখিত কোর লুপ আছে।

const int n = 100000;

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

এই লুপটি অন্য বাইরের forলুপের মাধ্যমে 10,000 বার কার্যকর করা হয় । এটির গতি বাড়ানোর জন্য, আমি কোডটি এতে পরিবর্তন করেছি:

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

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

সম্পূর্ণ অপ্টিমাইজেশনের সাথে এমএস ভিজ্যুয়াল সি ++ 10.0 তে সংকলিত এবং এসইএস 2 এ 32-বিটের জন্য সক্ষম হয়েছে 2 ইন্টেল কোর 2 ডুও (x64) , প্রথম উদাহরণটি 5.5 সেকেন্ড নেয় এবং ডাবল-লুপ উদাহরণটি কেবল 1.9 সেকেন্ড নেয়। আমার প্রশ্নটি: (দয়া করে নীচে আমার পুনঃবিবেচিত প্রশ্নটি দেখুন)

পিএস: আমি নিশ্চিত নই, যদি এটি সাহায্য করে:

প্রথম লুপটির জন্য নির্গমনটি মূলত এর মতো দেখায় (সম্পূর্ণ ব্লকটিতে এই ব্লকটি প্রায় পাঁচবার পুনরাবৃত্তি হয়):

movsd       xmm0,mmword ptr [edx+18h]
addsd       xmm0,mmword ptr [ecx+20h]
movsd       mmword ptr [ecx+20h],xmm0
movsd       xmm0,mmword ptr [esi+10h]
addsd       xmm0,mmword ptr [eax+30h]
movsd       mmword ptr [eax+30h],xmm0
movsd       xmm0,mmword ptr [edx+20h]
addsd       xmm0,mmword ptr [ecx+28h]
movsd       mmword ptr [ecx+28h],xmm0
movsd       xmm0,mmword ptr [esi+18h]
addsd       xmm0,mmword ptr [eax+38h]

ডাবল লুপ উদাহরণের প্রতিটি লুপ এই কোডটি তৈরি করে (নীচের ব্লকটি প্রায় তিনবার পুনরাবৃত্তি করা হয়):

addsd       xmm0,mmword ptr [eax+28h]
movsd       mmword ptr [eax+28h],xmm0
movsd       xmm0,mmword ptr [ecx+20h]
addsd       xmm0,mmword ptr [eax+30h]
movsd       mmword ptr [eax+30h],xmm0
movsd       xmm0,mmword ptr [ecx+28h]
addsd       xmm0,mmword ptr [eax+38h]
movsd       mmword ptr [eax+38h],xmm0
movsd       xmm0,mmword ptr [ecx+30h]
addsd       xmm0,mmword ptr [eax+40h]
movsd       mmword ptr [eax+40h],xmm0

প্রশ্নটির কোনও সম্পর্ক নেই, কারণ আচরণটি মারাত্মকভাবে অ্যারে (এন) এবং সিপিইউ ক্যাশের আকারের উপর নির্ভর করে। সুতরাং যদি আরও আগ্রহ থাকে তবে আমি এই প্রশ্নটি পুনরায় বলি:

নিম্নলিখিত গ্রাফের পাঁচটি অঞ্চলের দ্বারা বর্ণিত বিভিন্ন ক্যাশে আচরণের দিকে পরিচালিত করে এমন বিশদ সম্পর্কে আপনি কি কিছু দৃ ?়দৃষ্টি দিতে পারেন?

এই সিপিইউগুলির জন্য অনুরূপ গ্রাফ সরবরাহ করে সিপিইউ / ক্যাশে আর্কিটেকচারের মধ্যে পার্থক্যগুলি চিহ্নিত করাও আকর্ষণীয় হতে পারে।

পিপিএস: পুরো কোডটি এখানে। এটি উচ্চতর রেজোলিউশন সময়ের জন্য টিবিবি Tick_Count ব্যবহার করে, যা TBB_TIMINGম্যাক্রো সংজ্ঞায়িত না করে অক্ষম করা যেতে পারে :

#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>

//#define TBB_TIMING

#ifdef TBB_TIMING   
#include <tbb/tick_count.h>
using tbb::tick_count;
#else
#include <time.h>
#endif

using namespace std;

//#define preallocate_memory new_cont

enum { new_cont, new_sep };

double *a1, *b1, *c1, *d1;


void allo(int cont, int n)
{
    switch(cont) {
      case new_cont:
        a1 = new double[n*4];
        b1 = a1 + n;
        c1 = b1 + n;
        d1 = c1 + n;
        break;
      case new_sep:
        a1 = new double[n];
        b1 = new double[n];
        c1 = new double[n];
        d1 = new double[n];
        break;
    }

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

void ff(int cont)
{
    switch(cont){
      case new_sep:
        delete[] b1;
        delete[] c1;
        delete[] d1;
      case new_cont:
        delete[] a1;
    }
}

double plain(int n, int m, int cont, int loops)
{
#ifndef preallocate_memory
    allo(cont,n);
#endif

#ifdef TBB_TIMING   
    tick_count t0 = tick_count::now();
#else
    clock_t start = clock();
#endif

    if (loops == 1) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++){
                a1[j] += b1[j];
                c1[j] += d1[j];
            }
        }
    } else {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                a1[j] += b1[j];
            }
            for (int j = 0; j < n; j++) {
                c1[j] += d1[j];
            }
        }
    }
    double ret;

#ifdef TBB_TIMING   
    tick_count t1 = tick_count::now();
    ret = 2.0*double(n)*double(m)/(t1-t0).seconds();
#else
    clock_t end = clock();
    ret = 2.0*double(n)*double(m)/(double)(end - start) *double(CLOCKS_PER_SEC);
#endif

#ifndef preallocate_memory
    ff(cont);
#endif

    return ret;
}


void main()
{   
    freopen("C:\\test.csv", "w", stdout);

    char *s = " ";

    string na[2] ={"new_cont", "new_sep"};

    cout << "n";

    for (int j = 0; j < 2; j++)
        for (int i = 1; i <= 2; i++)
#ifdef preallocate_memory
            cout << s << i << "_loops_" << na[preallocate_memory];
#else
            cout << s << i << "_loops_" << na[j];
#endif

    cout << endl;

    long long nmax = 1000000;

#ifdef preallocate_memory
    allo(preallocate_memory, nmax);
#endif

    for (long long n = 1L; n < nmax; n = max(n+1, long long(n*1.2)))
    {
        const long long m = 10000000/n;
        cout << n;

        for (int j = 0; j < 2; j++)
            for (int i = 1; i <= 2; i++)
                cout << s << plain(n, m, j, i);
        cout << endl;
    }
}

(এটি বিভিন্ন মানের জন্য FLOP / গুলি দেখায় n))

এখানে চিত্র বর্ণনা লিখুন


4
অপারেটিং সিস্টেম হতে পারে যা শারীরিক মেমরির প্রতিবার অ্যাক্সেস করার সময় ধীর হয়ে যায় এবং একই ঝিল্লিতে সেকেন্ডারি অ্যাক্সেসের ক্ষেত্রে ক্যাশের মতো কিছু রয়েছে।
অ্যালেক্স থিও

7
আপনি কি অপটিমাইজেশন দিয়ে সংকলন করছেন? এটি ও 2 এর জন্য অনেকগুলি এসএম কোডের মতো দেখাচ্ছে ...
লুচিয়ান গ্রিগোর

1
আমি জিজ্ঞাসা করেছি কিছু সময় আগে কি একই ধরণের প্রশ্ন বলে মনে হচ্ছে । এটি বা উত্তরগুলির আগ্রহের তথ্য থাকতে পারে।
মার্ক উইলকিনস

61
কেবল বাছাই করার জন্য, এই দুটি কোড স্নিপেটগুলি সম্ভাব্য ওভারল্যাপিং পয়েন্টারগুলির কারণে সমতুল্য নয়। C99 এ restrictজাতীয় পরিস্থিতিতে মূল শব্দটি রয়েছে । আমি জানি না এমএসভিসির অনুরূপ কিছু আছে কিনা। অবশ্যই এটি যদি সমস্যা হয় তবে এসএসই কোডটি সঠিক হবে না।
ব্যবহারকারীর 10306

8
এটির স্মৃতি অ্যালিজিংয়ের সাথে কিছু থাকতে পারে। একটি লুপের d1[j]সাহায্যে এলিয়াস হতে পারে a1[j], তাই সংকলক কিছু মেমরি অপ্টিমাইজেশানগুলি করতে পারে না। যদিও আপনি যদি দুটি লুপগুলিতে মেমরিগুলিতে লেখাগুলি আলাদা করেন তবে তা ঘটে না।
rturrado

উত্তর:


1690

এর আরও বিশ্লেষণের পরে, আমি বিশ্বাস করি যে এটি (অন্তত আংশিক) চার-পয়েন্টারের ডেটা সারিবদ্ধকরণের কারণে ঘটে। এটি কিছু স্তরের ক্যাশে ব্যাংক / উপায় দ্বন্দ্ব সৃষ্টি করবে।

আপনি কীভাবে আপনার অ্যারেগুলি বরাদ্দ করছেন সে সম্পর্কে আমি যদি সঠিকভাবে অনুমান করে থাকি তবে সেগুলি পৃষ্ঠার লাইনে একত্রিত হওয়ার সম্ভাবনা রয়েছে

এর অর্থ হ'ল প্রতিটি লুপের সমস্ত অ্যাক্সেস একই ক্যাশে পড়বে। তবে, ইন্টেল প্রসেসরের কিছু সময়ের জন্য 8-ওয়ে এল 1 ক্যাশে এসোসিয়েটিভিটি রয়েছে। কিন্তু বাস্তবে, অভিনয়টি সম্পূর্ণ অভিন্ন নয়। দ্বি-উপায় বলার চেয়ে 4-উপায় অ্যাক্সেস করা এখনও ধীর।

সম্পাদনা: এটি বাস্তবের মতো দেখে মনে হচ্ছে আপনি সমস্ত অ্যারে আলাদাভাবে বরাদ্দ করছেন। সাধারণত যখন এত বড় বরাদ্দ অনুরোধ করা হয়, বরাদ্দকারী ওএস থেকে নতুন পৃষ্ঠাগুলির জন্য অনুরোধ করবে will অতএব, উচ্চতর সম্ভাবনা রয়েছে যে পৃষ্ঠার সীমানা থেকে একই অফসেটে বড় বরাদ্দ উপস্থিত হবে।

পরীক্ষার কোডটি এখানে:

int main(){
    const int n = 100000;

#ifdef ALLOCATE_SEPERATE
    double *a1 = (double*)malloc(n * sizeof(double));
    double *b1 = (double*)malloc(n * sizeof(double));
    double *c1 = (double*)malloc(n * sizeof(double));
    double *d1 = (double*)malloc(n * sizeof(double));
#else
    double *a1 = (double*)malloc(n * sizeof(double) * 4);
    double *b1 = a1 + n;
    double *c1 = b1 + n;
    double *d1 = c1 + n;
#endif

    //  Zero the data to prevent any chance of denormals.
    memset(a1,0,n * sizeof(double));
    memset(b1,0,n * sizeof(double));
    memset(c1,0,n * sizeof(double));
    memset(d1,0,n * sizeof(double));

    //  Print the addresses
    cout << a1 << endl;
    cout << b1 << endl;
    cout << c1 << endl;
    cout << d1 << endl;

    clock_t start = clock();

    int c = 0;
    while (c++ < 10000){

#if ONE_LOOP
        for(int j=0;j<n;j++){
            a1[j] += b1[j];
            c1[j] += d1[j];
        }
#else
        for(int j=0;j<n;j++){
            a1[j] += b1[j];
        }
        for(int j=0;j<n;j++){
            c1[j] += d1[j];
        }
#endif

    }

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

    system("pause");
    return 0;
}

বেঞ্চমার্ক ফলাফল:

সম্পাদনা: প্রকৃত কোর 2 আর্কিটেকচার মেশিনের ফলাফল :

2 এক্স ইন্টেল শিওন এক্স 5482 হার্পারটাউন @ 3.2 গিগাহার্টজ:

#define ALLOCATE_SEPERATE
#define ONE_LOOP
00600020
006D0020
007A0020
00870020
seconds = 6.206

#define ALLOCATE_SEPERATE
//#define ONE_LOOP
005E0020
006B0020
00780020
00850020
seconds = 2.116

//#define ALLOCATE_SEPERATE
#define ONE_LOOP
00570020
00633520
006F6A20
007B9F20
seconds = 1.894

//#define ALLOCATE_SEPERATE
//#define ONE_LOOP
008C0020
00983520
00A46A20
00B09F20
seconds = 1.993

পর্যবেক্ষণ:

  • 6,206 সেকেন্ড এক লুপ সঙ্গে এবং 2,116 সেকেন্ড দুটি loops সঙ্গে। এটি ওপির ফলাফলগুলি হুবহু পুনরুত্পাদন করে।

  • প্রথম দুটি পরীক্ষায় অ্যারেগুলি আলাদাভাবে বরাদ্দ করা হয়। আপনি লক্ষ্য করবেন যে পৃষ্ঠাগুলির তুলনায় তাদের সবার সমান প্রান্তিককরণ রয়েছে।

  • দ্বিতীয় দুটি পরীক্ষায় অ্যারেগুলি একসাথে প্যাক করা হয় যাতে সেই প্রান্তিককরণটি ভেঙে যায়। এখানে আপনি উভয় লুপগুলি দ্রুত দেখবেন। তদতিরিক্ত, দ্বিতীয় (ডাবল) লুপটি এখন আপনার ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে পড়ে যাবেন।

@ স্টেফেন ক্যানন মন্তব্যগুলিতে যেমন উল্লেখ করেছেন, খুব সম্ভবত সম্ভাবনা রয়েছে যে এই প্রান্তিককরণের ফলে লোড / স্টোর ইউনিট বা ক্যাশে মিথ্যা আলিয়াসিং ঘটায় । আমি এটির জন্য প্রায় গুগল করে দেখতে পেলাম যে আংশিক ঠিকানা আলিয়াসিংয়ের জন্য ইন্টেলের আসলে একটি হার্ডওয়্যার কাউন্টার রয়েছে স্টলের :

http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/~amplifierxe/pmw_dp/events/partial_address_alias.html


5 অঞ্চল - ব্যাখ্যা

অঞ্চল 1:

এই এক সহজ। ডেটাসেট এত ছোট যে কর্মক্ষমতা লুপিং এবং ব্রাঞ্চিংয়ের মতো ওভারহেডের দ্বারা প্রাধান্য পায়।

অঞ্চল 2:

এখানে, ডেটার আকার বাড়ার সাথে সাথে আপেক্ষিক ওভারহেডের পরিমাণ নেমে যায় এবং পারফরম্যান্স "স্যাচুরেট" হয়। এখানে দুটি লুপগুলি ধীর হয় কারণ এতে দ্বিগুণ লুপ এবং ব্রাঞ্চিং ওভারহেড রয়েছে।

আমি ঠিক জানি না এখানে ঠিক কী চলছে ... অ্যাগনার ফাগ ক্যাশে ব্যাংক বিরোধের উল্লেখ করায় অ্যালাইনমেন্ট এখনও কার্যকর হতে পারে । (এই লিঙ্কটি স্যান্ডি ব্রিজ সম্পর্কিত, তবে ধারণাটি এখনও কোর 2 এর জন্য প্রযোজ্য হওয়া উচিত)

অঞ্চল 3:

এই মুহুর্তে, ডেটা আর L1 ক্যাশে ফিট করে না। সুতরাং পারফরম্যান্সটি L1 <-> এল 2 ক্যাশে ব্যান্ডউইদথ দ্বারা ক্যাপড।

অঞ্চল 4:

একক লুপের পারফরম্যান্সের ড্রপটি আমরা পর্যবেক্ষণ করছি। এবং যেমনটি উল্লেখ করা হয়েছে, এটি প্রান্তিককরণের কারণে যা (সম্ভবত সম্ভবত) প্রসেসরের লোড / স্টোর ইউনিটগুলিতে ভুয়া আলিয়াসিং স্টল তৈরি করে।

যাইহোক, মিথ্যা আলিয়াজিং ঘটতে হলে ডেটাসেটগুলির মধ্যে একটি বৃহত পর্যায়ে পদক্ষেপ থাকতে হবে। এই কারণেই আপনি অঞ্চল 3 এ এটি দেখছেন না।

অঞ্চল 5:

এই সময়ে, কিছুই ক্যাশে ফিট করে না। সুতরাং আপনি মেমরি ব্যান্ডউইথ দ্বারা আবদ্ধ হন।


2 এক্স ইন্টেল এক্স 5482 হার্পারটাউন @ 3.2 গিগাহার্টজ ইন্টেল কোর আই 7 870 @ 2.8 গিগাহার্টজ ইন্টেল কোর i7 2600K @ 4.4 গিগাহার্টজ


162
+1: আমি মনে করি এটিই এর উত্তর। অন্যান্য সমস্ত উত্তর যা বলেছে তার বিপরীতে, এটি একক লুপ বৈকল্পিকের সাথে অন্তর্নিহিতভাবে আরও বেশি ক্যাশে মিস করা সম্পর্কে নয়, এটি ক্যাশে মিস করার কারণে অ্যারের নির্দিষ্ট প্রান্তিককরণ সম্পর্কে।
অলিভার চার্লসওয়ার্থ

30
এই; একটি মিথ্যা আলিয়াজিং স্টল সবচেয়ে সম্ভবত ব্যাখ্যা।
স্টিফেন ক্যানন

7
@VictorT। আমি ওপি সংযুক্ত কোডটি ব্যবহার করেছি used এটি একটি .css ফাইল উত্পন্ন করে যা আমি এক্সেলে খুলতে পারি এবং এ থেকে গ্রাফ তৈরি করতে পারি।
রহস্যময়

5
@ নাওয়াজ একটি পৃষ্ঠা সাধারণত 4KB। আমি যে মুদ্রণটি ছড়িয়ে দিয়েছি তা যদি আপনি হেক্সাডেসিমাল ঠিকানার দিকে লক্ষ্য করেন তবে পৃথকভাবে বরাদ্দ হওয়া পরীক্ষাগুলিতে সব একই ধরণের 4096 থাকে that এটি ব্যাখ্যা করতে পারে যে আপনি কেন পার্থক্য দেখছেন না।
রহস্যময়


224

ঠিক আছে, সঠিক উত্তরটি অবশ্যই অবশ্যই সিপিইউ ক্যাশে দিয়ে কিছু করতে হবে। তবে ক্যাশে যুক্তি ব্যবহার করা বেশ কঠিন হতে পারে, বিশেষত ডেটা ছাড়াই।

অনেক উত্তর রয়েছে, যা প্রচুর আলোচনার দিকে পরিচালিত করেছিল, তবে আসুন এটির মুখোমুখি হোন: ক্যাশে সমস্যাগুলি খুব জটিল হতে পারে এবং এটি একটি মাত্রিক নয়। তারা ডেটার আকারের উপর খুব বেশি নির্ভর করে, সুতরাং আমার প্রশ্নটি ছিল অন্যায্য: এটি ক্যাশে গ্রাফের খুব আকর্ষণীয় পয়েন্টে পরিণত হয়েছিল।

@ মিস্টিয়ালের উত্তরটি অনেক লোককে (আমাকে সহ) নিশ্চিত করেছে, সম্ভবত কারণ এটি ছিল যে একমাত্র সত্যের উপর নির্ভর করে বলে মনে হয়েছিল, তবে এটি ছিল সত্যের একটি "ডেটা পয়েন্ট"।

এজন্যই আমি তাঁর পরীক্ষাটি (অবিচ্ছিন্ন বনাম পৃথক বরাদ্দ ব্যবহার করে) এবং @ জেমসের উত্তরের পরামর্শকে একত্রিত করেছি।

নীচের গ্রাফগুলি দেখায় যে, বেশিরভাগ উত্তর এবং বিশেষত প্রশ্ন এবং উত্তরগুলির বেশিরভাগ মন্তব্যে ব্যবহৃত সঠিক পরিস্থিতি এবং পরামিতিগুলির উপর নির্ভর করে সম্পূর্ণ ভুল বা সত্য হিসাবে বিবেচনা করা যেতে পারে।

মনে রাখবেন যে আমার প্রাথমিক প্রশ্নটি n = 100.000 এ ছিল । এই বিন্দুটি (দুর্ঘটনাক্রমে) বিশেষ আচরণ প্রদর্শন করে:

  1. এটি এক এবং দুটি লুপ'ড সংস্করণ (প্রায় তিনটির একটি ফ্যাক্টর) এর মধ্যে সবচেয়ে বড় তাত্পর্য ধারণ করে

  2. এটিই একমাত্র পয়েন্ট, যেখানে এক-লুপ (ক্রমাগত বরাদ্দ সহ) দুটি-লুপ সংস্করণকে পরাজিত করে। (এটি মিস্টিয়ালের উত্তরটি একেবারেই সম্ভব করে তুলেছে))

প্রারম্ভিক ডেটা ব্যবহার করে ফলাফল:

এখানে চিত্র বিবরণ লিখুন

অনির্দেশিত ডেটা ব্যবহার করে ফলাফল (এটি মাইস্টিয়াল পরীক্ষিত):

এখানে চিত্র বিবরণ লিখুন

এবং এটি কঠোরভাবে ব্যাখ্যাযোগ্য: সূচনাযুক্ত ডেটা, যা একবার বরাদ্দ করা হয় এবং বিভিন্ন ভেক্টর আকারের প্রতিটি নিম্নলিখিত পরীক্ষার ক্ষেত্রে পুনরায় ব্যবহার করা হয়:

এখানে চিত্র বিবরণ লিখুন

প্রস্তাব

স্ট্যাক ওভারফ্লোতে প্রতিটি নিম্ন-স্তরের কার্য সম্পাদন সম্পর্কিত প্রশ্নের ক্যাশে প্রাসঙ্গিক ডেটা মাপের পুরো পরিসরের জন্য MFLOPS তথ্য সরবরাহ করা প্রয়োজন! এই তথ্যগুলি ছাড়াই উত্তরগুলি চিন্তা করা এবং বিশেষত অন্যদের সাথে সেগুলি নিয়ে আলোচনা করা প্রত্যেকের সময় নষ্ট।


18
+1 সুন্দর বিশ্লেষণ। আমি ডেটাটি প্রথম জায়গায় অবিচ্ছিন্নভাবে ছেড়ে যাওয়ার ইচ্ছা করি নি। এটি ঠিক ঘটেছে যে বরাদ্দকারীরা তাদের যাইহোক শূন্য করেছে। সুতরাং আরম্ভ করা ডেটা কি গুরুত্বপূর্ণ। আমি কেবলমাত্র উত্তরটি একটি আসল কোর 2 আর্কিটেকচার মেশিনের ফলাফলের সাথে সম্পাদনা করেছি এবং তারা আপনি যা পর্যবেক্ষণ করছেন তার থেকে অনেক বেশি কাছাকাছি। আরেকটি বিষয় হ'ল আমি বিভিন্ন আকারের পরীক্ষা করেছি nএবং এটি n = 80000, n = 100000, n = 200000ইত্যাদির জন্য একই পারফরম্যান্স ফাঁক দেখায় ...
রহস্যময়

2
@ মিস্টিয়াল আমার মনে হয় যে কোনও আন্তঃ প্রক্রিয়া গুপ্তচরবৃত্তি এড়াতে কোনও প্রক্রিয়াতে নতুন পৃষ্ঠা দেওয়ার সময় ওএস পৃষ্ঠা শূন্যকরণ কার্যকর করে।
v.oddou

1
@ v.oddou: আচরণ ওএসের উপরও নির্ভর করে; আইআইআরসি, উইন্ডোজটির ব্যাকগ্রাউন্ডে জিরো-আউট মুক্ত পৃষ্ঠাগুলির একটি থ্রেড রয়েছে এবং যদি একটি অনুরোধ ইতিমধ্যে শূন্য পৃষ্ঠাগুলি থেকে সন্তুষ্ট না করা যায়, VirtualAllocতবে অনুরোধটি সন্তুষ্ট করতে পর্যাপ্ত শূন্য না হওয়া পর্যন্ত কল ব্লকগুলি। বিপরীতে, লিনাক্স শূন্য পৃষ্ঠাটি কেবলমাত্র অনুলিপি হিসাবে অনুলিপি হিসাবে প্রয়োজনীয় হিসাবে ম্যাপ করে, এবং লেখার উপর, এটি নতুন তথ্যগুলিতে লেখার আগে নতুন শূন্যগুলি একটি নতুন পৃষ্ঠায় অনুলিপি করে। যেভাবেই, ব্যবহারকারীর মোড প্রক্রিয়াটির দৃষ্টিকোণ থেকে, পৃষ্ঠাগুলি শূন্য করা হয়, তবে অবিচ্ছিন্ন মেমরির প্রথম ব্যবহারটি সাধারণত উইন্ডোজের চেয়ে লিনাক্সে আরও ব্যয়বহুল হবে।
শ্যাডোর্যাঞ্জার

81

দ্বিতীয় লুপটিতে অনেক কম ক্যাশে ক্রিয়াকলাপ জড়িত তাই প্রসেসরের পক্ষে মেমরির চাহিদা পূরণ করা আরও সহজ।


1
আপনি বলছেন যে দ্বিতীয় রূপটি কম ক্যাশে মিস করে? কেন?
অলিভার চার্লসওয়ার্থ

2
@Oli: প্রথম বৈকল্পিক ইন, প্রসেসর এক্সেস চার মেমরির লাইন করার জন্য একটি time- প্রয়োজন a[i], b[i], c[i]এবং d[i]দ্বিতীয় বৈকল্পিক, এটা ঠিক দুই দরকার। এটি যুক্ত করার সাথে সাথে সেই লাইনগুলিকে আবার পূরণ করতে এটি আরও বেশি কার্যকর করে তোলে।
কুকুরছানা

4
তবে যতক্ষণ না অ্যারে ক্যাশে সংঘর্ষ হয় না, ততক্ষণ প্রতিটি ভেরিয়েন্টের / থেকে মূল স্মৃতিতে / পাঠানোর জন্য একই সংখ্যার পাঠ্য এবং লেখার প্রয়োজন হয়। সুতরাং উপসংহারটি (আমার মনে হয়) এই দুটি অ্যারেটি সারাক্ষণ সংঘর্ষে ঘটে।
অলিভার চার্লসওয়ার্থ

3
আমি অনুসরণ করি না প্রতি নির্দেশ অনুসারে (উদাহরণস্বরূপ প্রতি উদাহরণ x += y), সেখানে দুটি পঠিত এবং একটি লেখা রয়েছে। এটি উভয় বৈকল্পিকের জন্য সত্য। ক্যাশে <-> সিপিইউ ব্যান্ডউইথ প্রয়োজন তাই একই। যতক্ষণ কোনও বিরোধ নেই, ক্যাশে <-> র্যাম ব্যান্ডউইথের প্রয়োজনীয়তাও একই the
অলিভার চার্লসওয়ার্থ

2
স্ট্যাকওভারফ্লো . com / a / 1742231 / 102916 এ উল্লিখিত হিসাবে , পেন্টিয়াম এম এর হার্ডওয়ার প্রিফেচ 12 টি বিভিন্ন ফরোয়ার্ড স্ট্রিমগুলি ট্র্যাক করতে পারে (এবং আমি আশা করব যে পরে হার্ডওয়্যার কমপক্ষে সক্ষম হবে)। লুপ 2 এখনও কেবলমাত্র চারটি স্ট্রিম পড়ছে, তাই সেই সীমাতেও ভাল।
ব্রুকস মূসা 21

50

কল্পনা করুন আপনি এমন একটি মেশিনে কাজ করছেন যেখানে nকেবলমাত্র একবারে আপনার দুটি অ্যারে ধরে রাখা সম্ভব হত, তবে ডিস্ক ক্যাশে হয়ে মোট মোট মেমরি এখনও চারটি ধরে রাখতে যথেষ্ট ছিল।

একটি সাধারণ LIFO ক্যাশে নীতি অনুমান করে, এই কোড:

for(int j=0;j<n;j++){
    a[j] += b[j];
}
for(int j=0;j<n;j++){
    c[j] += d[j];
}

প্রথমে হয়ত লাগত কারণ aএবং bর্যাম মধ্যে লোড এবং তারপর করা RAM- র মধ্যে সম্পূর্ণরূপে কাজ হবে না। যখন দ্বিতীয় লুপটি শুরু হয় cএবংd তারপরে ডিস্ক থেকে র‍্যামে লোড হবে এবং চালিত হবে।

অন্য লুপ

for(int j=0;j<n;j++){
    a[j] += b[j];
    c[j] += d[j];
}

লুপের চারপাশে প্রতিবার দু'টি অ্যারে এবং অন্য দুটিতে পৃষ্ঠা প্রদর্শন করবে । এটি অবশ্যই অনেক ধীর হবে।

আপনি সম্ভবত আপনার পরীক্ষাগুলিতে ডিস্ক ক্যাচিং দেখছেন না তবে আপনি সম্ভবত ক্যাচিংয়ের অন্য কোনও রূপের পার্শ্ব প্রতিক্রিয়া দেখছেন।


এখানে কিছুটা বিভ্রান্তি / ভুল বুঝাবুঝি হচ্ছে বলে মনে হচ্ছে তাই আমি উদাহরণ ব্যবহার করে কিছুটা বিশদ দেওয়ার চেষ্টা করব।

বলুন n = 2এবং আমরা বাইট নিয়ে কাজ করছি। আমার দৃশ্যে আমাদের কাছে র্যামের মাত্র 4 বাইট রয়েছে এবং আমাদের বাকী স্মৃতিশক্তি উল্লেখযোগ্যভাবে ধীর হয় (100 গুণ বেশি লম্বা অ্যাক্সেস বলুন)।

মোটামুটি বোবা ক্যাশে নীতি অনুমান করে যদি বাইটটি ক্যাশে না থাকে তবে এটি সেখানে রেখে দিন এবং নীচের বাইটটিও পেয়ে যান যখন আমরা এতে থাকি আপনি এরকম কিছু দৃশ্য পেয়ে যাবেন:

  • সঙ্গে

    for(int j=0;j<n;j++){
     a[j] += b[j];
    }
    for(int j=0;j<n;j++){
     c[j] += d[j];
    }
  • ক্যাশে a[0]এবং a[1]তারপরে b[0]এবং ক্যাশে b[1]সেট a[0] = a[0] + b[0]- ক্যাশে এখন চারটি বাইট রয়েছে a[0], a[1]এবং b[0], b[1]। খরচ = 100 + 100।

  • a[1] = a[1] + b[1]ক্যাশে সেট । ব্যয় = 1 + 1।
  • cএবং এর জন্য পুনরাবৃত্তি করুন d
  • মোট ব্যয় = (100 + 100 + 1 + 1) * 2 = 404

  • সঙ্গে

    for(int j=0;j<n;j++){
     a[j] += b[j];
     c[j] += d[j];
    }
  • ক্যাশে a[0]এবং a[1]তারপরে b[0]এবং ক্যাশে b[1]সেট a[0] = a[0] + b[0]- ক্যাশে এখন চারটি বাইট রয়েছে a[0], a[1]এবং b[0], b[1]। খরচ = 100 + 100।

  • বের করে নিন a[0], a[1], b[0], b[1]ক্যাশে এবং ক্যাশে থেকে c[0]এবং c[1]তারপর d[0]এবং d[1]এবং সেটc[0] = c[0] + d[0] ক্যাশে । খরচ = 100 + 100।
  • আমার সন্দেহ হয় আপনি কোথায় যাচ্ছেন তা আপনি দেখতে শুরু করেছেন।
  • মোট ব্যয় = (100 + 100 + 100 + 100) * 2 = 800

এটি একটি ধ্রুপদী ক্যাশে থ্র্যাশ দৃশ্য।


12
এটি ভুল। একটি অ্যারের নির্দিষ্ট উপাদানের একটি রেফারেন্স পুরো অ্যারেটিকে ডিস্ক থেকে (বা নন-ক্যাশেড মেমরি থেকে) পেজড করে না; কেবলমাত্র প্রাসঙ্গিক পৃষ্ঠা বা ক্যাশে লাইনটি পৃষ্ঠাযুক্ত রয়েছে
ব্রুকস মূসা

1
@ ব্রুকস মূসা - আপনি যদি পুরো অ্যারেটি অনুসরণ করেন, যেমনটি এখানে ঘটছে, তবে তা হবে।
ওল্ড কার্মিউডজিয়ন

1
আচ্ছা, হ্যাঁ, তবে পুরো অপারেশন জুড়েই এটি ঘটে থাকে, লুপের চারপাশে প্রতিবার যা ঘটে তা নয়। আপনি দাবি করেছেন যে দ্বিতীয় ফর্মটি "লুপের চারপাশে প্রতিবার দুটি অ্যারে এবং পৃষ্ঠা দুটি বের করে দেবে," এবং এটিই আমি আপত্তি করছি। সামগ্রিক অ্যারেগুলির আকার নির্বিশেষে, এই লুপের মাঝখানে আপনার র‌্যাম চারটি অ্যারের প্রত্যেকটির একটি পৃষ্ঠা ধরে রাখবে এবং লুপটি শেষ না হওয়া পর্যন্ত কোনও কিছুই পেজ হবে না।
ব্রুকস মূসা

নির্দিষ্ট ক্ষেত্রে যেখানে n কেবল সঠিক সময়ে এটির জন্য আপনার দুটি অ্যারে একবারে মেমোরিতে রাখা সম্ভব ছিল তারপরে চারটি অ্যারের সমস্ত উপাদানকে এক লুপে অ্যাক্সেস করতে অবশ্যই অবশ্যই ছিন্নমূল হওয়া উচিত end
ওল্ড কার্মিউডজিয়ন

1
আপনি কেন লুপটি 2 পৃষ্ঠাগুলির সম্পূর্ণরূপে a1এবং b1প্রথম অ্যাসাইনমেন্টের জন্য রেখেছেন, কেবল তার প্রত্যেকটির প্রথম পৃষ্ঠার চেয়ে? (আপনি কি 5-বাইট পৃষ্ঠাগুলি ধরে নিচ্ছেন, তাই কোনও পৃষ্ঠাটি আপনার র্যামের অর্ধেক? এটি কেবল স্কেলিং নয়, এটি একটি বাস্তব প্রসেসরের সম্পূর্ণ ভিন্ন।)
ব্রুকস মূসা

35

এটি কোনও ভিন্ন কোডের কারণে নয়, তবে ক্যাশে হওয়ার কারণে: সিপিইউ রেজিস্টারগুলির তুলনায় র‌্যাম ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে আসে এবং প্রতিবার কোনও ভেরিয়েবল পরিবর্তন হয় র‌্যাম লিখতে এড়াতে সিপিইউতে ক্যাশে মেমরি থাকে। তবে র‌্যামটি ক্যাশেটি বড় নয়, সুতরাং এটি কেবল তার একটি ভগ্নাংশের মানচিত্র করে।

প্রথম কোড দূরবর্তী মেমরি ঠিকানাগুলি প্রতিটি লুপে সেগুলি পরিবর্তিত করে, এভাবে ক্যাশে অবৈধ করার জন্য অবিচ্ছিন্নভাবে প্রয়োজন requ

দ্বিতীয় কোডটি বিকল্প নয়: এটি সংলগ্ন ঠিকানার উপর দু'বার প্রবাহিত হবে। এটি সমস্ত কাজ ক্যাশে সম্পূর্ণ করতে সক্ষম করে, দ্বিতীয় লুপটি শুরু হওয়ার পরে এটিকে অকার্যকর করে তোলে।


কেন এটি ক্যাশে ক্রমাগত অবৈধ হয়ে যাবে?
অলিভার চার্লসওয়ার্থ

1
@ অলিচার্লসওয়ার্থ: ক্যাশেটিকে মনে রাখবেন মেমরি ঠিকানার একটানা পরিসরের হার্ড কপি হিসাবে। আপনি যদি কোনও ঠিকানাটির অংশ না হয়ে কোনও অ্যাক্সেস করার ভান করেন, আপনাকে ক্যাশেটি আবার লোড করতে হবে। এবং যদি ক্যাশে কিছু সংশোধন করা হত, তবে এটি র‍্যামে আবার লিখতে হবে, বা এটি হারিয়ে যাবে। নমুনা কোডে, 100'000 পূর্ণসংখ্যার 4 ভেক্টর (400kbytes) সম্ভবত L1 ক্যাশে (128 বা 256 কে) এর ক্ষমতা থেকে বেশি।
এমিলিও গারাভাগলিয়া

5
এই দৃশ্যে ক্যাশের আকারের কোনও প্রভাব নেই। প্রতিটি অ্যারে উপাদান কেবল একবার ব্যবহার করা হয় এবং এর পরে তা উচ্ছেদ হয়ে গেলেও তাতে কিছু আসে যায় না। ক্যাশের আকার কেবল তখনই গুরুত্বপূর্ণ যদি আপনার অস্থায়ী লোকালয় থাকে (যেমন আপনি ভবিষ্যতে একই উপাদানগুলি পুনরায় ব্যবহার করতে যাচ্ছেন)।
অলিভার চার্লসওয়ার্থ

2
@ অলিচর্লেসওয়ার্থ: যদি আমাকে কোনও ক্যাশে নতুন মান লোড করতে হয় এবং এর মধ্যে ইতিমধ্যে একটি মান সংশোধন করা হয়েছে তবে আমি প্রথমে এটি লিখতে হবে, এবং এটি আমার লেখার জন্য অপেক্ষা করতে বাধ্য করে।
এমিলিও গারাভাগলিয়া

2
তবে ওপি কোডের উভয় ভেরিয়েন্টে প্রতিটি মান একবারেই সংশোধিত হয়ে যায়। আপনি প্রতিটি ভেরিয়েন্টে একই সংখ্যার লেখার ব্যাক করেন।
অলিভার চার্লসওয়ার্থ

22

আমি এখানে আলোচিত ফলাফলগুলি প্রতিলিপি করতে পারি না।

আমি জানি না দুর্বল বেঞ্চমার্ক কোডটি দোষারোপ করা হয়েছে কিনা, বা কী, তবে দুটি পদ্ধতি নীচের কোডটি ব্যবহার করে আমার মেশিনে একে অপরের 10% এর মধ্যে রয়েছে এবং একটি লুপ সাধারণত দু'জনের চেয়ে সামান্য দ্রুত হয় - আপনি যেমন চান আশা।

অ্যারে আকারগুলি আট লুপ ব্যবহার করে 2 ^ 16 থেকে 2 ^ 24 অবধি। উত্স অ্যারেগুলি সূচনা করার জন্য আমি সাবধান ছিলাম সুতরাং +=অ্যাসাইনমেন্টটি FPU জিজ্ঞাসা করছিল না কে মেমরি আবর্জনা ডাবল হিসাবে ব্যাখ্যা করার জন্য ।

আমি যেমন এর নিয়োগ নির্বাণ বিভিন্ন স্কিম সঙ্গে প্রায় অভিনয় b[j], d[j]করতে InitToZero[j]ব্যবহার করে লুপ ভিতরে, এবং এছাড়াও += b[j] = 1এবং+= d[j] = 1 , এবং আমি মোটামুটি সামঞ্জস্যপূর্ণ ফলাফল পেয়েছি।

যেমনটি আপনি প্রত্যাশা করতে পারেন, লুপটি ব্যবহার করে আরম্ভ bএবং dঅভ্যন্তরীণভাবে InitToZero[j]সম্মিলিত পদ্ধতির একটি সুবিধা দিয়েছে কারণ এ্যাসাইনমেন্টের পূর্বে সেগুলি পিছনে পিছনে সম্পন্ন করা হয়েছিল aএবংc , কিন্তু এখনও মধ্যে 10%। চিত্রে যান.

হার্ডওয়্যারটি হ'ল ডেল এক্সপিএস 8500 প্রজন্মের 3 কোর আই 7 @ 3.4 গিগাহার্টজ এবং 8 জিবি মেমরির সাথে রয়েছে। আট lo টি লুপ ব্যবহার করে 2 ^ 16 থেকে 2 For 24 এর জন্য ক্রমসংখ্যক সময় যথাক্রমে 44.987 এবং 40.965 ছিল। ভিজ্যুয়াল সি ++ ২০১০, সম্পূর্ণরূপে অনুকূলিত।

পিএস: আমি লুপগুলি পরিবর্তন করে শূন্যে নেমে গেলাম, এবং সম্মিলিত পদ্ধতিটি ছিল সামান্য দ্রুত। আমার মাথা আঁচড়ানো। নতুন অ্যারে আকার এবং লুপ গণনা নোট করুন।

// MemBufferMystery.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <cmath>
#include <string>
#include <time.h>

#define  dbl    double
#define  MAX_ARRAY_SZ    262145    //16777216    // AKA (2^24)
#define  STEP_SZ           1024    //   65536    // AKA (2^16)

int _tmain(int argc, _TCHAR* argv[]) {
    long i, j, ArraySz = 0,  LoopKnt = 1024;
    time_t start, Cumulative_Combined = 0, Cumulative_Separate = 0;
    dbl *a = NULL, *b = NULL, *c = NULL, *d = NULL, *InitToOnes = NULL;

    a = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    b = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    c = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    d = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    InitToOnes = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    // Initialize array to 1.0 second.
    for(j = 0; j< MAX_ARRAY_SZ; j++) {
        InitToOnes[j] = 1.0;
    }

    // Increase size of arrays and time
    for(ArraySz = STEP_SZ; ArraySz<MAX_ARRAY_SZ; ArraySz += STEP_SZ) {
        a = (dbl *)realloc(a, ArraySz * sizeof(dbl));
        b = (dbl *)realloc(b, ArraySz * sizeof(dbl));
        c = (dbl *)realloc(c, ArraySz * sizeof(dbl));
        d = (dbl *)realloc(d, ArraySz * sizeof(dbl));
        // Outside the timing loop, initialize
        // b and d arrays to 1.0 sec for consistent += performance.
        memcpy((void *)b, (void *)InitToOnes, ArraySz * sizeof(dbl));
        memcpy((void *)d, (void *)InitToOnes, ArraySz * sizeof(dbl));

        start = clock();
        for(i = LoopKnt; i; i--) {
            for(j = ArraySz; j; j--) {
                a[j] += b[j];
                c[j] += d[j];
            }
        }
        Cumulative_Combined += (clock()-start);
        printf("\n %6i miliseconds for combined array sizes %i and %i loops",
                (int)(clock()-start), ArraySz, LoopKnt);
        start = clock();
        for(i = LoopKnt; i; i--) {
            for(j = ArraySz; j; j--) {
                a[j] += b[j];
            }
            for(j = ArraySz; j; j--) {
                c[j] += d[j];
            }
        }
        Cumulative_Separate += (clock()-start);
        printf("\n %6i miliseconds for separate array sizes %i and %i loops \n",
                (int)(clock()-start), ArraySz, LoopKnt);
    }
    printf("\n Cumulative combined array processing took %10.3f seconds",
            (dbl)(Cumulative_Combined/(dbl)CLOCKS_PER_SEC));
    printf("\n Cumulative seperate array processing took %10.3f seconds",
        (dbl)(Cumulative_Separate/(dbl)CLOCKS_PER_SEC));
    getchar();

    free(a); free(b); free(c); free(d); free(InitToOnes);
    return 0;
}

আমি নিশ্চিত না কেন এমএফএলপিএস একটি প্রাসঙ্গিক মেট্রিক ছিল তা কেন সিদ্ধান্ত নেওয়া হয়েছিল। আমি যদিও ধারণাটি ছিল মেমোরি অ্যাক্সেসগুলিতে ফোকাস করা, তাই আমি ভাসমান পয়েন্ট গণনার সময়টির পরিমাণ হ্রাস করার চেষ্টা করেছি। আমি ছেড়েছি +=, তবে কেন তা নিশ্চিত নয়।

কোনও গণনা ছাড়াই একটি সরল অ্যাসাইনমেন্ট মেমরি অ্যাক্সেস সময়ের একটি ক্লিনার পরীক্ষা এবং লুপ গণনা নির্বিশেষে একটি পরীক্ষা তৈরি করবে যা অভিন্ন। আমি কথোপকথনে কিছু মিস করেছি, তবে এটি সম্পর্কে দু'বার ভাবা উচিত। প্লাসটি যদি অ্যাসাইনমেন্টের বাইরে রেখে যায় তবে প্রতিটি 31 সেকেন্ডে ক্রমবর্ধমান সময় প্রায় অভিন্ন।


1
আপনি এখানে বিভ্রান্তিকর জরিমানার কথা উল্লেখ করেছেন যখন কোনও স্বতন্ত্র লোড / স্টোর যা ভুল পথে চালিত হয় (স্বাক্ষরযুক্ত এসএসই লোড / স্টোর সহ)। পারফর্মেন্সটি বিভিন্ন অ্যারেগুলির আপেক্ষিক প্রান্তিককরণের সাথে সংবেদনশীল হওয়ার কারণে এখানে বিষয়টি তা নয়। নির্দেশ স্তরে কোনও বিভ্রান্তি নেই। প্রতিটি একক লোড / স্টোর যথাযথভাবে সংযুক্ত থাকে।
রহস্যময়

18

এটি কারণ সিপিইউতে এতগুলি ক্যাশে মিস হয় না (যেখানে এটি র‌্যাম চিপস থেকে অ্যারে ডেটার জন্য অপেক্ষা করতে হয়)। এটা তোলে আকর্ষণীয় আপনি অ্যারে এর আকার সামঞ্জস্য জন্য ক্রমাগত যাতে আপনি এর মাপ অতিক্রম হবে স্তর 1 ক্যাশে (এটি L1), এবং তারপর স্তর 2 ক্যাশে আপনার CPU- র এর (ও L2), এবং প্লটে বিভক্ত সময় আপনার কোড গ্রহণ অ্যারেগুলির আকারগুলির বিরুদ্ধে কার্যকর করতে। গ্রাফটি আপনার প্রত্যাশা মতো কোনও সরল রেখা হওয়া উচিত নয়।


2
ক্যাশে আকার এবং অ্যারের আকারের মধ্যে কোনও মিথস্ক্রিয়া আছে বলে আমি বিশ্বাস করি না। প্রতিটি অ্যারে উপাদান কেবল একবার ব্যবহার করা হয় এবং তারপরে নিরাপদে উচ্ছেদ করা যেতে পারে। ক্যাশে লাইন আকার এবং অ্যারের আকারের মধ্যে খুব ভাল একটি মিথস্ক্রিয়া থাকতে পারে , যদিও এটি চারটি অ্যারে দ্বন্দ্ব সৃষ্টি করে।
অলিভার চার্লসওয়ার্থ

15

প্রথম লুপটি প্রতিটি ভেরিয়েবলের লেখার বিকল্প হয়। দ্বিতীয় এবং তৃতীয়টি কেবলমাত্র উপাদান আকারের ছোট জাম্প তৈরি করে।

কলম এবং কাগজকে 20 সেমি দ্বারা পৃথক করে 20 ক্রসের দুটি সমান্তরাল রেখা লেখার চেষ্টা করুন। একবার এবং অন্য লাইনটি শেষ করার চেষ্টা করুন এবং পর্যায়ক্রমে প্রতিটি লাইনে একটি ক্রস লিখে আরও একবার চেষ্টা করুন।


সিপিইউয়ের নির্দেশাবলীর মতো জিনিসগুলির বিষয়ে চিন্তা করার সময় রিয়েল-ওয়ার্ল্ড ক্রিয়াকলাপগুলির উপমাগুলি বিপদে পরিপূর্ণ। আপনি যা চিত্রিত করছেন তা কার্যকরভাবে সময় সন্ধান করা , যা আমরা যদি কোনও স্পিনিং ডিস্কে সঞ্চিত ডেটা পড়ার / লেখার কথা বলছিলাম তবে এটি প্রযোজ্য হবে, তবে সিপিইউ ক্যাশে (অথবা র‌্যামে বা কোনও এসএসডি তে) সময় নেওয়ার সময় নেই। মেমরির অঞ্চলগুলিকে বিচ্ছিন্ন করার অ্যাক্সেসের সাথে কোনও জরিমানা বনাম সংলগ্ন অ্যাক্সেস নিতে হয় না।
এফআরডি

7

আসল প্রশ্ন

দুটি লুপের চেয়ে কেন একটি লুপ এত ধীর?


উপসংহার:

মামলা 1 হ'ল একটি ক্লাসিক ইন্টারপোলেশন সমস্যা যা অকার্যকর হয়ে থাকে। আমি আরও মনে করি যে অনেকগুলি মেশিন আর্কিটেকচার এবং বিকাশকারীরা মাল্টি-থ্রেড অ্যাপ্লিকেশন করার পাশাপাশি প্যারালাল প্রোগ্রামিংয়ের ক্ষমতা সম্পন্ন মাল্টি-কোর সিস্টেমগুলি বিল্ডিং এবং ডিজাইনিংয়ের কাজ শেষ করার অন্যতম কারণ ছিল।

হার্ডওয়্যার, ওএস এবং কম্পাইলার (রা) কীভাবে র‌্যাম, ক্যাশে, পৃষ্ঠা ফাইল ইত্যাদির সাথে কাজ করে জড়িত হ্যাপ বরাদ্দ করতে একসাথে কাজ করে তা জড়িত ছাড়াই এ জাতীয় পদ্ধতির থেকে এটিকে দেখানো; এই অ্যালগরিদমের ভিত্তিতে যে গণিত রয়েছে তা আমাদের দেখায় যে এই দুটির মধ্যে কোনটি আরও ভাল সমাধান।

আমরা একটি একটি উপমা ব্যবহার করতে পারেন Bossএকটি হচ্ছে Summationযে একটি উপস্থাপিত করবে For Loopশ্রমিকদের মধ্যে ভ্রমণ করতে হবে যে A& B

আমরা সহজেই দেখতে পাই যে কেস 2 কমপক্ষে অর্ধেক তত দ্রুত হয় যদি কেস 1 এর চেয়ে সামান্য বেশি না হয় যাতায়াতের জন্য প্রয়োজনীয় দূরত্ব এবং শ্রমিকদের মধ্যে যে সময় নেওয়া হয় তার পার্থক্যের কারণে। এই গণিতটি লঞ্চ প্রায় কার্যত এবং নিখুঁতভাবে বেঞ্চমার্ক টাইমসের পাশাপাশি অ্যাসেম্বলি নির্দেশাবলীর পার্থক্যের সংখ্যার সাথে মিলিয়ে।


আমি এখন নীচে এই সমস্ত কীভাবে কাজ করে তা ব্যাখ্যা করতে শুরু করব।


সমস্যা মূল্যায়ন

ওপির কোড:

const int n=100000;

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

এবং

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

বিবেচনা

অপর দুটি লুপের দুটি রূপ সম্পর্কে ওপির মূল প্রশ্ন এবং অন্যান্য অনেক দুর্দান্ত উত্তর এবং দরকারী মন্তব্যের পাশাপাশি ক্যাশেগুলির আচরণ সম্পর্কে তার সংশোধিত প্রশ্ন বিবেচনা করা; আমি এই পরিস্থিতি এবং সমস্যা সম্পর্কে আলাদা ধারণা গ্রহণ করে এখানে চেষ্টা করতে এবং কিছু আলাদা করতে চাই।


অভিগমন

দুটি লুপগুলি বিবেচনা করে এবং ক্যাশে এবং পৃষ্ঠা ফাইলিংয়ের সমস্ত আলোচনা বিবেচনা করে আমি এটিকে অন্য দৃষ্টিকোণ থেকে দেখার জন্য অন্য একটি পদ্ধতি অবলম্বন করতে চাই। একটি যা ক্যাশে এবং পৃষ্ঠার ফাইলগুলি বা মেমরি বরাদ্দকরণের জন্য মৃত্যুদন্ড জড়িত করে না, বাস্তবে, এই পদ্ধতির এমনকি আসল হার্ডওয়্যার বা সফ্টওয়্যার মোটেই উদ্বেগ নয়।


পরিপ্রেক্ষিত

কিছুক্ষণ কোডটি দেখার পরে এটি বেশ স্পষ্ট হয়ে উঠল যে সমস্যাটি কী এবং এটি কী উত্পন্ন করছে। আসুন এটিকে একটি অ্যালগোরিদমিক সমস্যা হিসাবে ভাঙা যাক এবং গাণিতিক স্বরলিপি ব্যবহারের দৃষ্টিকোণ থেকে এটি দেখুন তবে গণিতের সমস্যাগুলির সাথে সাথে অ্যালগরিদমের ক্ষেত্রেও সাদৃশ্য প্রয়োগ করুন।


আমরা কি জানি

আমরা জানি যে এই লুপটি 100,000 বার চলবে। আমরা জানি যে a1, b1, c1&d1 64-বিট আর্কিটেকচারের উপর পয়েন্টার আছে। সি -+ এর মধ্যে 32-বিট মেশিনে সমস্ত পয়েন্টার 4 বাইট এবং 64-বিট মেশিনে থাকে, সেগুলি 8 বাইট আকারের হয় কারণ পয়েন্টারগুলি একটি নির্দিষ্ট দৈর্ঘ্যের হয়।

আমরা জানি যে আমাদের 32 টি বাইট রয়েছে যাতে উভয় ক্ষেত্রেই বরাদ্দ করতে হয়। পার্থক্যটি হ'ল আমরা প্রতিটি পুনরাবৃত্তির উপর 32 বাইট বা 2-8 বাইটের 2 সেট বরাদ্দ করছি যেখানে 2 য় ক্ষেত্রে আমরা স্বতন্ত্র লুপগুলির জন্য প্রতিটি পুনরাবৃত্তির জন্য 16 বাইট বরাদ্দ করছি।

উভয় লুপ মোট বরাদ্দে এখনও 32 বাইট সমান। এই তথ্যের সাহায্যে এখন এগিয়ে চলুন এবং এই ধারণাগুলির সাধারণ গণিত, অ্যালগরিদম এবং উপমা দেখান।

একই সেট বা অপারেশনের গ্রুপ যা উভয় ক্ষেত্রেই সম্পাদন করতে হবে তার সংখ্যা আমরা জানি। উভয় ক্ষেত্রে যে পরিমাণ মেমরি বরাদ্দ করা দরকার তা আমরা জানি। আমরা মূল্যায়ন করতে পারি যে উভয় ক্ষেত্রেই বরাদ্দের সামগ্রিক কাজের চাপ প্রায় একই রকম হবে।


আমরা কী জানি না

আমরা কাউন্টার সেট না করে এবং একটি মানদণ্ড পরীক্ষা না চালালে প্রতিটি মামলায় কতক্ষণ সময় লাগবে তা আমরা জানি না। তবে মুল প্রশ্নগুলি ইতিমধ্যে মূল প্রশ্ন এবং কিছু উত্তর এবং মন্তব্য থেকে অন্তর্ভুক্ত ছিল; এবং আমরা দুজনের মধ্যে একটি উল্লেখযোগ্য পার্থক্য দেখতে পাচ্ছি এবং এটিই এই সমস্যার এই প্রস্তাবের পুরো যুক্তি।


আসুন তদন্ত করা যাক

এটি ইতিমধ্যে প্রতীয়মান যে ইতিমধ্যে অনেকে হ্যাপ বরাদ্দ, বেঞ্চমার্ক পরীক্ষা, র‌্যাম, ক্যাশে এবং পৃষ্ঠা ফাইলগুলি দেখে এই কাজটি করেছেন। নির্দিষ্ট ডেটা পয়েন্ট এবং সুনির্দিষ্ট পুনরাবৃত্তি সূচকগুলির দিকে তাকানোও অন্তর্ভুক্ত ছিল এবং এই নির্দিষ্ট সমস্যা সম্পর্কে বিভিন্ন কথোপকথনের অনেক লোক এটি সম্পর্কে অন্যান্য সম্পর্কিত বিষয়গুলি প্রশ্ন করা শুরু করে। গাণিতিক অ্যালগোরিদম ব্যবহার করে এবং এর সাথে সাদৃশ্য প্রয়োগ করে আমরা কীভাবে এই সমস্যাটি দেখতে শুরু করব? আমরা দু'দফা দাবি করে শুরু করি! তারপরে আমরা সেখান থেকে আমাদের অ্যালগরিদম তৈরি করি।


আমাদের বক্তব্য:

  • আমরা আমাদের লুপ এবং এর পুনরাবৃত্তিগুলি একটি সংমিশ্রণ হতে দেব যা 1 থেকে শুরু হয় এবং 100000 এ শেষ হয় 0 এর সাথে শুরু না করে লুপগুলির জন্য কারণ আমাদের কেবল মেমরি অ্যাড্রেসিংয়ের 0 সূচীকরণ স্কিম সম্পর্কে চিন্তা করার দরকার নেই যেহেতু আমরা কেবল আগ্রহী অ্যালগরিদম নিজেই।
  • উভয় ক্ষেত্রেই আমাদের সাথে কাজ করার জন্য 4 টি ফাংশন এবং প্রতিটি ফাংশন কলে 2 টি অপারেশন সহ 2 ফাংশন কল রয়েছে। আমরা নিম্নলিখিত হিসাবে ফাংশন ফাংশন এবং কলের এই সেট আপ করবে: F1(), F2(), f(a), f(b), f(c)এবং f(d)

অ্যালগরিদম:

1 ম কেস: - শুধুমাত্র একটি সংশ্লেষ তবে দুটি স্বতন্ত্র ফাংশন কল।

Sum n=1 : [1,100000] = F1(), F2();
                       F1() = { f(a) = f(a) + f(b); }
                       F2() = { f(c) = f(c) + f(d); }

২ য় কেস: - দুটি সারসংক্ষেপ তবে প্রত্যেকটির নিজস্ব ফাংশন কল রয়েছে।

Sum1 n=1 : [1,100000] = F1();
                        F1() = { f(a) = f(a) + f(b); }

Sum2 n=1 : [1,100000] = F1();
                        F1() = { f(c) = f(c) + f(d); }

আপনি খেয়াল F2()কেবলমাত্র বিদ্যমান Sumথেকে Case1যেখানে F1()মধ্যে অন্তর্ভুক্ত করা হয় Sumথেকে Case1এবং উভয় Sum1এবং Sum2থেকে Case2। এটি পরে স্পষ্ট হবে যখন আমরা এই সিদ্ধান্তে পৌঁছাতে শুরু করি যে একটি বিকল্প রয়েছে যা দ্বিতীয় অ্যালগরিদমের মধ্যে চলছে।

প্রথম কেস Sumকলগুলির মাধ্যমে পুনরাবৃত্তিগুলি f(a)তার স্বতে যুক্ত হবে f(b)তারপরে এটি কল f(c)করবে যা একই কাজ করবে তবে f(d)প্রতিটি 100000পুনরাবৃত্তির জন্য নিজেকে যুক্ত করবে । দ্বিতীয় ক্ষেত্রে, আমাদের আছে Sum1এবং Sum2উভয়ই একই কাজ করে যেমন তারা একই ফাংশনটি পরপর দু'বার ডাকা হচ্ছে।

এই ক্ষেত্রে আমরা চিকিত্সা করতে পারি Sum1এবং Sum2ঠিক সাদামাটা পুরাতন Sumযেখানে Sumএই ক্ষেত্রে এটি দেখতে দেখতে: Sum n=1 : [1,100000] { f(a) = f(a) + f(b); }এবং এখন এটি একটি অপ্টিমাইজেশানের মতো দেখাচ্ছে যেখানে আমরা কেবল এটি একই ফাংশন হিসাবে বিবেচনা করতে পারি।


উপমা সঙ্গে সংক্ষিপ্তসার

আমরা দ্বিতীয় ক্ষেত্রে যা দেখেছি তার সাথে এটি প্রায় উপস্থিতি হিসাবে দেখা যায় যেহেতু অপ্টিমাইজেশন রয়েছে যেহেতু উভয় লুপের জন্য একই সঠিক স্বাক্ষর রয়েছে, তবে এটি আসল সমস্যা নয়। ইস্যু কাজ করা হয় যে কাজ করা হচ্ছে না f(a), f(b), f(c), এবং f(d)। উভয় ক্ষেত্রে এবং উভয়ের মধ্যে তুলনা করা, এটিই প্রতিটি ক্ষেত্রে সামুশনের ভ্রমণ করতে হবে এমন দূরত্বের পার্থক্য যা আপনাকে মৃত্যুদন্ডের সময়টিতে পার্থক্য দেয়।

চিন্তা করুন For Loopsহচ্ছে Summationsএকটি হচ্ছে পুনরাবৃত্তিও করে Bossযে দুই জনের আদেশ প্রদান করা হয় A& Bএবং যে তাদের কাজ মাংস হয় C& Dযথাক্রমে তাদের কাছ থেকে কিছু প্যাকেজ নিতে এবং এটা দেখাবে। এই সাদৃশ্যগুলিতে, লুপগুলি বা সংমিশ্রণ পুনরাবৃত্তির জন্য এবং শর্তগুলি পরীক্ষা করে তারা প্রকৃতপক্ষে প্রতিনিধিত্ব করে না Boss। আসলে কি প্রতিনিধিত্ব করে Bossপ্রকৃত গাণিতিক আলগোরিদিম সরাসরি থেকে কিন্তু প্রকৃত ধারণা থেকে নয় Scopeএবং Code Blockমধ্যে একটি রুটিন বা সাবরুটিন, পদ্ধতি, ফাংশন, অনুবাদ ইউনিট, ইত্যাদি প্রথম অ্যালগরিদম 1 সুযোগ যেখানে 2nd অ্যালগরিদম পরপর 2 সুযোগ রয়েছে।

প্রতিটি কল স্লিপে প্রথম কেসের মধ্যে, Bossযায় Aএবং অর্ডার দেয় এবং প্যাকেজ Aআনতে চলে যায় এবং পরে একই কাজ করার আদেশ দেয় এবং প্রতিটি পুনরাবৃত্তির থেকে প্যাকেজটি গ্রহণ করে ।B'sBossCD

দ্বিতীয় ক্ষেত্রে, সমস্ত প্যাকেজ না পাওয়া পর্যন্ত প্যাকেজ আনতে Bossসরাসরি কাজ করে । তারপরে সমস্ত প্যাকেজ পাওয়ার জন্য একই কাজ করে।AB'sBossCD's

যেহেতু আমরা একটি 8-বাইট পয়েন্টার নিয়ে কাজ করছি এবং গাদা বরাদ্দ নিয়ে কাজ করছি আসুন নীচের সমস্যাটি বিবেচনা করুন। ধরা যাক যে এটি Bossথেকে 100 ফুট Aএবং এটি A500 ফুট C। মৃত্যুদন্ডের আদেশের কারণে Bossআমাদের প্রাথমিকভাবে কতটা দূরে রয়েছে তা নিয়ে আমাদের চিন্তা Cকরার দরকার নেই। উভয় ক্ষেত্রেই প্রথম দিকে প্রথম Bossথেকে Aপ্রথম দিকে ভ্রমণ হয় B। এই সাদৃশ্যটি এই দূরত্বটি হুবহু বলা যায় না; অ্যালগরিদমের কাজ দেখানোর জন্য এটি কেবল একটি দরকারী পরীক্ষার ক্ষেত্রে দৃশ্যপট।

অনেক ক্ষেত্রে হিপ বরাদ্দগুলি করার সময় এবং ক্যাশে এবং পৃষ্ঠা ফাইলগুলির সাথে কাজ করার সময়, ঠিকানার অবস্থানগুলির মধ্যে এই দূরত্বগুলি খুব বেশি আলাদা হয় না বা তারা ডেটা ধরণের এবং অ্যারের আকারগুলির প্রকৃতির উপর নির্ভর করে উল্লেখযোগ্যভাবে পরিবর্তিত হতে পারে।


পরীক্ষার কেস:

প্রথম কেস: প্রথম পুনরাবৃত্তির সময়Bossঅর্ডার স্লিপ দিতে প্রথমে 100 ফুট যেতে হয়AএবংAচলে যায় এবং তার কাজটি করে, তবেতার অর্ডার স্লিপ দিতে তারBoss500 পা ভ্রমণ করতেCহবে। তারপরে পরবর্তী পুনরাবৃত্তিতে এবং অন্য প্রতিটি পুনরাবৃত্তির পরে উভয়েরBossমধ্যে 500 ফুট পিছনে পিছনে যেতে হবে।

দ্বিতীয়ত কেস:Boss প্রথম পুনরাবৃত্তির উপর 100 ফুট ভ্রমণ করতে হয়েছেA, কিন্তু যে পরে, তিনি ইতিমধ্যেই আছে এবং মাত্র জন্য অপেক্ষাAফিরে পেতে পর্যন্ত স্লিপ ভরা হয়। তারপরেBossপ্রথম পুনরাবৃত্তিতে 500 ফুট ভ্রমণ করতে হবেCকারণCএটি 500 ফুটA। যেহেতুতাঁরBoss( Summation, For Loop )সাথে কাজ করার পরে এটি ডাকা হচ্ছে ঠিকAতখনই সেখানে অপেক্ষা করেনAযতক্ষণ না তিনিC'sঅর্ডার স্লিপনা করে সমস্তকাজ শেষ করেন।


দূরত্বের পার্থক্য ভ্রমণ

const n = 100000
distTraveledOfFirst = (100 + 500) + ((n-1)*(500 + 500); 
// Simplify
distTraveledOfFirst = 600 + (99999*100);
distTraveledOfFirst = 600 + 9999900;
distTraveledOfFirst =  10000500;
// Distance Traveled On First Algorithm = 10,000,500ft

distTraveledOfSecond = 100 + 500 = 600;
// Distance Traveled On Second Algorithm = 600ft;    

সালিসী মূল্যবোধের তুলনা

আমরা সহজেই দেখতে পাই যে 600 টি 10 ​​মিলিয়নের চেয়ে অনেক কম। এখন, এটি সঠিক নয়, কারণ আমরা জানি না কোন র‌্যামের ঠিকানা বা যা থেকে ক্যাশে বা পৃষ্ঠা ফাইলের মধ্যে প্রতিটি দূরত্বের প্রতিটি কল অন্য অনেকগুলি অদেখা ভেরিয়েবলের কারণে হবে তার মধ্যে দূরত্বের প্রকৃত পার্থক্যটি আমরা জানি না। সবচেয়ে খারাপ পরিস্থিতি থেকে সচেতন হওয়া এবং এটিকে দেখার জন্য এটি কেবলমাত্র পরিস্থিতির একটি মূল্যায়ন।

এই সংখ্যাগুলি থেকে এটি প্রায় প্রদর্শিত হবে যেন অ্যালগোরিদম ওয়ান 99%আলগোরিদম টু এর চেয়ে ধীর হওয়া উচিত ; যাইহোক, এই মাত্র Boss'sঅংশ বা আলগোরিদিম দায়িত্ব এবং এটা প্রকৃত শ্রমিকদের জন্য অ্যাকাউন্ট নেই A, B, C, & Dএবং তারা কি একে এবং লুপ প্রতিটি পুনরাবৃত্তির উপর করতে হবে। সুতরাং বসের কাজটি মোট কাজকর্মের প্রায় 15 - 40% কাজ করে। শ্রমিকদের মাধ্যমে যে কাজটি করা হয় তার বেশিরভাগ গতি হারের পার্থক্যের অনুপাতকে প্রায় 50-70% রাখার দিকে কিছুটা বড় প্রভাব ফেলে


পর্যবেক্ষণ: - দুটি অ্যালগরিদমের মধ্যে পার্থক্য

এই পরিস্থিতিতে, এটি কাজ করা প্রক্রিয়াটির কাঠামো। এটি দেখায় যে কেস 2 সমান ফাংশন ঘোষণা এবং সংজ্ঞা যেখানে আংশিক অপ্টিমাইজেশান উভয় থেকে আরও দক্ষ যেখানে এটি কেবল নাম এবং পার্থক্য অনুসারে দূরত্বে পরিবর্তনশীল।

আমরা আরও দেখতে পেলাম যে কেস 1 এ ভ্রমণ করা মোট দূরত্ব কেস 2 এর তুলনায় অনেক বেশি দূরে এবং আমরা এই দূরত্বটিকে দুটি অ্যালগরিদমের মধ্যে আমাদের টাইম ফ্যাক্টর ভ্রমণ করে বিবেচনা করতে পারি । কেস 1 এর ক্ষেত্রে কেস 2 এর তুলনায় যথেষ্ট বেশি কাজ করা উচিত।

ASMউভয় ক্ষেত্রে যে নির্দেশাবলী প্রদর্শিত হয়েছিল তার প্রমাণ থেকে এটি পর্যবেক্ষণযোগ্য । কি ইতিমধ্যে এই মামলা সম্পর্কে নিদিষ্ট ছিল সাথে এই সত্য যে হিসাব নেই কেস 1 মনিব উভয়ের জন্য অপেক্ষা করতে হবে A& Cফিরে পেতে আগে তিনি ফিরে যেতে পারেন Aপ্রতিটি পুনরাবৃত্তির জন্য আবার। এটি এই সত্যের জন্যও দায়বদ্ধ নয় যে যদি Aবা Bখুব দীর্ঘ সময় নিচ্ছে তবে উভয় Bossএবং অন্যান্য কর্মী (গুলি) মৃত্যুদন্ড কার্যকর হওয়ার অপেক্ষায় রয়েছেন ।

ইন কেস 2 শুধুমাত্র এক হচ্ছে নিষ্ক্রিয় Bossপর্যন্ত শ্রমিক ফিরে পায়। এমনকি এটিরও অ্যালগরিদমের উপর প্রভাব রয়েছে।



ওপিএস সংশোধিত প্রশ্ন (গুলি)

সম্পাদনা: প্রশ্নটি কোনও প্রাসঙ্গিকতার প্রমাণিত হয়নি, কারণ আচরণটি মারাত্মকভাবে অ্যারে (এন) এর আকার এবং সিপিইউ ক্যাশে নির্ভর করে। সুতরাং যদি আরও আগ্রহ থাকে তবে আমি এই প্রশ্নটি পুনরায় বলি:

নিম্নলিখিত গ্রাফের পাঁচটি অঞ্চলের দ্বারা বর্ণিত বিভিন্ন ক্যাশে আচরণের দিকে পরিচালিত করে এমন বিশদ সম্পর্কে আপনি কি কিছু দৃ ?়দৃষ্টি দিতে পারেন?

এই সিপিইউগুলির জন্য অনুরূপ গ্রাফ সরবরাহ করে সিপিইউ / ক্যাশে আর্কিটেকচারের মধ্যে পার্থক্যগুলি চিহ্নিত করাও আকর্ষণীয় হতে পারে।


এই প্রশ্নগুলি সম্পর্কে

আমি কোনও সন্দেহ ছাড়াই প্রদর্শিত করেছি যেহেতু হার্ডওয়্যার এবং সফ্টওয়্যার জড়িত হওয়ার আগেই অন্তর্নিহিত সমস্যা রয়েছে।

এখন মেমরি পরিচালনা এবং পৃষ্ঠাগুলি ফাইল ইত্যাদির সাথে ক্যাশে করার ব্যবস্থা যা সমস্ত নিম্নলিখিতগুলির মধ্যে সিস্টেমের একীভূত সংস্থায় একসাথে কাজ করে:

  • The Architecture {হার্ডওয়্যার, ফার্মওয়্যার, কিছু এম্বেডড ড্রাইভার, কার্নেল এবং এএসএম নির্দেশাবলী সেট}
  • The OSফাইল এবং মেমরি ম্যানেজমেন্ট সিস্টেম, ড্রাইভার এবং রেজিস্ট্রি}
  • The Compiler {উত্স কোডটির অনুবাদ ইউনিট এবং অপ্টিমাইজেশন}
  • এমনকি Source Codeনিজেই এর স্বতন্ত্র অ্যালগরিদমের সেট (গুলি) সহ।

আমরা ইতিমধ্যে দেখতে পারেন একটি বোতলের যে প্রথম অ্যালগরিদম মধ্যে ঘটছে আগে আমরা এমনকি কোনো অবাধ সঙ্গে কোনো মেশিনে এটি প্রয়োগ নেই Architecture, OSএবং Programmable Languageদ্বিতীয় অ্যালগরিদম তুলনায়। একটি আধুনিক কম্পিউটারের অন্তর্নিহিত জড়িত হওয়ার আগে একটি সমস্যা ইতিমধ্যে রয়েছে।


শেষ ফলাফল

যাহোক; এটি বলার অপেক্ষা রাখে না যে এই নতুন প্রশ্নগুলির গুরুত্ব নেই কারণ তারা নিজেরাই এবং তারা সর্বোপরি একটি ভূমিকা পালন করে। তারা পদ্ধতিগুলি এবং সামগ্রিক পারফরম্যান্সকে প্রভাবিত করে এবং তাদের উত্তর (গুলি) এবং মন্তব্য (গুলি) দিয়েছেন এমন অনেকের বিভিন্ন গ্রাফ এবং মূল্যায়নের মাধ্যমে এটি স্পষ্ট হয়।

তোমাদের মধ্যে উপমা থেকে মনোযোগ দেওয়া যদি Bossও দুই শ্রমিক ABযারা যান এবং থেকে প্যাকেজ পুনরুদ্ধার করতে ছিল C& Dযথাক্রমে এবং প্রশ্ন দুটি আলগোরিদিম গাণিতিক স্বরলিপি বিবেচনা করা; আপনি কম্পিউটারের হার্ডওয়্যার এবং সফ্টওয়্যার এর জড়িততা ছাড়াই দেখতে Case 2প্রায় 60%তুলনায় দ্রুত Case 1

যখন আপনি এই অ্যালগরিদমগুলি কোনও উত্স কোডে প্রয়োগ করা, সংকলিত, অনুকূলিতকরণ এবং ওএসের মাধ্যমে কোনও হার্ডওয়্যারের কোনও অংশে তাদের ক্রিয়াকলাপ সম্পাদনের জন্য প্রয়োগ করার পরে গ্রাফগুলি এবং চার্টগুলি দেখেন, আপনি এমনকি পার্থক্যগুলির মধ্যে কিছুটা আরও অবনতি দেখতে পান এই অ্যালগরিদমে।

যদি Dataসেট মোটামুটি ছোট এটা প্রথমে একটি পার্থক্য সব যে খারাপ বলে মনে হচ্ছে না পারে। যাইহোক, Case 1আমার হয় 60 - 70%ধীর চেয়ে Case 2আমরা সময় মৃত্যুদন্ড পার্থক্য পরিপ্রেক্ষিতে এই ফাংশন বৃদ্ধি তাকান করতে পারেন:

DeltaTimeDifference approximately = Loop1(time) - Loop2(time)
//where 
Loop1(time) = Loop2(time) + (Loop2(time)*[0.6,0.7]) // approximately
// So when we substitute this back into the difference equation we end up with 
DeltaTimeDifference approximately = (Loop2(time) + (Loop2(time)*[0.6,0.7])) - Loop2(time)
// And finally we can simplify this to
DeltaTimeDifference approximately = [0.6,0.7]*Loop2(time)

এই আনুমানিকতা উভয় আলগরিদমিকভাবে এবং সফ্টওয়্যার অপ্টিমাইজেশন এবং মেশিনের নির্দেশাবলী জড়িত মেশিন অপারেশনগুলির মধ্যে এই দুটি লুপের মধ্যে গড় পার্থক্য।

যখন ডেটা সেটটি রৈখিকভাবে বৃদ্ধি পায়, তখন উভয়ের মধ্যে সময়ের মধ্যে পার্থক্য থাকে। যখন অ্যালগরিদম 1 অ্যালগরিদম 2 চেয়ে বেশি নিয়ে আসে যা স্পষ্ট হয়েছে Bossমধ্যে ভ্রমণ আগে পিছে সর্বাধিক দূরত্ব রয়েছে A& Cপ্রথম পুনরাবৃত্তির পর প্রতি পুনরাবৃত্তির সময় অ্যালগরিদম 2 Bossআছে ভ্রমণ করতে Aএকবার এবং তারপর সঙ্গে সম্পন্ন হওয়ার পর Aতিনি ভ্রমণ করেছেন সর্বাধিক দূরত্ব শুধুমাত্র এক সময় যখন থেকে যাচ্ছে Aকরার C

Bossএকই সাথে দুটি একই কাজ করার দিকে মনোযোগ কেন্দ্রীভূত করার চেষ্টা করা এবং একই ধ্রুবক ক্রিয়াকলাপের দিকে মনোনিবেশ করার পরিবর্তে সেগুলি পিছনে পিছনে জাগ্রত করা তাকে দিনের শেষের দিকে বেশ ক্রুদ্ধ করবে যেহেতু তাকে ভ্রমণ করতে হয়েছে এবং দ্বিগুণ কাজ করতে হয়েছিল। সুতরাং আপনার বসকে একটি দ্বিখণ্ডিত বাধা দেওয়াতে পরিস্থিতিটির সুযোগটি হারাবেন না কারণ বসের পত্নী এবং বাচ্চারা এর প্রশংসা করবে না।



সংশোধন: সফটওয়্যার ইঞ্জিনিয়ারিং ডিজাইনের নীতিমালা

- লুপগুলির জন্য পুনরাবৃত্তির মধ্যে Local Stackএবং Heap Allocatedগণনাগুলির মধ্যে পার্থক্য এবং তাদের ব্যবহার, তাদের দক্ষতা এবং কার্যকারিতার মধ্যে পার্থক্য -

গাণিতিক অ্যালগরিদম যেটি আমি উপরে প্রস্তাব করেছি তা মূলত লুপগুলিতে প্রযোজ্য যেগুলি হিটে বরাদ্দকৃত ডেটাতে অপারেশন করে।

  • ধারাবাহিক স্ট্যাক অপারেশন:
    • যদি লুপগুলি স্ট্যাক ফ্রেমের মধ্যে থাকা একটি একক কোড ব্লক বা স্কোপের মধ্যে স্থানীয়ভাবে ডেটাতে ক্রিয়াকলাপ সম্পাদন করে তবে এটি এখনও সাজানোর জন্য প্রযোজ্য হবে, তবে মেমরির অবস্থানগুলি আরও ঘনিষ্ঠ যেখানে তারা সাধারণত ক্রমযুক্ত এবং দূরত্বে ভ্রমণ বা সম্পাদনের সময় পার্থক্যের পার্থক্য প্রায় নগণ্য। যেহেতু স্তূপের মধ্যে কোনও বরাদ্দ করা হচ্ছে না, স্মৃতিটি ছড়িয়ে নেই, এবং মেমরিটি রামের মধ্য দিয়ে আনা হচ্ছে না। মেমরিটি সাধারণত স্ট্যাক ফ্রেম এবং স্ট্যাক পয়েন্টারটির সাথে ক্রমযুক্ত হয়।
    • যখন স্ট্যাকের উপর ক্রমাগত অপারেশন করা হচ্ছে, একটি আধুনিক প্রসেসর পুনরাবৃত্তিযোগ্য মান এবং ঠিকানাগুলিকে স্থানীয় ক্যাশে রেজিস্টারে রেখে এই মানগুলিকে ক্যাশে করবে। এখানে অপারেশন বা নির্দেশাবলীর সময়টি ন্যানো-সেকেন্ডের ক্রম অনুসারে।
  • ধারাবাহিকভাবে গাদা বরাদ্দকৃত অপারেশন:
    • আপনি যখন হিপ বরাদ্দ প্রয়োগ করতে শুরু করেন এবং প্রসেসরের সিপিইউ, বাস কন্ট্রোলার এবং রাম মডিউলগুলির আর্কিটেকচারের উপর নির্ভর করে পরপর কলগুলিতে মেমরি ঠিকানাগুলি আনতে হয় এবং অপারেশন বা সম্পাদনের সময় মাইক্রো থেকে ক্রম হতে পারে মিলিসেকেন্ড। ক্যাশেড স্ট্যাক অপারেশনের তুলনায়, এগুলি বেশ ধীর slow
    • সিপিইউকে রামের কাছ থেকে মেমরি ঠিকানাটি আনতে হবে এবং সাধারণত সিপিইউতে থাকা অভ্যন্তরীণ ডেটা পাথ বা ডেটা বাসের তুলনায় সিস্টেম বাসের যে কোনও কিছুই ধীর গতির হয়।

সুতরাং যখন আপনি সেই ডেটার সাথে কাজ করছেন যা গাদা হতে হবে এবং আপনি লুপগুলিতে সেগুলি অনুসরণ করছেন, প্রতিটি ডেটা সেট এবং তার সাথে সম্পর্কিত অ্যালগরিদমগুলি তার নিজস্ব একক লুপের মধ্যে রাখা আরও দক্ষ। একা লুপের মধ্যে থাকা বিভিন্ন ডেটা সেটগুলির একাধিক ক্রিয়াকলাপ রেখে একটানা লুপগুলি ফ্যাক্ট করার চেষ্টা করার তুলনায় আপনি আরও ভাল অপ্টিমাইজেশন পাবেন।

স্ট্যাকের সাথে থাকা ডেটা দিয়ে এটি করা ঠিক আছে যেহেতু তারা প্রায়শই ক্যাশে থাকে তবে এমন ডেটার জন্য নয় যা এর মেমরি ঠিকানাটি প্রতিটি পুনরাবৃত্তিতে জিজ্ঞাসা করে।

এখানেই সফ্টওয়্যার ইঞ্জিনিয়ারিং এবং সফটওয়্যার আর্কিটেকচার ডিজাইন কার্যকর হয়। আপনার ডেটা কীভাবে সংগঠিত করতে হবে তা জানার ক্ষমতা, আপনার ডেটা কখন ক্যাশে করবেন তা জেনে রাখা, কখন আপনার ডেটাটি গিমে বরাদ্দ করতে হবে তা জেনে রাখা, কীভাবে আপনার অ্যালগরিদমগুলি ডিজাইন এবং প্রয়োগ করতে হবে এবং কখন এবং কোথায় তাদের কল করতে হবে তা জানার ক্ষমতা is

আপনার একই অ্যালগরিদম হতে পারে যা একই ডেটা সেটের সাথে সম্পর্কিত, তবে আপনি তার স্ট্যাক ভেরিয়েন্টের জন্য একটি বাস্তবায়ন নকশা এবং অন্যটি তার গাদা-বরাদ্দিত ভেরিয়েন্টের জন্য কেবলমাত্র উপরের ইস্যুটির কারণেই O(n)কাজ করতে গিয়ে অ্যালগরিদমের জটিলতা থেকে দেখা যায় want গাদা দিয়ে

আমি বছরের পর বছর ধরে যা লক্ষ্য করেছি তা থেকে অনেকে এই বাস্তবতাকে বিবেচনা করে না। তারা একটি নির্দিষ্ট অ্যালগরিদম ডিজাইন করার প্রবণতা রাখবে যা কোনও নির্দিষ্ট ডেটা সেটে কাজ করে এবং তারা স্থানীয়ভাবে ডেটা সেটটিকে স্ট্যাকের উপরে ক্যাশে রেখে দেওয়া বা এটি স্তূপে বরাদ্দ করা হয়েছিল তা নির্বিশেষে তারা এটি ব্যবহার করবে।

আপনি যদি সত্য অপ্টিমাইজেশন চান তবে হ্যাঁ এটি কোড সদৃশ মনে হতে পারে তবে সাধারণকরণের জন্য একই অ্যালগোরিদমের দুটি রূপ থাকা আরও দক্ষ। একটি স্ট্যাক অপারেশনের জন্য এবং অন্যটি হিপ অপারেশনের জন্য যা পুনরাবৃত্ত লুপগুলিতে সঞ্চালিত হয়!

এখানে ছদ্ম উদাহরণ রয়েছে: দুটি সহজ স্ট্রাক্ট, একটি অ্যালগরিদম।

struct A {
    int data;
    A() : data{0}{}
    A(int a) : data{a}{} 
};
struct B {
    int data;
    B() : data{0}{}
    A(int b) : data{b}{}
}                

template<typename T>
void Foo( T& t ) {
    // do something with t
}

// some looping operation: first stack then heap.

// stack data:
A dataSetA[10] = {};
B dataSetB[10] = {};

// For stack operations this is okay and efficient
for (int i = 0; i < 10; i++ ) {
   Foo(dataSetA[i]);
   Foo(dataSetB[i]);
}

// If the above two were on the heap then performing
// the same algorithm to both within the same loop
// will create that bottleneck
A* dataSetA = new [] A();
B* dataSetB = new [] B();
for ( int i = 0; i < 10; i++ ) {
    Foo(dataSetA[i]); // dataSetA is on the heap here
    Foo(dataSetB[i]); // dataSetB is on the heap here
} // this will be inefficient.

// To improve the efficiency above, put them into separate loops... 

for (int i = 0; i < 10; i++ ) {
    Foo(dataSetA[i]);
}
for (int i = 0; i < 10; i++ ) {
    Foo(dataSetB[i]);
}
// This will be much more efficient than above.
// The code isn't perfect syntax, it's only psuedo code
// to illustrate a point.

হ'ল ভেরিয়েন্ট বনাম স্ট্যাক ভেরিয়েন্টগুলির জন্য পৃথক বাস্তবায়ন করে আমি এটি উল্লেখ করছি। অ্যালগোরিদমগুলি এগুলি খুব বেশি গুরুত্ব দেয় না, এটি লুপিং স্ট্রাকচারগুলি যা আপনি তাদের এটিতে ব্যবহার করবেন।


আমি এই উত্তরটি পোস্ট করার পরে এটি হয়ে গেছে, তবে আমি একটি দ্রুত মন্তব্যও যুক্ত করতে চেয়েছিলাম যা এটি বুঝতেও সহায়তা করতে পারে: লসের জন্য বসের সাথে সংক্ষিপ্তসার বা পুনরাবৃত্তির সাথে আমার সাদৃশ্যগুলিতে আমরাও পারতাম এই বসকে স্ট্যাক ফ্রেম এবং স্ট্যাক পয়েন্টারের মধ্যে সংমিশ্রণ হিসাবে বিবেচনা করুন যা স্কুপ এবং স্ট্যাক ভেরিয়েবল এবং লুপের স্মৃতি সম্বোধন পরিচালনা করে।
ফ্রান্সিস কুগার

@ পিটারমোরটেনসেন আমি আমার মূল উত্তরটি সামান্য পরিবর্তন করে আপনার পরামর্শটি বিবেচনায় নিয়েছি। আমি বিশ্বাস করি এটি আপনি পরামর্শ দিয়েছিলেন।
ফ্রান্সিস কুগার

2

এটি পুরানো সি ++ এবং অপ্টিমাইজেশান হতে পারে। আমার কম্পিউটারে আমি প্রায় একই গতি পেয়েছি:

একটি লুপ: 1.577 এমএস

দুটি লুপ: 1.507 এমএস

আমি 16 গিগাবাইট র‌্যাম সহ একটি ই 5-1620 3.5 গিগাহার্টজ প্রসেসরে ভিজ্যুয়াল স্টুডিও 2015 চালাচ্ছি।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.