সমান্তরালভাবে এলোমেলো পড়া ভাল মনে হচ্ছে - কেন?


18

নিম্নলিখিত খুব সাধারণ কম্পিউটার প্রোগ্রাম বিবেচনা করুন:

for i = 1 to n:
    y[i] = x[p[i]]

এখানে এক্স এবং Y হয় এন বাইটের -element অ্যারে, এবং পি একটি হল এন শব্দের -element অ্যারে। এখানে এন বড়, উদাহরণস্বরূপ, এন=231 (যাতে ডেটাগুলির কেবল একটি नगনীয় ভগ্নাংশ কোনও ধরণের ক্যাশে স্মৃতিতে ফিট করে)।

ধরে নিন যে এলোমেলো সংখ্যাপি নিয়ে গঠিত, 1 এবং n এর মধ্যে সমানভাবে বিতরণ করা হয়েছে ।1এন

আধুনিক হার্ডওয়্যার দৃষ্টিকোণ থেকে, এর অর্থ নিম্নলিখিত হওয়া উচিত:

  • পড়া [ আমি ]পি[আমি] সস্তা (ক্রমানুসারে পড়া)
  • পড়া এক্স[পি[আমি]]খুব ব্যয়বহুল (এলোমেলো পড়া; প্রায় সমস্ত পাঠই ক্যাশে মিস করে; আমাদের প্রতিটি স্বতন্ত্র বাইটকে মূল স্মৃতি থেকে আনতে হবে)
  • লেখা [ i ]Y[আমি] সস্তা (ক্রমানুসারে লেখা)।

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

এখন প্রশ্ন আসে: আধুনিক মাল্টি-কোর প্ল্যাটফর্মগুলিতে এই প্রোগ্রামটি কতটা সমান্তরাল হয়?


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

যাইহোক, আমি যখন কিছু অ্যালগরিদমের সাথে পরীক্ষা শুরু করলাম যেখানে বাধাটি এই ধরণের অপারেশন ছিল তা আমি লক্ষ্য করি নি !

আমি খালি নিখরচুর জন্য লুপের জন্য একটি ওপেনএমপি সমান্তরাল-লুপের সাথে প্রতিস্থাপন করেছি (সংক্ষেপে, এটি কেবলমাত্র এর পরিসরকে ছোট ছোট অংশে বিভক্ত করবে এবং এই অংশগুলিকে সমান্তরালে বিভিন্ন সিপিইউ কোরে চালিত করবে)।[1,এন]

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

  • 2 এক্স 4-কোর জিয়ন (মোট 8 টি কোরে): একক-থ্রেড সংস্করণের তুলনায় ফ্যাক্টর 5-8 স্পিডআপগুলি।

  • 2 এক্স 6-কোর জিয়ন (মোট 12 কোরে): একক-থ্রেডযুক্ত সংস্করণের তুলনায় 8-14 স্পিডআপগুলি ফ্যাক্টর।

এখন এটি সম্পূর্ণ অপ্রত্যাশিত ছিল। প্রশ্নাবলী:

  1. স্পষ্টতই কেন এই ধরণের প্রোগ্রামটি এত ভালভাবে সমান্তরাল হয় ? হার্ডওয়ারে কী হয়? (আমার বর্তমান অনুমান এই রেখাগুলির পাশাপাশি কিছু: বিভিন্ন থ্রেড থেকে এলোমেলো পড়াগুলি "পাইপলাইনযুক্ত" এবং এগুলির উত্তর পাওয়ার গড় হার একক থ্রেডের তুলনায় অনেক বেশি))

  2. কোনও স্পিডআপগুলি পেতে একাধিক থ্রেড এবং একাধিক কোর ব্যবহার করা কী প্রয়োজনীয় ? মূল স্মৃতি এবং সিপিইউর মধ্যে ইন্টারফেসে যদি কোনও ধরণের পাইপলাইনিং ঘটে থাকে তবে কোনও একক থ্রেডযুক্ত অ্যাপ্লিকেশন প্রধান মেমরিটিকে তা শিগগিরই জানতে দেয় না যে খুব শীঘ্রই এর জন্য , x [ p [ i + 1 ] ] , ... এবং কম্পিউটার মূল স্মৃতি থেকে প্রাসঙ্গিক ক্যাশে লাইন আনতে শুরু করতে পারে? যদি নীতিগতভাবে এটি সম্ভব হয় তবে আমি বাস্তবে এটি কীভাবে অর্জন করব?এক্স[পি[আমি]]এক্স[পি[আমি+ +1]]

  3. সঠিক তাত্ত্বিক মডেল কী যা আমরা এই জাতীয় প্রোগ্রাম বিশ্লেষণ করতে (এবং পারফরম্যান্সের সঠিক ভবিষ্যদ্বাণী করতে পারি) ব্যবহার করতে পারি ?


সম্পাদনা করুন: এখন এখানে কিছু সোর্স কোড এবং মাপদণ্ডের ফলাফলগুলি পাওয়া যায়: https://github.com/suomela/parallel-random-read

বলপার্কের পরিসংখ্যানগুলির কয়েকটি উদাহরণ ( ):এন=232

  • প্রায়. একক থ্রেড সহ পুনরাবৃত্তি (এলোমেলোভাবে পড়া) 42 এনএস
  • প্রায়. 12 কোরের সাথে পুনরাবৃত্তির জন্য 5 এনএস (এলোমেলো পড়া)।

উত্তর:


9

পিএনপিএনপিপি

এখন, আসুন আমরা মেমরির সমস্যাগুলি বিবেচনা করি। আপনার হাই-এন্ড জিওন ভিত্তিক নোডে আপনি যে সুপার-লিনিয়ার স্পিডআপটি দেখেছেন তা নীচে ন্যায়সঙ্গত।

এনএন/পিপি

এন=231

এন

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


1
এটি প্রতিটি কোর আরও কম বা কম নির্দিষ্ট পরিমাণে মেমরি স্তরের সমান্তরালতা সরবরাহ করে, যেমন একটি নির্দিষ্ট সময়ে 10 x [] লোড প্রক্রিয়াতে সরবরাহ করে এটি এটিকে দেখতেও সহায়তা করতে পারে। ভাগ করা এল 3-তে হিট হওয়ার 0.5% সম্ভাবনা সহ, একটি একক থ্রেডে 0.995 ** 10 (95 +%) সুযোগ থাকবে মূল মেমরির প্রতিক্রিয়ার জন্য অপেক্ষা করা সমস্ত লোডের প্রয়োজন হয়। মোট x০ x [] মুলতুবি পাঠ্য সরবরাহকারী 6 টি কোরের সাথে, প্রায় 26% সম্ভাবনা রয়েছে যে কমপক্ষে একটি পঠন L3 এ পড়বে। এছাড়াও, যত বেশি এমএলপি, মেমরি নিয়ামক তত বেশি প্রকৃত ব্যান্ডউইথকে বাড়ানোর জন্য অ্যাক্সেসগুলি নির্ধারণ করতে পারে।
পল এ। ক্লেটন

5

আমি নিজেই __ বিল্টিন_প্রিফেট () চেষ্টা করার সিদ্ধান্ত নিয়েছি। অন্যরা তাদের মেশিনে এটি পরীক্ষা করতে চাইলে আমি উত্তর হিসাবে এখানে পোস্ট করছি। ফলাফলগুলি জুক্কার বর্ণনার কাছাকাছি: চলমান সময়টিতে প্রায় 20% হ্রাস যখন 20 উপাদানকে পূর্বের দিকে 0 উপাদান এগিয়ে নিয়ে যাওয়া যায়।

ফলাফল:

prefetch =   0, time = 1.58000
prefetch =   1, time = 1.47000
prefetch =   2, time = 1.39000
prefetch =   3, time = 1.34000
prefetch =   4, time = 1.31000
prefetch =   5, time = 1.30000
prefetch =   6, time = 1.27000
prefetch =   7, time = 1.28000
prefetch =   8, time = 1.26000
prefetch =   9, time = 1.27000
prefetch =  10, time = 1.27000
prefetch =  11, time = 1.27000
prefetch =  12, time = 1.30000
prefetch =  13, time = 1.29000
prefetch =  14, time = 1.30000
prefetch =  15, time = 1.28000
prefetch =  16, time = 1.24000
prefetch =  17, time = 1.28000
prefetch =  18, time = 1.29000
prefetch =  19, time = 1.25000
prefetch =  20, time = 1.24000
prefetch =  19, time = 1.26000
prefetch =  18, time = 1.27000
prefetch =  17, time = 1.26000
prefetch =  16, time = 1.27000
prefetch =  15, time = 1.28000
prefetch =  14, time = 1.29000
prefetch =  13, time = 1.26000
prefetch =  12, time = 1.28000
prefetch =  11, time = 1.30000
prefetch =  10, time = 1.31000
prefetch =   9, time = 1.27000
prefetch =   8, time = 1.32000
prefetch =   7, time = 1.31000
prefetch =   6, time = 1.30000
prefetch =   5, time = 1.27000
prefetch =   4, time = 1.33000
prefetch =   3, time = 1.38000
prefetch =   2, time = 1.41000
prefetch =   1, time = 1.41000
prefetch =   0, time = 1.59000

কোড:

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

void cracker(int *y, int *x, int *p, int n, int pf) {
    int i;
    int saved = pf;  /* let compiler optimize address computations */

    for (i = 0; i < n; i++) {
        __builtin_prefetch(&x[p[i+saved]]);
        y[i] += x[p[i]];
    }
}

int main(void) {
    int n = 50000000;
    int *x, *y, *p, i, pf, k;
    clock_t start, stop;
    double elapsed;

    /* set up arrays */
    x = malloc(sizeof(int)*n);
    y = malloc(sizeof(int)*n);
    p = malloc(sizeof(int)*n);
    for (i = 0; i < n; i++)
        p[i] = rand()%n;

    /* warm-up exercise */
    cracker(y, x, p, n, pf);

    k = 20;
    for (pf = 0; pf < k; pf++) {
        start = clock();
        cracker(y, x, p, n, pf);
        stop = clock();
        elapsed = ((double)(stop-start))/CLOCKS_PER_SEC;
        printf("prefetch = %3d, time = %.5lf\n", pf, elapsed);
    }
    for (pf = k; pf >= 0; pf--) {
        start = clock();
        cracker(y, x, p, n, pf);
        stop = clock();
        elapsed = ((double)(stop-start))/CLOCKS_PER_SEC;
        printf("prefetch = %3d, time = %.5lf\n", pf, elapsed);
    }

    return 0;
}

4
  1. ডিডিআর 3 অ্যাক্সেসটি সত্যই পাইপলাইনযুক্ত। http://www.eng.utah.edu/~cs7810/pres/dram-cs7810-protocolx2.pdf 20 এবং 24 স্লাইডগুলি দেখায় যে পাইপলাইনযুক্ত পড়া অপারেশনের সময় মেমরি বাসে কী ঘটে।

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

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

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

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


__ বিল্টিন_প্রিফেক খুব আশাব্যঞ্জক মনে হচ্ছে। দুর্ভাগ্যক্রমে, আমার দ্রুত পরীক্ষাগুলিতে এটি একক থ্রেড কর্মক্ষমতা (<10%) এর সাথে খুব বেশি সাহায্য করবে বলে মনে হয় না। এই ধরণের অ্যাপ্লিকেশনটিতে আমার কত বড় গতির উন্নতি আশা করা উচিত?
Jukka Suomela

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

আমার প্রথম প্রয়াসটি একটি অ্যারেতে সঞ্চিত একটি স্থির র্যান্ডম ক্রম তৈরি করছিল, তারপরে প্রিফেচ ( gist.github.com/osimola/7917602 ) সহ এবং না করে সেই ক্রমে পুনরাবৃত্তি করছিল । এটি একটি কোর আই 5 এর প্রায় 2% এর পার্থক্য নিয়ে এসেছে। প্রিফেটের মতো শব্দগুলি মোটেও কাজ করে না বা হার্ডওয়্যার প্রেডিক্টর ইন্ডিরিশান বুঝতে পারে।
জুহানী সিমোলা

1
সুতরাং, এর জন্য পরীক্ষার জন্য, দ্বিতীয় প্রয়াস ( gist.github.com/osimola/7917568 ) একটি স্থির এলোমেলো বীজ দ্বারা উত্পাদিত ক্রমানুসারে মেমরিটি অ্যাক্সেস করে। এবার প্রিফেচিং সংস্করণটি প্রাক-প্রিফেচিংয়ের চেয়ে প্রায় 2 গুণ দ্রুত এবং 1 ধাপ এগিয়ে প্রিফেচিংয়ের চেয়ে 3 গুণ দ্রুত ছিল। মনে রাখবেন যে প্রিফেচিং সংস্করণটি মেমরি অ্যাক্সেসের জন্য অ-প্রিফেচিং সংস্করণের চেয়ে আরও বেশি গণনা করে।
জুহানী সিমোলা

এটি মেশিন নির্ভর বলে মনে হচ্ছে। আমি নীচে প্যাট মরিনের কোডটি চেষ্টা করেছি (যে খ্যাতি আমার নেই বলে সে পোস্টটিতে মন্তব্য করতে পারছি না) এবং আমার ফলাফলটি বিভিন্ন প্রিফেচ মানগুলির জন্য 1.3% এর মধ্যে is
জুহানী সিমোলা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.