আমি কীভাবে চক্রের তাত্ত্বিক সর্বোচ্চ 4 টি এফএলওপি অর্জন করব?


642

একটি আধুনিক x86-64 ইন্টেল সিপিইউতে চক্র প্রতি 4 ভাসমান পয়েন্ট অপারেশনগুলির (ডাবল যথার্থতা) তাত্ত্বিক শিখর সম্পাদন কীভাবে করা যায়?

যতদূর আমি বুঝতে পেরেছি এটি কোনও এসএসইর জন্য তিনটি চক্র addএবং mulআধুনিক ইন্টেল সিপিইউগুলির বেশিরভাগের জন্য সম্পূর্ণ করার জন্য পাঁচটি চক্র লাগে (উদাহরণস্বরূপ অ্যাগ্রার ফগের 'নির্দেশাবলী সারণী' দেখুন )। পাইপলাইনের কারণে addযদি অ্যালগরিদমে কমপক্ষে তিনটি স্বতন্ত্র সংক্ষেপ থাকে তবে প্রতি চক্রের একটিতে একটির একটি থ্রুপুট পেতে পারে । যেহেতু এটি প্যাকড addpdপাশাপাশি স্কেলারের addsdসংস্করণগুলির ক্ষেত্রেও সত্য এবং এসএসই রেজিস্টারগুলিতে দু'জনের doubleথ্রুপুট চক্র প্রতি দুটি ফ্লপ হতে পারে।

তদ্ব্যতীত, এটি মনে হয় (যদিও আমি এ সম্পর্কে কোনও সঠিক দলিল দেখিনি) addএর এবং mulচক্র প্রতি চারটি ফ্লপের একটি তাত্ত্বিক সর্বোচ্চ থ্রুপুট প্রদান সমান্তরালভাবে সম্পাদন করা যেতে পারে।

তবে, আমি সাধারণ সি / সি ++ প্রোগ্রামের মাধ্যমে সেই সম্পাদনাটির প্রতিলিপি করতে সক্ষম হইনি। আমার সর্বোত্তম প্রচেষ্টার ফলে প্রায় ২. about ফ্লপ / চক্র হয়েছিল। যদি কেউ একটি সাধারণ সি / সি ++ বা এসেম্বলারের প্রোগ্রামে অবদান রাখতে পারেন যা শিখর পারফরম্যান্স প্রদর্শন করে যা প্রশংসিত হবে।

আমার প্রচেষ্টা:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>

double stoptime(void) {
   struct timeval t;
   gettimeofday(&t,NULL);
   return (double) t.tv_sec + t.tv_usec/1000000.0;
}

double addmul(double add, double mul, int ops){
   // Need to initialise differently otherwise compiler might optimise away
   double sum1=0.1, sum2=-0.1, sum3=0.2, sum4=-0.2, sum5=0.0;
   double mul1=1.0, mul2= 1.1, mul3=1.2, mul4= 1.3, mul5=1.4;
   int loops=ops/10;          // We have 10 floating point operations inside the loop
   double expected = 5.0*add*loops + (sum1+sum2+sum3+sum4+sum5)
               + pow(mul,loops)*(mul1+mul2+mul3+mul4+mul5);

   for (int i=0; i<loops; i++) {
      mul1*=mul; mul2*=mul; mul3*=mul; mul4*=mul; mul5*=mul;
      sum1+=add; sum2+=add; sum3+=add; sum4+=add; sum5+=add;
   }
   return  sum1+sum2+sum3+sum4+sum5+mul1+mul2+mul3+mul4+mul5 - expected;
}

int main(int argc, char** argv) {
   if (argc != 2) {
      printf("usage: %s <num>\n", argv[0]);
      printf("number of operations: <num> millions\n");
      exit(EXIT_FAILURE);
   }
   int n = atoi(argv[1]) * 1000000;
   if (n<=0)
       n=1000;

   double x = M_PI;
   double y = 1.0 + 1e-8;
   double t = stoptime();
   x = addmul(x, y, n);
   t = stoptime() - t;
   printf("addmul:\t %.3f s, %.3f Gflops, res=%f\n", t, (double)n/t/1e9, x);
   return EXIT_SUCCESS;
}

সংকলিত

g++ -O2 -march=native addmul.cpp ; ./a.out 1000

একটি ইনটেল কোর i5-750, 2.66 গিগাহার্টজ উপরের আউটপুট উত্পাদন করে।

addmul:  0.270 s, 3.707 Gflops, res=1.326463

অর্থাৎ প্রতি চক্রের প্রায় 1.4 ফ্লপ। g++ -S -O2 -march=native -masm=intel addmul.cppমূল লুপের সাথে এসেম্বলারের কোডটি দেখানো আমার কাছে একরকম অনুকূল বলে মনে হচ্ছে:

.L4:
inc    eax
mulsd    xmm8, xmm3
mulsd    xmm7, xmm3
mulsd    xmm6, xmm3
mulsd    xmm5, xmm3
mulsd    xmm1, xmm3
addsd    xmm13, xmm2
addsd    xmm12, xmm2
addsd    xmm11, xmm2
addsd    xmm10, xmm2
addsd    xmm9, xmm2
cmp    eax, ebx
jne    .L4

প্যাক করা সংস্করণগুলি সহ স্কেলার সংস্করণগুলি পরিবর্তন করা ( addpdএবংmulpd ) সম্পাদনের সময় পরিবর্তন না করেই ফ্লপ গণনা দ্বিগুণ করবে এবং তাই আমি প্রতি চক্রের মাত্র 2.8 ফ্লপের সংক্ষিপ্ততা পাব। একটি সাধারণ উদাহরণ যা চক্র প্রতি চারটি ফ্লপ অর্জন করে?

মাইস্টিয়াল দ্বারা দুর্দান্ত ছোট প্রোগ্রাম; এখানে আমার ফলাফল (যদিও কয়েক সেকেন্ডের জন্য চালানো হবে):

  • gcc -O2 -march=nocona: 10.66 জিএফলপগুলির মধ্যে 5.6 জিএফলপস (2.1 ফ্লপ / চক্র)
  • cl /O2, ওপেনপ্প সরানো হয়েছে: 10.16 জিএফলপগুলির মধ্যে 10.1 জিএফলপস (3.8 ফ্লপ / চক্র)

এগুলি কিছুটা জটিল বলে মনে হচ্ছে তবে এ পর্যন্ত আমার সিদ্ধান্তগুলি:

  • gcc -O2বিকল্পের লক্ষ্যে addpdএবং mulpdযদি সম্ভব হয় তবে এর লক্ষ্য সহ স্বাধীন ভাসমান পয়েন্ট ক্রমের ক্রম পরিবর্তন করে । একই প্রযোজ্য gcc-4.6.2 -O2 -march=core2

  • gcc -O2 -march=nocona C ++ উত্স হিসাবে সংজ্ঞায়িত হিসাবে ভাসমান পয়েন্ট অপারেশনগুলির ক্রমকে মনে হচ্ছে।

  • cl /O2উইন্ডোজ 7 -এর এসডিকে থেকে -৪-বিট সংকলকটি স্বয়ংক্রিয়ভাবে লুপ-আনرولোলিং করে এবং অপারেশনগুলি চেষ্টা করে চেষ্টা করে মনে হয় যাতে তিনটির addpdবিকল্প তিনটির গোষ্ঠী তিনটি mulpd(ভাল, কমপক্ষে আমার সিস্টেমে এবং আমার সাধারণ প্রোগ্রামের জন্য) ।

  • আমার কোর আই 5 750 ( নেহালেম আর্কিটেকচার ) অ্যাড এবং মুলের বিকল্প পরিবর্তন পছন্দ করে না এবং উভয় ক্রিয়াকে সমান্তরালে চালাতে অক্ষম বলে মনে হচ্ছে। যাইহোক, 3 এর মধ্যে গ্রুপ করা থাকলে এটি হঠাৎ ম্যাজিকের মতো কাজ করে।

  • অন্যান্য আর্কিটেকচার (সম্ভবত স্যান্ডি ব্রিজ এবং অন্যান্য) বিধানসভা কোডে বিকল্প হিসাবে যদি তারা সমস্যা ছাড়াই সমান্তরালে অ্যাড / মুল চালাতে সক্ষম হয় বলে মনে হয়।

  • যদিও স্বীকার করা কঠিন, তবে আমার সিস্টেমে আমার সিস্টেমের cl /O2জন্য নিম্ন-স্তরের অপ্টিমাইজিং অপারেশনগুলিতে আরও ভাল কাজ করে এবং উপরের সামান্য সি ++ উদাহরণের জন্য শিখর পারফরম্যান্সের কাছাকাছি অর্জন করে। আমি 1.85-2.01 এর মধ্যে ফ্লপ / চক্রের মধ্যে পরিমাপ করেছি (উইন্ডোতে ঘড়ি ব্যবহার করেছেন) যা সুনির্দিষ্ট নয় I আমার ধারণা, আরও ভাল টাইমার ব্যবহার করা দরকার - ধন্যবাদ ম্যাকি মেসার)।

  • আমি সবচেয়ে ভাল পরিচালনা করেছি gccহ'ল ম্যানুয়ালি আনরোল লুপ করা এবং তিনজনের দলে সংযোজন এবং গুণগুলি সাজানো। সঙ্গে g++ -O2 -march=nocona addmul_unroll.cpp আমি সেরা এ পেতে 0.207s, 4.825 Gflopsযা 1.8 সাথে সঙ্গতিপূর্ণ flops / চক্র যা আমি এখন বেশ খুশি।

সি ++ কোডে আমি forলুপটি প্রতিস্থাপন করেছি

   for (int i=0; i<loops/3; i++) {
       mul1*=mul; mul2*=mul; mul3*=mul;
       sum1+=add; sum2+=add; sum3+=add;
       mul4*=mul; mul5*=mul; mul1*=mul;
       sum4+=add; sum5+=add; sum1+=add;

       mul2*=mul; mul3*=mul; mul4*=mul;
       sum2+=add; sum3+=add; sum4+=add;
       mul5*=mul; mul1*=mul; mul2*=mul;
       sum5+=add; sum1+=add; sum2+=add;

       mul3*=mul; mul4*=mul; mul5*=mul;
       sum3+=add; sum4+=add; sum5+=add;
   }

এবং সমাবেশ এখন দেখতে

.L4:
mulsd    xmm8, xmm3
mulsd    xmm7, xmm3
mulsd    xmm6, xmm3
addsd    xmm13, xmm2
addsd    xmm12, xmm2
addsd    xmm11, xmm2
mulsd    xmm5, xmm3
mulsd    xmm1, xmm3
mulsd    xmm8, xmm3
addsd    xmm10, xmm2
addsd    xmm9, xmm2
addsd    xmm13, xmm2
...

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

আপনার জিসিসির সংস্করণটি কী? আপনি যদি ডিফল্ট ব্যবহার করে কোনও ম্যাকের উপরে থাকেন তবে আপনি সমস্যায় পড়ে যাবেন (এটি পুরানো ৪.২)।
সেমিসাইট

2
হ্যাঁ লিনাক্স চলমান তবে সিস্টেমে কোনও বোঝা নেই এবং এটি বহুবার পুনরাবৃত্তি করা সামান্য পার্থক্য করে (উদাহরণস্বরূপ, স্কেলার সংস্করণের জন্য 4.0-4.2 জিফ্লপস, তবে এখন -funroll-loops)। জিসিসি সংস্করণ ৪.৪.১ এবং ৪.6.২ সহ চেষ্টা করা হয়েছে, তবে asm আউটপুটটি ঠিক আছে?
ব্যবহারকারী 1059432

আপনি কি -O3জিসিসির জন্য চেষ্টা করেছেন, যা সক্ষম করে -ftree-vectorize? সম্ভবত এটি সংযুক্ত -funroll-loopsযদিও আমি এটি সত্যিই প্রয়োজনীয় না হলে না। সামগ্রিকভাবে তুলনাটি অন্যায়ের মতো বলে মনে হচ্ছে যদি সংকলকগুলির মধ্যে একটি ভেক্টরাইজেশন / আনরোলিং করে তবে অন্যটি তা না পারার কারণে না, তবে এটি খুব বেশি বলা হয় না বলেও।
গ্রিজলি

4
@ গ্রিজলি -funroll-loopsসম্ভবত চেষ্টা করার মতো কিছু। তবে আমি মনে করি -ftree-vectorizeবিন্দু ছাড়াও। ওপি চেষ্টা করছে কেবল 1 টি মুল + 1 যুক্ত নির্দেশ / চক্র বজায় রাখার জন্য। নির্দেশাবলী স্কেলার বা ভেক্টর হতে পারে - যেহেতু বিলম্বিতা এবং থ্রুপুট একই রকম হয় তা বিবেচ্য নয়। সুতরাং আপনি যদি স্কেলার এসএসই দিয়ে 2 / চক্র ধরে রাখতে পারেন তবে আপনি সেগুলি ভেক্টর এসএসই দিয়ে প্রতিস্থাপন করতে পারেন এবং আপনি 4 টি ফ্লপ / চক্র অর্জন করতে পারেন। আমার উত্তরে আমি এসএসই -> এভিএক্স থেকে যাচ্ছিলাম। আমি সমস্ত এসএসইকে এভিএক্স - একই লেটেন্সিগুলি, একই থ্রোপুটগুলি, 2x ফ্লপ দিয়ে প্রতিস্থাপন করেছি।
রহস্যময়

উত্তর:


517

আমি এই নির্ভুল কাজটি আগে করেছি। তবে এটি মূলত বিদ্যুৎ খরচ এবং সিপিইউ তাপমাত্রা পরিমাপ করা ছিল। নিম্নলিখিত কোডটি (যা মোটামুটি দীর্ঘ) আমার কোর আই 7600 কেতে সর্বোত্তমের কাছাকাছি অর্জন করে।

এখানে লক্ষ্য করার মূল বিষয়টি হ'ল ম্যানুয়াল লুপ-আনرولলিংয়ের বিশাল পরিমাণ এবং সেইসাথে বহুগুণ এবং সংযোজনকে ইন্টারলিভিং করা ...

সম্পূর্ণ প্রকল্পটি আমার গিটহাবটিতে পাওয়া যাবে: https://github.com/Mysticial/Flops

সতর্কতা:

আপনি যদি এটি সঙ্কলন এবং পরিচালনা করার সিদ্ধান্ত নেন, আপনার সিপিইউ তাপমাত্রায় মনোযোগ দিন !!!
আপনি এটি অত্যধিক গরম না তা নিশ্চিত করুন। এবং নিশ্চিত করুন যে সিপিইউ-থ্রোটলিং আপনার ফলাফলগুলিকে প্রভাবিত করে না!

তদুপরি, এই কোডটি চালানোর ফলে যে কোনও ক্ষতি হতে পারে তার জন্য আমি কোনও দায় নিই না।

মন্তব্য:

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

#include <emmintrin.h>
#include <omp.h>
#include <iostream>
using namespace std;

typedef unsigned long long uint64;

double test_dp_mac_SSE(double x,double y,uint64 iterations){
    register __m128d r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,rA,rB,rC,rD,rE,rF;

    //  Generate starting data.
    r0 = _mm_set1_pd(x);
    r1 = _mm_set1_pd(y);

    r8 = _mm_set1_pd(-0.0);

    r2 = _mm_xor_pd(r0,r8);
    r3 = _mm_or_pd(r0,r8);
    r4 = _mm_andnot_pd(r8,r0);
    r5 = _mm_mul_pd(r1,_mm_set1_pd(0.37796447300922722721));
    r6 = _mm_mul_pd(r1,_mm_set1_pd(0.24253562503633297352));
    r7 = _mm_mul_pd(r1,_mm_set1_pd(4.1231056256176605498));
    r8 = _mm_add_pd(r0,_mm_set1_pd(0.37796447300922722721));
    r9 = _mm_add_pd(r1,_mm_set1_pd(0.24253562503633297352));
    rA = _mm_sub_pd(r0,_mm_set1_pd(4.1231056256176605498));
    rB = _mm_sub_pd(r1,_mm_set1_pd(4.1231056256176605498));

    rC = _mm_set1_pd(1.4142135623730950488);
    rD = _mm_set1_pd(1.7320508075688772935);
    rE = _mm_set1_pd(0.57735026918962576451);
    rF = _mm_set1_pd(0.70710678118654752440);

    uint64 iMASK = 0x800fffffffffffffull;
    __m128d MASK = _mm_set1_pd(*(double*)&iMASK);
    __m128d vONE = _mm_set1_pd(1.0);

    uint64 c = 0;
    while (c < iterations){
        size_t i = 0;
        while (i < 1000){
            //  Here's the meat - the part that really matters.

            r0 = _mm_mul_pd(r0,rC);
            r1 = _mm_add_pd(r1,rD);
            r2 = _mm_mul_pd(r2,rE);
            r3 = _mm_sub_pd(r3,rF);
            r4 = _mm_mul_pd(r4,rC);
            r5 = _mm_add_pd(r5,rD);
            r6 = _mm_mul_pd(r6,rE);
            r7 = _mm_sub_pd(r7,rF);
            r8 = _mm_mul_pd(r8,rC);
            r9 = _mm_add_pd(r9,rD);
            rA = _mm_mul_pd(rA,rE);
            rB = _mm_sub_pd(rB,rF);

            r0 = _mm_add_pd(r0,rF);
            r1 = _mm_mul_pd(r1,rE);
            r2 = _mm_sub_pd(r2,rD);
            r3 = _mm_mul_pd(r3,rC);
            r4 = _mm_add_pd(r4,rF);
            r5 = _mm_mul_pd(r5,rE);
            r6 = _mm_sub_pd(r6,rD);
            r7 = _mm_mul_pd(r7,rC);
            r8 = _mm_add_pd(r8,rF);
            r9 = _mm_mul_pd(r9,rE);
            rA = _mm_sub_pd(rA,rD);
            rB = _mm_mul_pd(rB,rC);

            r0 = _mm_mul_pd(r0,rC);
            r1 = _mm_add_pd(r1,rD);
            r2 = _mm_mul_pd(r2,rE);
            r3 = _mm_sub_pd(r3,rF);
            r4 = _mm_mul_pd(r4,rC);
            r5 = _mm_add_pd(r5,rD);
            r6 = _mm_mul_pd(r6,rE);
            r7 = _mm_sub_pd(r7,rF);
            r8 = _mm_mul_pd(r8,rC);
            r9 = _mm_add_pd(r9,rD);
            rA = _mm_mul_pd(rA,rE);
            rB = _mm_sub_pd(rB,rF);

            r0 = _mm_add_pd(r0,rF);
            r1 = _mm_mul_pd(r1,rE);
            r2 = _mm_sub_pd(r2,rD);
            r3 = _mm_mul_pd(r3,rC);
            r4 = _mm_add_pd(r4,rF);
            r5 = _mm_mul_pd(r5,rE);
            r6 = _mm_sub_pd(r6,rD);
            r7 = _mm_mul_pd(r7,rC);
            r8 = _mm_add_pd(r8,rF);
            r9 = _mm_mul_pd(r9,rE);
            rA = _mm_sub_pd(rA,rD);
            rB = _mm_mul_pd(rB,rC);

            i++;
        }

        //  Need to renormalize to prevent denormal/overflow.
        r0 = _mm_and_pd(r0,MASK);
        r1 = _mm_and_pd(r1,MASK);
        r2 = _mm_and_pd(r2,MASK);
        r3 = _mm_and_pd(r3,MASK);
        r4 = _mm_and_pd(r4,MASK);
        r5 = _mm_and_pd(r5,MASK);
        r6 = _mm_and_pd(r6,MASK);
        r7 = _mm_and_pd(r7,MASK);
        r8 = _mm_and_pd(r8,MASK);
        r9 = _mm_and_pd(r9,MASK);
        rA = _mm_and_pd(rA,MASK);
        rB = _mm_and_pd(rB,MASK);
        r0 = _mm_or_pd(r0,vONE);
        r1 = _mm_or_pd(r1,vONE);
        r2 = _mm_or_pd(r2,vONE);
        r3 = _mm_or_pd(r3,vONE);
        r4 = _mm_or_pd(r4,vONE);
        r5 = _mm_or_pd(r5,vONE);
        r6 = _mm_or_pd(r6,vONE);
        r7 = _mm_or_pd(r7,vONE);
        r8 = _mm_or_pd(r8,vONE);
        r9 = _mm_or_pd(r9,vONE);
        rA = _mm_or_pd(rA,vONE);
        rB = _mm_or_pd(rB,vONE);

        c++;
    }

    r0 = _mm_add_pd(r0,r1);
    r2 = _mm_add_pd(r2,r3);
    r4 = _mm_add_pd(r4,r5);
    r6 = _mm_add_pd(r6,r7);
    r8 = _mm_add_pd(r8,r9);
    rA = _mm_add_pd(rA,rB);

    r0 = _mm_add_pd(r0,r2);
    r4 = _mm_add_pd(r4,r6);
    r8 = _mm_add_pd(r8,rA);

    r0 = _mm_add_pd(r0,r4);
    r0 = _mm_add_pd(r0,r8);


    //  Prevent Dead Code Elimination
    double out = 0;
    __m128d temp = r0;
    out += ((double*)&temp)[0];
    out += ((double*)&temp)[1];

    return out;
}

void test_dp_mac_SSE(int tds,uint64 iterations){

    double *sum = (double*)malloc(tds * sizeof(double));
    double start = omp_get_wtime();

#pragma omp parallel num_threads(tds)
    {
        double ret = test_dp_mac_SSE(1.1,2.1,iterations);
        sum[omp_get_thread_num()] = ret;
    }

    double secs = omp_get_wtime() - start;
    uint64 ops = 48 * 1000 * iterations * tds * 2;
    cout << "Seconds = " << secs << endl;
    cout << "FP Ops  = " << ops << endl;
    cout << "FLOPs   = " << ops / secs << endl;

    double out = 0;
    int c = 0;
    while (c < tds){
        out += sum[c++];
    }

    cout << "sum = " << out << endl;
    cout << endl;

    free(sum);
}

int main(){
    //  (threads, iterations)
    test_dp_mac_SSE(8,10000000);

    system("pause");
}

আউটপুট (1 থ্রেড, 10000000 পুনরাবৃত্তি) - ভিজ্যুয়াল স্টুডিও 2010 এসপি 1 - x64 প্রকাশের সাথে সংকলিত:

Seconds = 55.5104
FP Ops  = 960000000000
FLOPs   = 1.7294e+010
sum = 2.22652

মেশিনটি একটি কোর i7 2600K @ 4.4 গিগাহার্টজ। তাত্ত্বিক এসএসই শিখরটি 4 ফ্লপ * 4.4 গিগাহার্জ = 17.6 জিএফলপস । এই কোডটি 17.3 জিএফলপগুলি অর্জন করে - খারাপ নয়।

আউটপুট (8 টি থ্রেড, 10000000 পুনরাবৃত্তি) - ভিজ্যুয়াল স্টুডিও 2010 এসপি 1 - x64 প্রকাশের সাথে সংকলিত:

Seconds = 117.202
FP Ops  = 7680000000000
FLOPs   = 6.55279e+010
sum = 17.8122

তাত্ত্বিক এসএসই শিখরটি 4 ফ্লপ * 4 কোর * 4.4 গিগাহার্টজ = 70.4 জিএফলপস। ACTUAL যা 65.5 GFlops


এর আরও এক ধাপ এগিয়ে নেওয়া যাক। AVX ...

#include <immintrin.h>
#include <omp.h>
#include <iostream>
using namespace std;

typedef unsigned long long uint64;

double test_dp_mac_AVX(double x,double y,uint64 iterations){
    register __m256d r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,rA,rB,rC,rD,rE,rF;

    //  Generate starting data.
    r0 = _mm256_set1_pd(x);
    r1 = _mm256_set1_pd(y);

    r8 = _mm256_set1_pd(-0.0);

    r2 = _mm256_xor_pd(r0,r8);
    r3 = _mm256_or_pd(r0,r8);
    r4 = _mm256_andnot_pd(r8,r0);
    r5 = _mm256_mul_pd(r1,_mm256_set1_pd(0.37796447300922722721));
    r6 = _mm256_mul_pd(r1,_mm256_set1_pd(0.24253562503633297352));
    r7 = _mm256_mul_pd(r1,_mm256_set1_pd(4.1231056256176605498));
    r8 = _mm256_add_pd(r0,_mm256_set1_pd(0.37796447300922722721));
    r9 = _mm256_add_pd(r1,_mm256_set1_pd(0.24253562503633297352));
    rA = _mm256_sub_pd(r0,_mm256_set1_pd(4.1231056256176605498));
    rB = _mm256_sub_pd(r1,_mm256_set1_pd(4.1231056256176605498));

    rC = _mm256_set1_pd(1.4142135623730950488);
    rD = _mm256_set1_pd(1.7320508075688772935);
    rE = _mm256_set1_pd(0.57735026918962576451);
    rF = _mm256_set1_pd(0.70710678118654752440);

    uint64 iMASK = 0x800fffffffffffffull;
    __m256d MASK = _mm256_set1_pd(*(double*)&iMASK);
    __m256d vONE = _mm256_set1_pd(1.0);

    uint64 c = 0;
    while (c < iterations){
        size_t i = 0;
        while (i < 1000){
            //  Here's the meat - the part that really matters.

            r0 = _mm256_mul_pd(r0,rC);
            r1 = _mm256_add_pd(r1,rD);
            r2 = _mm256_mul_pd(r2,rE);
            r3 = _mm256_sub_pd(r3,rF);
            r4 = _mm256_mul_pd(r4,rC);
            r5 = _mm256_add_pd(r5,rD);
            r6 = _mm256_mul_pd(r6,rE);
            r7 = _mm256_sub_pd(r7,rF);
            r8 = _mm256_mul_pd(r8,rC);
            r9 = _mm256_add_pd(r9,rD);
            rA = _mm256_mul_pd(rA,rE);
            rB = _mm256_sub_pd(rB,rF);

            r0 = _mm256_add_pd(r0,rF);
            r1 = _mm256_mul_pd(r1,rE);
            r2 = _mm256_sub_pd(r2,rD);
            r3 = _mm256_mul_pd(r3,rC);
            r4 = _mm256_add_pd(r4,rF);
            r5 = _mm256_mul_pd(r5,rE);
            r6 = _mm256_sub_pd(r6,rD);
            r7 = _mm256_mul_pd(r7,rC);
            r8 = _mm256_add_pd(r8,rF);
            r9 = _mm256_mul_pd(r9,rE);
            rA = _mm256_sub_pd(rA,rD);
            rB = _mm256_mul_pd(rB,rC);

            r0 = _mm256_mul_pd(r0,rC);
            r1 = _mm256_add_pd(r1,rD);
            r2 = _mm256_mul_pd(r2,rE);
            r3 = _mm256_sub_pd(r3,rF);
            r4 = _mm256_mul_pd(r4,rC);
            r5 = _mm256_add_pd(r5,rD);
            r6 = _mm256_mul_pd(r6,rE);
            r7 = _mm256_sub_pd(r7,rF);
            r8 = _mm256_mul_pd(r8,rC);
            r9 = _mm256_add_pd(r9,rD);
            rA = _mm256_mul_pd(rA,rE);
            rB = _mm256_sub_pd(rB,rF);

            r0 = _mm256_add_pd(r0,rF);
            r1 = _mm256_mul_pd(r1,rE);
            r2 = _mm256_sub_pd(r2,rD);
            r3 = _mm256_mul_pd(r3,rC);
            r4 = _mm256_add_pd(r4,rF);
            r5 = _mm256_mul_pd(r5,rE);
            r6 = _mm256_sub_pd(r6,rD);
            r7 = _mm256_mul_pd(r7,rC);
            r8 = _mm256_add_pd(r8,rF);
            r9 = _mm256_mul_pd(r9,rE);
            rA = _mm256_sub_pd(rA,rD);
            rB = _mm256_mul_pd(rB,rC);

            i++;
        }

        //  Need to renormalize to prevent denormal/overflow.
        r0 = _mm256_and_pd(r0,MASK);
        r1 = _mm256_and_pd(r1,MASK);
        r2 = _mm256_and_pd(r2,MASK);
        r3 = _mm256_and_pd(r3,MASK);
        r4 = _mm256_and_pd(r4,MASK);
        r5 = _mm256_and_pd(r5,MASK);
        r6 = _mm256_and_pd(r6,MASK);
        r7 = _mm256_and_pd(r7,MASK);
        r8 = _mm256_and_pd(r8,MASK);
        r9 = _mm256_and_pd(r9,MASK);
        rA = _mm256_and_pd(rA,MASK);
        rB = _mm256_and_pd(rB,MASK);
        r0 = _mm256_or_pd(r0,vONE);
        r1 = _mm256_or_pd(r1,vONE);
        r2 = _mm256_or_pd(r2,vONE);
        r3 = _mm256_or_pd(r3,vONE);
        r4 = _mm256_or_pd(r4,vONE);
        r5 = _mm256_or_pd(r5,vONE);
        r6 = _mm256_or_pd(r6,vONE);
        r7 = _mm256_or_pd(r7,vONE);
        r8 = _mm256_or_pd(r8,vONE);
        r9 = _mm256_or_pd(r9,vONE);
        rA = _mm256_or_pd(rA,vONE);
        rB = _mm256_or_pd(rB,vONE);

        c++;
    }

    r0 = _mm256_add_pd(r0,r1);
    r2 = _mm256_add_pd(r2,r3);
    r4 = _mm256_add_pd(r4,r5);
    r6 = _mm256_add_pd(r6,r7);
    r8 = _mm256_add_pd(r8,r9);
    rA = _mm256_add_pd(rA,rB);

    r0 = _mm256_add_pd(r0,r2);
    r4 = _mm256_add_pd(r4,r6);
    r8 = _mm256_add_pd(r8,rA);

    r0 = _mm256_add_pd(r0,r4);
    r0 = _mm256_add_pd(r0,r8);

    //  Prevent Dead Code Elimination
    double out = 0;
    __m256d temp = r0;
    out += ((double*)&temp)[0];
    out += ((double*)&temp)[1];
    out += ((double*)&temp)[2];
    out += ((double*)&temp)[3];

    return out;
}

void test_dp_mac_AVX(int tds,uint64 iterations){

    double *sum = (double*)malloc(tds * sizeof(double));
    double start = omp_get_wtime();

#pragma omp parallel num_threads(tds)
    {
        double ret = test_dp_mac_AVX(1.1,2.1,iterations);
        sum[omp_get_thread_num()] = ret;
    }

    double secs = omp_get_wtime() - start;
    uint64 ops = 48 * 1000 * iterations * tds * 4;
    cout << "Seconds = " << secs << endl;
    cout << "FP Ops  = " << ops << endl;
    cout << "FLOPs   = " << ops / secs << endl;

    double out = 0;
    int c = 0;
    while (c < tds){
        out += sum[c++];
    }

    cout << "sum = " << out << endl;
    cout << endl;

    free(sum);
}

int main(){
    //  (threads, iterations)
    test_dp_mac_AVX(8,10000000);

    system("pause");
}

আউটপুট (1 থ্রেড, 10000000 পুনরাবৃত্তি) - ভিজ্যুয়াল স্টুডিও 2010 এসপি 1 - x64 প্রকাশের সাথে সংকলিত:

Seconds = 57.4679
FP Ops  = 1920000000000
FLOPs   = 3.34099e+010
sum = 4.45305

তাত্ত্বিক AVX শিখরটি 8 ফ্লপ * 4.4 গিগাহার্জ = 35.2 জিএফলপ । ACTUAL যা 33.4 GFlops

আউটপুট (8 টি থ্রেড, 10000000 পুনরাবৃত্তি) - ভিজ্যুয়াল স্টুডিও 2010 এসপি 1 - x64 প্রকাশের সাথে সংকলিত:

Seconds = 111.119
FP Ops  = 15360000000000
FLOPs   = 1.3823e+011
sum = 35.6244

তাত্ত্বিক এভিএক্স শিখর 8 ফ্লপ * 4 কোর * 4.4 গিগাহার্টজ = 140.8 জিএফলপস। ACTUAL যা 138,2 GFlops


এখন কিছু ব্যাখ্যার জন্য:

পারফরম্যান্সের সমালোচনামূলক অংশটি অবশ্যই অভ্যন্তরীণ লুপের 48 টি নির্দেশাবলী। আপনি লক্ষ্য করবেন যে এটি প্রতিটি 12 টি নির্দেশের 4 টি ব্লক হয়ে গেছে। এই 12 টি নির্দেশাবলী ব্লকের প্রত্যেকটি একে অপরের থেকে সম্পূর্ণ স্বতন্ত্র - এবং সম্পাদন করতে গড়ে 6 টি চক্র গ্রহণ করে।

সুতরাং ইস্যু থেকে ব্যবহারের মধ্যে 12 টি নির্দেশনা এবং 6 টি চক্র রয়েছে। গুণনের বিলম্বটি 5 টি চক্র, সুতরাং বিলম্বের স্টলগুলি এড়াতে এটি যথেষ্ট।

স্বাভাবিককরণের ধাপটি ডেটা ওভারফ্লো / প্রবাহমান থেকে রাখার জন্য প্রয়োজন। ডু-কিছুই কোডটি ধীরে ধীরে ডেটাটির পরিমাণ বাড়াতে / হ্রাস করবে সেহেতু এটি দরকার।

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


আরও ফলাফল:

  • ইন্টেল কোর i7 920 @ 3.5 গিগাহার্টজ
  • উইন্ডোজ 7 আলটিমেট এক্স 64
  • ভিজ্যুয়াল স্টুডিও 2010 এসপি 1 - এক্স 64 প্রকাশ

থ্রেড: 1

Seconds = 72.1116
FP Ops  = 960000000000
FLOPs   = 1.33127e+010
sum = 2.22652

তাত্ত্বিক এসএসই পিক: 4 টি ফ্লপ * 3.5 গিগাহার্জ = 14.0 জিএফলপস । ACTUAL যা 13.3 GFlops

থ্রেড: 8

Seconds = 149.576
FP Ops  = 7680000000000
FLOPs   = 5.13452e+010
sum = 17.8122

তাত্ত্বিক এসএসই পিক: 4 ফ্লপ * 4 কোর * 3.5 গিগাহার্টজ = 56.0 জিএফলপস । ACTUAL যা 51,3 GFlops

আমার প্রসেসরের টেম্পস বহু-থ্রেড রানে 76C তে আঘাত করেছে! আপনি যদি এটি চালনা করেন তবে নিশ্চিত হন যে ফলাফলগুলি সিপিইউ থ্রোলটিং দ্বারা প্রভাবিত হবে না।


  • 2 এক্স ইন্টেল শিওন এক্স 5482 হার্পারটাউন @ 3.2 গিগাহার্টজ
  • উবুন্টু লিনাক্স 10 x64
  • জিসিসি 4.5.2 x64 - (-O2 -msse3 -fopenmp)

থ্রেড: 1

Seconds = 78.3357
FP Ops  = 960000000000
FLOPs   = 1.22549e+10
sum = 2.22652

তাত্ত্বিক এসএসই পিক: 4 টি ফ্লপ * 3.2 গিগাহার্জ = 12.8 জিএফলপস । ACTUAL যা 12.3 GFlops

থ্রেড: 8

Seconds = 78.4733
FP Ops  = 7680000000000
FLOPs   = 9.78676e+10
sum = 17.8122

তাত্ত্বিক এসএসই পিক: 4 ফ্লপ * 8 কোর * 3.2 গিগাহার্টজ = 102.4 জিএফ্লপস । ACTUAL যা 97,9 GFlops


13
আপনার ফলাফল খুব চিত্তাকর্ষক। আমি আপনার কোডটি আমার পুরানো সিস্টেমে g ++ দিয়ে সংকলন করেছি তবে প্রায় ভাল ফলাফল পাচ্ছি না: 100k পুনরাবৃত্তি, 1.814s, 5.292 Gflops, sum=0.448883শীর্ষে 10.68 জিএফপ্লস বা চক্র প্রতি 2.0 ফ্লপের খুব কম। সমানতালে সম্পাদিত হয় না add/ মনে mulহয়। আমি যখন আপনার কোডটি পরিবর্তন করি এবং সর্বদা একই রেজিস্টারের সাথে যোগ / গুণ করি, বলুন rC, এটি হঠাৎ প্রায় শিখরটি অর্জন করে: 0.953s, 10.068 Gflops, sum=0বা 3.8 ফ্লপ / চক্র। খুব অদ্ভুত.
ব্যবহারকারী 1059432

11
হ্যাঁ, যেহেতু আমি ইনলাইন অ্যাসেমবিলিটি ব্যবহার করছি না, তাই সম্পাদনাটি সংকলকের পক্ষে প্রকৃতপক্ষে অত্যন্ত সংবেদনশীল । আমার কাছে এখানে কোডটি ভিসি 2010 এর জন্য সুর করা হয়েছে। এবং আমি যদি সঠিকভাবে স্মরণ করি তবে ইন্টেল সংকলক ঠিক তেমন ভাল ফলাফল দেয়। যেমনটি আপনি লক্ষ্য করেছেন, এটি ভালভাবে সংকলন করার জন্য আপনাকে এটি কিছুটা টুইট করতে হতে পারে।
রহস্যময়

8
আমি উইন্ডোজ 7 এ আপনার উইন্ডোজ 7 cl /O2- তে (উইন্ডোজ এসডিকে থেকে -৪-বিট) ব্যবহার করে আপনার ফলাফলগুলি নিশ্চিত করতে পারি এবং এমনকি আমার উদাহরণটি স্কেলার অপারেশনের (১.৯ ফ্লপ / চক্র) শীর্ষে চলে আসে। সংকলক লুপ-আনআরোলস এবং পুনঃব্যবস্থাপনা কিন্তু এটি আরও কিছুটা দেখার প্রয়োজন হতে পারে না। থ্রটলিং কোনও সমস্যা নয় আমি আমার সিপিইউতে চমৎকার এবং পুনরাবৃত্তিগুলি 100 কে রাখি। :)
ব্যবহারকারী 1059432

6
@ মিস্টিয়াল: এটি আজ আর / কোডিং সাবরেডডিটে প্রদর্শিত হয়েছে।
গ্রেফ্যাড

2
@ হাইলেম এটি হয় গলে যায় বা বন্ধ হয়ে যায়। দুটোই কখনই না। যদি পর্যাপ্ত শীতলতা থাকে তবে এটি এয়ারটাইম পাবে। অন্যথায়, এটি কেবল গলে যায়। :)
রহস্যময়

33

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

আপনি যদি নেহালেম / স্যান্ডি ব্রিজ আর্কিটেকচারটি এখানে দেখুন http://www.realworldtech.com/page.cfm?ArticleID=RWT091810191937&p=6 কী হয় তা একেবারে পরিষ্কার।

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

এটি কেবলমাত্র তাত্ত্বিক কারণ আমার কাছে পরীক্ষার জন্য এই প্রসেসরের কোনওটিই নেই।


2
লুপ ওভারহেড এর মাত্র তিন নির্দেশাবলী আছে: inc, cmp, এবং jl। এই সমস্তগুলি পোর্ট # 5 এ যেতে পারে এবং ভেক্টরাইজড faddবা এর সাথে হস্তক্ষেপ করবে না fmul। আমি বরং সন্দেহ করব যে ডিকোডারটি (কখনও কখনও) পথে যায়। এটি চক্র প্রতি দুই থেকে তিনটি নির্দেশের মধ্যে টিকিয়ে রাখতে হবে। আমি সঠিক সীমাবদ্ধতাগুলি মনে করি না তবে নির্দেশের দৈর্ঘ্য, উপসর্গ এবং সারিবদ্ধকরণ সবই কার্যকর হয়।
ম্যাকি মেসার

cmpএবং jlঅবশ্যই পোর্ট 5 এ যান, ঠিক incতেমন নিশ্চিত হন না যে এটি সর্বদা 2 জনের সাথে গ্রুপে আসে। তবে আপনি ঠিক বলেছেন, বাধা কোথায় এবং ডিকোডাররাও এর অংশ হতে পারে তা বলা শক্ত।
প্যাট্রিক Schlüter

3
আমি মৌলিক লুপটি নিয়ে কিছুটা খেলাম: নির্দেশাবলীর ক্রমটি বিবেচনা করে। কিছু ব্যবস্থা ন্যূনতম 5 চক্রের পরিবর্তে 13 চক্র নেয়। পারফরম্যান্স ইভেন্টের কাউন্টারগুলি দেখার সময় বলে আমি মনে করি ...
ম্যাকি মেসার

16

শাখাগুলি অবশ্যই আপনাকে তাত্ত্বিক পারফরম্যান্সের শীর্ষস্থানীয় অবস্থান থেকে বিরত রাখতে পারে। আপনি ম্যানুয়ালি কিছু লুপ-আন-তালিকাভুক্ত করলে আপনি কি কোনও পার্থক্য দেখতে পাচ্ছেন? উদাহরণস্বরূপ, আপনি যদি লুপের পুনরাবৃত্তির জন্য 5 বা 10 বার হিসাবে অনেকগুলি অপশন রাখেন:

for(int i=0; i<loops/5; i++) {
      mul1*=mul; mul2*=mul; mul3*=mul; mul4*=mul; mul5*=mul;
      sum1+=add; sum2+=add; sum3+=add; sum4+=add; sum5+=add;
      mul1*=mul; mul2*=mul; mul3*=mul; mul4*=mul; mul5*=mul;
      sum1+=add; sum2+=add; sum3+=add; sum4+=add; sum5+=add;
      mul1*=mul; mul2*=mul; mul3*=mul; mul4*=mul; mul5*=mul;
      sum1+=add; sum2+=add; sum3+=add; sum4+=add; sum5+=add;
      mul1*=mul; mul2*=mul; mul3*=mul; mul4*=mul; mul5*=mul;
      sum1+=add; sum2+=add; sum3+=add; sum4+=add; sum5+=add;
      mul1*=mul; mul2*=mul; mul3*=mul; mul4*=mul; mul5*=mul;
      sum1+=add; sum2+=add; sum3+=add; sum4+=add; sum5+=add;
   }

4
আমার ভুল হতে পারে তবে আমি বিশ্বাস করি যে ++ এর সাথে জি ++ স্বয়ংক্রিয়ভাবে লুপটি উন্মুক্ত করার চেষ্টা করবে (আমার মনে হয় এটি ডফের ডিভাইসটি ব্যবহার করে)।
ওয়েভার 18

6
হ্যাঁ, ধন্যবাদ প্রকৃতপক্ষে কিছুটা উন্নতি হয়েছে। আমি এখন প্রায় ৪.১-৪.৩ জিফ্লপস, বা চক্র প্রতি 1.55 ফ্লপ পেয়েছি। এবং না, এই উদাহরণে -O2 আনরোল লুপ করেনি।
ব্যবহারকারীর 1059432

1
লুপ আন্রোলিংয়ের বিষয়ে তাঁতি সঠিক, আমি বিশ্বাস করি। সুতরাং ম্যানুয়ালি আনআরোলিং করা সম্ভবত প্রয়োজনীয় নয়
জিম এমকনামারা

5
উপরে এসেম্বল আউটপুট দেখুন, লুপ আন্রোলিংয়ের কোনও লক্ষণ নেই।
ব্যবহারকারীর 1059432

14
স্বয়ংক্রিয় তালিকাভুক্তিও গড়ে গড়ে ৪.২ জিএফপ্ল্যাপে উন্নত হয় তবে এর জন্য এমন -funroll-loopsবিকল্পের প্রয়োজন হয় যা অন্তর্ভুক্ত নয় -O3। দেখুন g++ -c -Q -O2 --help=optimizers | grep unroll
ব্যবহারকারীর 1059432

7

আমার প্রাপ্ত 2.4GHz ইন্টেল কোর 2 ডুওতে ইন্টেলস আইসিসি সংস্করণ 11.1 ব্যবহার করা

Macintosh:~ mackie$ icc -O3 -mssse3 -oaddmul addmul.cc && ./addmul 1000
addmul:  0.105 s, 9.525 Gflops, res=0.000000
Macintosh:~ mackie$ icc -v
Version 11.1 

এটি আদর্শ 9.6 গফ্লপসের খুব কাছে।

সম্পাদনা করুন:

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

Macintosh:~ mackie$ icc -O3 -mssse3 -oaddmul addmul.cc -fp-model precise && ./addmul 1000
addmul:  0.516 s, 1.938 Gflops, res=1.326463

EDIT2:

অনুরোধ হিসাবে:

Macintosh:~ mackie$ clang -O3 -mssse3 -oaddmul addmul.cc && ./addmul 1000
addmul:  0.209 s, 4.786 Gflops, res=1.326463
Macintosh:~ mackie$ clang -v
Apple clang version 3.0 (tags/Apple/clang-211.10.1) (based on LLVM 3.0svn)
Target: x86_64-apple-darwin11.2.0
Thread model: posix

ঝাঁকুনির কোডের অভ্যন্তরীণ লুপটি দেখতে দেখতে:

        .align  4, 0x90
LBB2_4:                                 ## =>This Inner Loop Header: Depth=1
        addsd   %xmm2, %xmm3
        addsd   %xmm2, %xmm14
        addsd   %xmm2, %xmm5
        addsd   %xmm2, %xmm1
        addsd   %xmm2, %xmm4
        mulsd   %xmm2, %xmm0
        mulsd   %xmm2, %xmm6
        mulsd   %xmm2, %xmm7
        mulsd   %xmm2, %xmm11
        mulsd   %xmm2, %xmm13
        incl    %eax
        cmpl    %r14d, %eax
        jl      LBB2_4

EDIT3:

শেষ অবধি, দুটি পরামর্শ: প্রথমত, আপনি যদি এই ধরণের বেঞ্চমার্কিং পছন্দ করেন, তবে rdtscনির্দেশাবলীর দিকনির্দেশটি ব্যবহার করে বিবেচনা করুন gettimeofday(2)। এটি অনেক বেশি নির্ভুল এবং চক্রগুলিতে সময় সরবরাহ করে যা সাধারণত যেভাবেই আপনি আগ্রহী। জিসিসি এবং বন্ধুদের জন্য আপনি এটি এর মতো সংজ্ঞা দিতে পারেন:

#include <stdint.h>

static __inline__ uint64_t rdtsc(void)
{
        uint64_t rval;
        __asm__ volatile ("rdtsc" : "=A" (rval));
        return rval;
}

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


2
এবং বিচ্ছিন্নতা দেখতে কেমন?
বাহবার

1
আকর্ষণীয়, এটি 1 ফ্লপ / চক্রেরও কম। সংকলকটি addsd'গুলি এবং mulsd' গুলিগুলি মিশ্রিত করে নাকি সেগুলি আমার সমাবেশ আউটপুট হিসাবে গোষ্ঠীতে রয়েছে? সংকলকটি তাদের সাথে মিশ্রিত হয়ে যায় (যা আমি ছাড়াই না -march=native) যখন আমি প্রায় 1 টি ফ্লপ / চক্র পাই । আপনি যদি add=mul;ফাংশনটির শুরুতে একটি লাইন যুক্ত করেন তবে পারফরম্যান্স কীভাবে পরিবর্তন হবে addmul(...)?
ব্যবহারকারী 1059432

1
@ user1059432: দ্য addsdএবং subsdনির্দেশাবলী প্রকৃতপক্ষে সুনির্দিষ্ট সংস্করণে মিশ্রিত করা হয়। আমি ঝাঁকুনি 3.0ও চেষ্টা করেছি, এটি নির্দেশাবলী মেশে না এবং এটি কোর 2 জুটির 2 ফ্লপ / চক্রের খুব কাছাকাছি আসে। আমি যখন আমার ল্যাপটপের কোর আই 5 এ একই কোডটি চালাচ্ছি তখন কোডটি মিশ্রিত করার ফলে কোনও তফাত হয় না। আমি উভয় ক্ষেত্রে প্রায় 3 ফ্লপ / চক্র পাই।
ম্যাকি মেসার

1
@ user1059432: শেষ পর্যন্ত এটি একটি সিনথেটিক বেঞ্চমার্কের জন্য "অর্থবহ" কোড তৈরি করতে সংকলকটিকে ট্রিক করা। এটি প্রথম দেখায় বলে মনে হয় তার চেয়ে শক্ত। (অর্থাত্ আইসিসি আপনার মানদণ্ডের আউটসামার্টস) আপনার সমস্ত কিছু যদি 4 ফ্লপ / চক্রের কিছু কোড চালানো হয় তবে সবচেয়ে সহজ জিনিসটি একটি ছোট অ্যাসেমব্লিং লুপটি লেখা হয়। অনেক কম মাথাব্যথা। :-)
ম্যাকি মেসার

1
ঠিক আছে, তাই আপনি উপরে উল্লিখিত মত একটি অ্যাসেম্বলি কোড সহ আপনি 2 ফ্লপ / চক্রের কাছাকাছি যান? কত কাছে? আমি কেবল 1.4 পেয়েছি যাতে তা তাৎপর্যপূর্ণ। আমি মনে করি না যে আপনি আপনার ল্যাপটপে 3 ফ্লপ / চক্র পেয়েছেন যদি না আপনি যদি পূর্বে যেমনটি দেখেছেন কম্পাইলারটি অপ্টিমাইজেশন না করে icc, আপনি কি সমাবেশের দ্বিগুণ পরীক্ষা করতে পারবেন?
ব্যবহারকারী 1059432
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.