performance - ইন্টেল স্কাইলেকে স্টোর লুপের জন্য অপ্রত্যাশিতভাবে দুর্বল এবং অদ্ভুতভাবে বিমোডাল পারফরম্যান্স




assembly optimization (2)

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

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

বিবরণ এবং ছবি

64-বাইট স্ট্রাইড

মূল প্রশ্নটি ইচ্ছামত 16 টি স্ট্রাইড ব্যবহার করেছে, তবে আসুন শুরু করা যাক সম্ভবত সবচেয়ে সহজ কেস: of৪ এর ধাপ, অর্থাৎ একটি সম্পূর্ণ ক্যাশে লাইন। এটি দেখা যাচ্ছে যে কোনও প্রভাবের সাথে বিভিন্ন প্রভাব দৃশ্যমান হয় তবে 64৪ টি প্রতিটি ধাপে একটি এল 2 ক্যাশে মিস নিশ্চিত করে এবং কিছু পরিবর্তনশীলগুলি সরিয়ে দেয়।

আসুন এখনের জন্য দ্বিতীয় স্টোরটিও সরিয়ে ফেলুন - সুতরাং আমরা কেবলমাত্র 64K মেমরির উপরে একক 64৪-বাইট স্ট্রাইডেড স্টোরটি পরীক্ষা করছি:

top:
mov    BYTE PTR [rdx],al
add    rdx,0x40
sub    rdi,0x1
jne    top

এটি উপরের মতো একই জোতাতে চালানো, আমি প্রায় 3.05 চক্র / স্টোর 2 পাই, যদিও আমি যা দেখতে অভ্যস্ত তা তুলনায় বেশ কিছুটা বৈকল্পিক রয়েছে (- আপনি সেখানে একটি 3.0ও খুঁজে পেতে পারেন)।

সুতরাং আমরা ইতিমধ্যে জানি আমরা সম্ভবত স্থায়ী স্টোরগুলির জন্য বিশুদ্ধরূপে L2 1 এ এর চেয়ে ভাল আর করতে যাচ্ছি না। স্কাইলেকের স্পষ্টতই L1 এবং L2 এর মধ্যে একটি 64 বাইট থ্রুটপুট রয়েছে, স্টোরগুলির প্রবাহের ক্ষেত্রে, ব্যান্ডউইথকে এল 1 থেকে উচ্ছেদ করার জন্য এবং নতুন লাইনটি এল 1-এ লোড করার জন্য ভাগ করতে হবে। ৩ টি চক্র যুক্তিসঙ্গত বলে মনে হয় যদি এটিতে প্রতিটি 1 টি চক্র লাগে (ক) এল 1 থেকে এল 2 পর্যন্ত নোংরা শিকারের লাইনটি উচ্ছেদ করে (খ) এল 2 থেকে নতুন লাইনের সাথে এল 1 আপডেট করুন এবং (গ) স্টোরকে এল 1 তে প্রতিশ্রুতিবদ্ধ।

যখন আপনি যুক্ত করবেন তখন লুপটিতে একই ক্যাশে লাইনে (পরবর্তী বাইটে, যদিও এটি বিবেচনার বিষয় নয়) কী লিখবে? এটার মত:

top:
mov    BYTE PTR [rdx],al
mov    BYTE PTR [rdx+0x1],al
add    rdx,0x40
sub    rdi,0x1
jne    top

উপরের লুপটির জন্য পরীক্ষার জোয়ারের 1000 রানের সময়কালীন একটি হিস্টোগ্রাম এখানে রয়েছে:

  count   cycles/itr
      1   3.0
     51   3.1
      5   3.2
      5   3.3
     12   3.4
    733   3.5
    139   3.6
     22   3.7
      2   3.8
     11   4.0
     16   4.1
      1   4.3
      2   4.4

সুতরাং বেশিরভাগ সময়ে প্রায় 3.5 টি চক্র ক্লাস্টার হয়। তার মানে এই যে অতিরিক্ত স্টোরটি সময়টিতে 0.5 টি চক্র যুক্ত করেছে। এটি এমন কিছু হতে পারে যেমন স্টোর বাফার একই লাইনে থাকলে দুটি স্টোরকে L1 এ ড্রেন করতে সক্ষম হয়, তবে এটি কেবল প্রায় অর্ধেক সময় ঘটে।

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

নোট করুন 3.5 এর পরিবর্তে 3.1 এর আশেপাশে প্রায় 6% ফলাফলের শীর্ষস্থান রয়েছে। এটি একটি স্থির রাষ্ট্র হতে পারে যেখানে আমরা সর্বদা ভাগ্যবান পরিণতি পাই get ~ 4.0-4.1 ডলারে প্রায় 3% এর আরও একটি শীর্ষ রয়েছে - "সর্বদা দুর্ভাগ্য" বিন্যাস।

প্রথম এবং দ্বিতীয় স্টোরের মধ্যে বিভিন্ন অফসেট দেখে এই তত্ত্বটি পরীক্ষা করা যাক:

top:
mov    BYTE PTR [rdx + FIRST],al
mov    BYTE PTR [rdx + SECOND],al
add    rdx,0x40
sub    rdi,0x1
jne    top

আমরা 8 এর পদক্ষেপে 0 থেকে 256 পর্যন্ত FIRST এবং SECOND এর সমস্ত মান চেষ্টা করি ফলাফলগুলি উল্লম্ব অক্ষের সাথে পৃথক FIRST মান এবং অনুভূমিকের উপর SECOND সহ ফলাফল:

আমরা একটি সুনির্দিষ্ট প্যাটার্নটি দেখতে পাই - সাদা মানগুলি "দ্রুত" (1 এর অফসেটের জন্য উপরে উল্লিখিত 3.0-4.1 মানগুলির চারপাশে)। হলুদ মানগুলি 8 টি চক্র পর্যন্ত এবং 10 পর্যন্ত লাল হয় The বেগুনি রঙের আউটলিয়ার্স সর্বাধিক এবং সাধারণত এমন হয় যেখানে ওপিতে বর্ণিত "স্লো মোড" কিক হয় (সাধারণত 18.0 চক্র / ইটারে ক্লকিং হয়)। আমরা নিম্নলিখিতটি লক্ষ্য করি:

  • সাদা কোষের ধরণ থেকে, আমরা দেখতে পাচ্ছি যে দ্বিতীয় স্টোর একই ক্যাশে লাইনে বা প্রথম স্টোরের পরবর্তী সামনের হিসাবে যতক্ষণ না আমরা ততক্ষণ দ্রুত ~ 3.5 ডলার চক্রের ফলাফল পাই get এটি উপরের ধারণার সাথে সামঞ্জস্যপূর্ণ যে একই ক্যাশে লাইনের স্টোরগুলি আরও দক্ষতার সাথে পরিচালিত হয়। পরবর্তী ক্যাশে লাইনে দ্বিতীয় স্টোর থাকার কারণটি হ'ল প্রথম প্রথম অ্যাক্সেস ব্যতীত প্যাটার্নটি একইরকম হয়ে যায়: 0, 0, 1, 1, 2, 2, ... বনাম 0, 1, 1, 2, 2, ... - যেখানে দ্বিতীয় ক্ষেত্রে এটি দ্বিতীয় স্টোর যা প্রথম প্রতিটি ক্যাশে লাইন স্পর্শ করে। স্টোর বাফার যদিও যত্ন করে না। আপনি বিভিন্ন ক্যাশে লাইনে উঠার সাথে সাথে আপনি 0, 2, 1, 3, 2, ... মতো একটি প্যাটার্ন পাবেন এবং স্পষ্টতই এই স্তন্যপান হয়?

  • বেগুনি রঙের "আউটলিয়ার" কখনও সাদা অঞ্চলে উপস্থিত হয় না, তাই দৃশ্যত ইতিমধ্যে ধীরগতির মধ্যে দৃশ্যমান সীমাবদ্ধ রয়েছে (এবং এখানে ধীরে ধীরে এটি 2.5x ধীর করে দেয়: 8 থেকে 18 চক্র পর্যন্ত)।

আমরা কিছুটা জুম করে আরও বড় অফসেটগুলি দেখতে পারি:

একই বেসিক প্যাটার্নটি, যদিও আমরা দেখি যে দ্বিতীয় স্টোরটি আরও দূরে (সামনে বা পিছনে) প্রায় ~ 1700 বাইটের অফসেটে খারাপ না হওয়া পর্যন্ত কর্মক্ষমতা উন্নত হয় (সবুজ অঞ্চল)। এমনকি উন্নত অঞ্চলে আমরা কেবলমাত্র সর্বোচ্চ 5.8 চক্র / পুনরাবৃত্তিটি 3.5 এর একই লাইন পারফরম্যান্সের চেয়ে আরও খারাপ।

আপনি যদি 3 ধরণের স্টোর এগিয়ে চলে এমন কোনও ধরণের লোড বা প্রিফেচ নির্দেশনা যুক্ত করেন তবে সামগ্রিক ধীর গতিতে পারফরম্যান্স এবং "স্লো মোড" উভয়ই অদৃশ্য হয়ে যায়:

আপনি এটিকে 16 টি সমস্যা দ্বারা মূল প্রান্তে ফিরিয়ে আনতে পারেন - মূল লুপে যে কোনও প্রকারের প্রিফেচ বা লোড, দূরত্বের প্রায় বেশ সংবেদনশীল (এমনকি বাস্তবে এটি পিছনে থাকলেও), সমস্যাটি সংশোধন করে এবং আপনি ২.৩ চক্র / পুনরাবৃত্তি পান, 2.0 এর সর্বোত্তম সম্ভাবনামুলক আদর্শের কাছাকাছি এবং পৃথক লুপের সাথে দুটি স্টোরের যোগফল equal

সুতরাং মৌলিক নিয়মটি হল যে আনুষঙ্গিক লোড ছাড়াই L2 এ স্টোরগুলি আপনি সফ্টওয়্যারগুলির প্রিফেট করেন তার চেয়ে অনেক ধীরে ধীরে - যদি না পুরো স্ট্রিম স্ট্রিমটি একক অনুক্রমিক প্যাটার্নে ক্যাশে লাইনগুলিতে অ্যাক্সেস করে। এটি এইর মতো লিনিয়ার প্যাটার্নটি এসডাব্লু প্রিফেচ থেকে কখনই উপকৃত হয় না idea

আমার সত্যিকার অর্থে একটি বিবর্ধিত ব্যাখ্যা নেই, তবে এতে এই কারণগুলি অন্তর্ভুক্ত থাকতে পারে:

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

ইন্টেল ফোরামগুলিতে ডাঃ ম্যাককাল্পিনের এই মন্তব্যগুলিও বেশ আকর্ষণীয়।

0 কেবলমাত্র L2 স্ট্রিমার অক্ষম হওয়ার সাথে কেবলমাত্র অর্জনযোগ্য কারণ অন্যথায় L2- এ অতিরিক্ত যুক্তি এটিকে ধীরে ধীরে ৩.৫ চক্রের প্রায় 1 লাইন করে দেয়।

1 স্টোরগুলির সাথে এটির বিপরীতে করুন, যেখানে আমি প্রতি লোকে প্রায় লোড 1.5 ডলার চক্র পাই, প্রতি চক্রের জন্য ~ 43 বাইটের অন্তর্নির্মিত ব্যান্ডউইথের জন্য। এটি নিখুঁত ধারণা দেয়: এল 1 <-> এল 2 ব্যান্ডউইথটি 64 বাইট, তবে ধরে নিচ্ছেন যে এল 1 হয় এল 2 থেকে একটি লাইন গ্রহণ করছে বা কোর থেকে প্রতিটি চক্রের (তবে সমান্তরালে উভয়ই নয়) সার্ভিসিং লোড অনুরোধ করছে তবে আপনার কাছে 3 চক্র রয়েছে বিভিন্ন লো -2 লাইনে দুটি লোডের জন্য: এল 2 থেকে লাইনগুলি গ্রহণ করার জন্য 2 চক্র এবং দুটি লোডের নির্দেশকে সন্তুষ্ট করতে 1 চক্র।

2 প্রিফেচিং বন্ধ রয়েছে । দেখা যাচ্ছে যে, L2 প্রিফেটচার L2 ক্যাশে অ্যাক্সেসের জন্য প্রতিযোগিতা করে যখন এটি স্ট্রিমিং অ্যাক্সেস সনাক্ত করে: যদিও এটি সর্বদা প্রার্থীর লাইনগুলি খুঁজে পায় এবং L3 এ যায় না, এটি কোডটি ধীর করে দেয় এবং পরিবর্তনশীলতা বাড়ায়। উপসংহারগুলি সাধারণত প্রিফেচিংয়ের সাথে ধারণ করে তবে সবকিছু কিছুটা ধীরে ধীরে হয় (এখানে প্রিফেচিং সহ ফলাফলগুলির একটি বৃহত ব্লব - আপনি প্রতি লোড সম্পর্কে প্রায় 3.3 চক্র দেখেন, তবে প্রচুর পরিবর্তনশীলতা সহ)।

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

আমি একটি সাধারণ স্টোর লুপের অপ্রত্যাশিতভাবে দুর্বল পারফরম্যান্সটি দেখছি যার দুটি স্টোর রয়েছে: একটি 16 বাইটের ফরোয়ার্ড স্ট্রাইড সহ একটি এবং সর্বদা একই স্থানে 1 , এর মতো:

volatile uint32_t value;

void weirdo_cpp(size_t iters, uint32_t* output) {

    uint32_t x = value;
    uint32_t          *rdx = output;
    volatile uint32_t *rsi = output;
    do {
        *rdx    = x;
        *rsi = x;

        rdx += 4;  // 16 byte stride
    } while (--iters > 0);
}

সমাবেশে এই লুপটি সম্ভবত 3 টির মতো দেখাচ্ছে:

weirdo_cpp:

...

align 16
.top:
    mov    [rdx], eax  ; stride 16
    mov    [rsi], eax  ; never changes

    add    rdx, 16

    dec    rdi
    jne    .top

    ret

অ্যাক্সেস করা মেমরি অঞ্চলটি যখন এল 2 এ থাকে তখন আমি প্রত্যাশা করব যে এটি পুনরাবৃত্তির জন্য 3 টিরও কম চক্রের মধ্যে চলবে। দ্বিতীয় স্টোরটি একই স্থানে আঘাত করতে থাকে এবং একটি চক্র যুক্ত করা উচিত। প্রথম স্টোরটি বোঝায় যে এল 2 থেকে একটি লাইন আনা এবং সেইজন্য প্রতি 4 পুনরাবৃত্তিতে একবার লাইন সরিয়ে দেওয়া। আপনি কীভাবে এল 2 ব্যয়ের মূল্যায়ন করেন তা আমি নিশ্চিত নই, তবে আপনি যদি রক্ষণশীলভাবে অনুমানও করেন যে এল 1 নিম্নলিখিত প্রতিটি চক্রের মধ্যে কেবল একটির মতো করতে পারে: (ক) স্টোর কমিট করুন বা (খ) এল 2 বা (সি) থেকে একটি লাইন পাবেন এল 2 তে একটি লাইন উচ্ছেদ করুন, আপনি স্ট্রাইড -16 স্টোর স্ট্রিমের জন্য 1 + 0.25 + 0.25 = 1.5 চক্রের মতো কিছু পাবেন।

প্রকৃতপক্ষে, আপনি একটি স্টোর মন্তব্য করেছেন যা আপনি কেবল প্রথম স্টোরের জন্য পুনরাবৃত্তি প্রতি ~ 1.25 চক্র এবং দ্বিতীয় স্টোরের জন্য পুনরাবৃত্তি প্রতি ~ 1.01 চক্র পান, সুতরাং পুনরাবৃত্তির প্রতি 2.5 চক্রটি রক্ষণশীল অনুমান হিসাবে মনে হয়।

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

Estimated CPU speed:  2.60 GHz
output size     :   64 KiB
output alignment:   32
 3.90 cycles/iter,  1.50 ns/iter, cpu before: 0, cpu after: 0
 3.90 cycles/iter,  1.50 ns/iter, cpu before: 0, cpu after: 0
 3.90 cycles/iter,  1.50 ns/iter, cpu before: 0, cpu after: 0
 3.89 cycles/iter,  1.49 ns/iter, cpu before: 0, cpu after: 0
 3.90 cycles/iter,  1.50 ns/iter, cpu before: 0, cpu after: 0
 4.73 cycles/iter,  1.81 ns/iter, cpu before: 0, cpu after: 0
 7.33 cycles/iter,  2.81 ns/iter, cpu before: 0, cpu after: 0
 7.33 cycles/iter,  2.81 ns/iter, cpu before: 0, cpu after: 0
 7.34 cycles/iter,  2.81 ns/iter, cpu before: 0, cpu after: 0
 7.26 cycles/iter,  2.80 ns/iter, cpu before: 0, cpu after: 0
 7.28 cycles/iter,  2.80 ns/iter, cpu before: 0, cpu after: 0
 7.31 cycles/iter,  2.81 ns/iter, cpu before: 0, cpu after: 0
 7.29 cycles/iter,  2.81 ns/iter, cpu before: 0, cpu after: 0
 7.28 cycles/iter,  2.80 ns/iter, cpu before: 0, cpu after: 0
 7.29 cycles/iter,  2.80 ns/iter, cpu before: 0, cpu after: 0
 7.27 cycles/iter,  2.80 ns/iter, cpu before: 0, cpu after: 0
 7.30 cycles/iter,  2.81 ns/iter, cpu before: 0, cpu after: 0
 7.30 cycles/iter,  2.81 ns/iter, cpu before: 0, cpu after: 0
 7.28 cycles/iter,  2.80 ns/iter, cpu before: 0, cpu after: 0
 7.28 cycles/iter,  2.80 ns/iter, cpu before: 0, cpu after: 0

দুটি জিনিস এখানে অদ্ভুত।

প্রথমটি হ'ল বিমোডাল সময়: একটি দ্রুত মোড এবং একটি ধীর মোড । আমরা স্লো মোডে শুরু করি পুনরুক্তি প্রতি প্রায় 7.3 চক্র নিয়ে, এবং কোনও কোনও সময়ে পুনরাবৃত্তির প্রায় 3.9 চক্রে স্থানান্তর করি। এই আচরণটি সামঞ্জস্যপূর্ণ এবং পুনরুত্পাদনযোগ্য এবং দুটি সময় সর্বদা দুটি মানকে ঘিরে বেশ সামঞ্জস্যপূর্ণ থাকে। ধীরগতির মোড থেকে দ্রুত মোড এবং অন্যান্য উপায়ে (এবং কখনও কখনও এক রানে একাধিক ট্রানজিশন) উভয় দিকেই স্থানান্তর দেখা যায়।

অন্য অদ্ভুত জিনিস হ'ল সত্যই খারাপ অভিনয়। এমনকি দ্রুত মোডে , প্রায় 3.9 চক্রের পারফরম্যান্সটি 1.0 + 1.3 = 2.3 চক্রের চেয়ে সবচেয়ে খারাপ যা আপনি একক স্টোরের সাথে প্রতিটি কেস একসাথে যুক্ত করে প্রত্যাশা করতেন (এবং ধরে নিবেন যে একেবারে শূন্য কাজ করেছে ওভারল্যাপ করা যাবে যখন উভয় স্টোর লুপে থাকবে)। ধীর মোডে , প্রথম নীতিগুলির ভিত্তিতে আপনি যা আশা করেছিলেন তার তুলনায় পারফরম্যান্স ভয়ঙ্কর: 2 স্টোর করতে 7.3 চক্র লাগছে, এবং যদি আপনি এটি এল 2 স্টোরের ব্যান্ডউইথের শর্তায় রাখেন, তবে এটি L2 স্টোরের জন্য প্রায় 29 চক্র (যেহেতু আমরা প্রতি 4 পুনরাবৃত্তিতে কেবল একটি সম্পূর্ণ ক্যাশে লাইন সংরক্ষণ করুন)।

স্কাইলেকে এল 1 এবং এল 2 এর মধ্যে একটি 64 বি / চক্রের থ্রুটপুট হিসাবে recorded করা হয়েছে, যা এখানে পর্যবেক্ষণ হওয়া থ্রুপুট (প্রায় 2 বাইট / স্লো মোডে চক্র) এর চেয়ে অনেক বেশি।

দুর্বল থ্রুটপুট এবং বিমোডাল পারফরম্যান্সটি কী ব্যাখ্যা করে এবং আমি এড়াতে পারি?

আমি অন্যান্য কৌতূহলও বোধ করি যদি এটি অন্যান্য আর্কিটেকচার এমনকি অন্য স্কাইলাক বাক্সেও পুনরুত্পাদন করে। মন্তব্যগুলিতে স্থানীয় ফলাফল অন্তর্ভুক্ত নির্দ্বিধায়।

আপনি গিথুবে পরীক্ষার কোড এবং জোতা খুঁজে পেতে পারেন। লিনাক্স বা ইউনিক্সের মতো প্ল্যাটফর্মগুলির জন্য একটি Makefile তবে উইন্ডোজেও এটি তুলনামূলকভাবে সহজ হওয়া উচিত। আপনি যদি asm nasm চালাতে চান তবে আপনার সমাবেশ 4 এর জন্য nasm বা yasm প্রয়োজন হবে - যদি আপনার কাছে না থাকে তবে আপনি কেবল সি ++ সংস্করণ চেষ্টা করতে পারেন।

সম্ভাবনা দূর করে

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

  • প্রান্তিককরণের উপাদানগুলি: আউটপুট অ্যারেটি 16 বাইট প্রান্তিককরণ করা হয়েছে এবং আমি 2MB বিনা প্রান্তিককরণের চেষ্টা করেছি। ডিফল্ট নির্মূলকরণ দ্বারাও নির্মূল করা
  • মেশিনে অন্যান্য প্রক্রিয়াগুলির সাথে যুক্তি: প্রভাবটি কমবেশি অলস মেশিনে এবং এমনকি ভারী বোঝা একটিতে (যেমন, stress -vm 4 ব্যবহার করে) এ দেখা যায়। L2- তে ফিট করে বেঞ্চমার্ক নিজেই সম্পূর্ণ স্থানীয়-হওয়া উচিত, এবং perf নিশ্চিত করে যে প্রতি পুনরাবৃত্তির জন্য খুব কম এল 2 মিস আছে (প্রায় 300 মিসরের প্রতি 300 টি পুনরুক্তি সম্ভবত মুদ্রণের সাথে সম্পর্কিত) 1
  • টার্বোবস্ট: তিনটি মেগাহার্জ রিডিংয়ের দ্বারা নিশ্চিত হওয়া টার্বোবুস্ট সম্পূর্ণ অক্ষম।
  • বিদ্যুৎ-সঞ্চয়কারী স্টাফ: পারফরম্যান্স গভর্নর performance মোডে intel_pstate । পরীক্ষার সময় কোনও ফ্রিকোয়েন্সি বৈকল্পিকতা পরিলক্ষিত হয় না (সিপিইউ মূলত 2.59 গিগাহার্টজ লক থাকে)।
  • টিএলবি এফেক্টস: আউটপুট বাফারটি 2 এমবি বিশাল পৃষ্ঠায় অবস্থিত থাকলেও প্রভাবটি উপস্থিত থাকে। যাই হোক না কেন, 128K আউটপুট বাফারের চেয়ে 64 4k টিএলবি বেশি প্রবেশ করে। perf কোনও বিশেষভাবে অদ্ভুত টিএলবি আচরণের খবর দেয় না।
  • 4 কে অ্যালিয়াসিং: এই বেঞ্চমার্কের আরও পুরানো জটিল সংস্করণগুলি 4k অ্যালিয়াসিং দেখিয়েছিল তবে বেঞ্চমার্কে কোনও বোঝা নেই বলে এটি মুছে ফেলা হয়েছে (এটি এমন লোড যা ভুলভাবে উড়ানের আগে স্টোর করতে পারে)। ডিফল্ট নির্মূলকরণ দ্বারাও নির্মূল করা
  • L2 সাহচর্য বিরোধগুলি: ডিফল্ট নির্মূলকরণের মাধ্যমে এবং এটি 2MB পৃষ্ঠাগুলির সাথেও সরে যায় না, যেখানে আমরা নিশ্চিত হতে পারি যে আউটপুট বাফারটি শারীরিক স্মৃতিতে রৈখিকভাবে ছাঁটাই হয়েছে।
  • হাইপারথ্রেডিং প্রভাব: এইচটি অক্ষম করা আছে।
  • উপস্থাপনা: কেবলমাত্র দুটি প্রিফেটচারই এখানে যুক্ত হতে পারে ("ডিসিইউ", ওরফে এল 1 <-> এল 2 প্রিফেটচারস), যেহেতু সমস্ত ডেটা এল 1 বা এল 2 তে থাকে তবে সমস্ত প্রিফেটচারার সক্ষম বা সমস্ত অক্ষম থাকাতে পারফরম্যান্স একই হয়।
  • বাধা: বিঘ্নিত গণনা এবং ধীর মোডের মধ্যে কোনও সম্পর্ক নেই। মোট বাধা সীমিত সংখ্যক রয়েছে, বেশিরভাগ ক্লক টিক্স।

toplev.py

আমি toplev.py ব্যবহার toplev.py যা ইন্টেলের শীর্ষ ডাউন বিশ্লেষণ পদ্ধতিটি প্রয়োগ করে এবং এতে কোনও বিস্ময়ের বিষয় নয় যে এটি স্টোর বাউন্ড হিসাবে বেঞ্চমার্ককে চিহ্নিত করে:

BE             Backend_Bound:                                                      82.11 % Slots      [  4.83%]
BE/Mem         Backend_Bound.Memory_Bound:                                         59.64 % Slots      [  4.83%]
BE/Core        Backend_Bound.Core_Bound:                                           22.47 % Slots      [  4.83%]
BE/Mem         Backend_Bound.Memory_Bound.L1_Bound:                                 0.03 % Stalls     [  4.92%]
    This metric estimates how often the CPU was stalled without
    loads missing the L1 data cache...
    Sampling events:  mem_load_retired.l1_hit:pp mem_load_retired.fb_hit:pp
BE/Mem         Backend_Bound.Memory_Bound.Store_Bound:                             74.91 % Stalls     [  4.96%] <==
    This metric estimates how often CPU was stalled  due to
    store memory accesses...
    Sampling events:  mem_inst_retired.all_stores:pp
BE/Core        Backend_Bound.Core_Bound.Ports_Utilization:                         28.20 % Clocks     [  4.93%]
BE/Core        Backend_Bound.Core_Bound.Ports_Utilization.1_Port_Utilized:         26.28 % CoreClocks [  4.83%]
    This metric represents Core cycles fraction where the CPU
    executed total of 1 uop per cycle on all execution ports...
               MUX:                                                                 4.65 %           
    PerfMon Event Multiplexing accuracy indicator

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

L1-L2 মিথস্ক্রিয়ায় জড়িত কিছু বিষয়গুলির একটি যুক্তিসঙ্গত সংক্ষিপ্তসার Here's

ফেব্রুয়ারী 2019 আপডেট করুন: আমি আর পারফরম্যান্সের "বিমোডাল" অংশটি পুনরুত্পাদন করতে পারি না: আমার জন্য একই আই 7-6700 এইচকিউ বক্সে, কর্মক্ষমতা এখন সর্বদা একই ক্ষেত্রে ধীর এবং খুব ধীর বিমোডাল পারফরম্যান্স প্রয়োগ হয়, অর্থাৎ , প্রতি লাইনে প্রায় 16-20 চক্রের ফলাফল সহ:

এই পরিবর্তনটি আগস্ট 2018 এর স্কাইলেক মাইক্রোকোড আপডেট, রিভিশন 0xC6-এ চালু হয়েছে বলে মনে হচ্ছে। পূর্বের মাইক্রোকোড, 0xC2 প্রশ্নটিতে বর্ণিত আসল আচরণ দেখায়।

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

3 বিশেষত, আপনি হাত দিয়ে অ্যাসেম্বলিটি লিখলে, বা আপনি এটি gcc -O1 (সংস্করণ gcc -O1 ) দিয়ে সংকলন করেন এবং সম্ভবত সবচেয়ে যুক্তিসঙ্গত সংকলক (বেশিরভাগ মৃত দ্বিতীয়টি ডুবে যাওয়া এড়াতে volatile ব্যবহৃত হয়) লুপের বাইরে সঞ্চয় করুন)।

4 কোনও সন্দেহ নেই যে আপনি এটিকে এমএএসএম সিনট্যাক্সে কয়েকটি ছোটখাটো সম্পাদনা দিয়ে রূপান্তর করতে পারেন যেহেতু সমাবেশটি তুচ্ছ tri টান অনুরোধ গৃহীত।


স্যান্ডি ব্রিজটিতে "এল 1 ডেটা হার্ডওয়্যার প্রি-ফেচারারস" রয়েছে। এর অর্থ হ'ল প্রথম দিকে যখন আপনি আপনার স্টোরটি করেন তখন সিপিইউকে এল 2 থেকে এল 1 এ ডেটা আনতে হয়; তবে এটি বেশ কয়েকবার হওয়ার পরে হার্ডওয়্যার প্রি-ফ্যাচারটি দুর্দান্ত ক্রমবর্ধমান প্যাটার্নটি লক্ষ্য করে এবং আপনার জন্য L2 থেকে এল 1 এ প্রি-আনতে শুরু করে, যাতে আপনার কোডটি সম্পাদনের আগে ডেটা হয় এল 1 বা "এল 1 এর অর্ধেক" দোকান।





x86-64