একটি আধুনিক 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
...
-funroll-loops
)। জিসিসি সংস্করণ ৪.৪.১ এবং ৪.6.২ সহ চেষ্টা করা হয়েছে, তবে asm আউটপুটটি ঠিক আছে?
-O3
জিসিসির জন্য চেষ্টা করেছেন, যা সক্ষম করে -ftree-vectorize
? সম্ভবত এটি সংযুক্ত -funroll-loops
যদিও আমি এটি সত্যিই প্রয়োজনীয় না হলে না। সামগ্রিকভাবে তুলনাটি অন্যায়ের মতো বলে মনে হচ্ছে যদি সংকলকগুলির মধ্যে একটি ভেক্টরাইজেশন / আনরোলিং করে তবে অন্যটি তা না পারার কারণে না, তবে এটি খুব বেশি বলা হয় না বলেও।
-funroll-loops
সম্ভবত চেষ্টা করার মতো কিছু। তবে আমি মনে করি -ftree-vectorize
বিন্দু ছাড়াও। ওপি চেষ্টা করছে কেবল 1 টি মুল + 1 যুক্ত নির্দেশ / চক্র বজায় রাখার জন্য। নির্দেশাবলী স্কেলার বা ভেক্টর হতে পারে - যেহেতু বিলম্বিতা এবং থ্রুপুট একই রকম হয় তা বিবেচ্য নয়। সুতরাং আপনি যদি স্কেলার এসএসই দিয়ে 2 / চক্র ধরে রাখতে পারেন তবে আপনি সেগুলি ভেক্টর এসএসই দিয়ে প্রতিস্থাপন করতে পারেন এবং আপনি 4 টি ফ্লপ / চক্র অর্জন করতে পারেন। আমার উত্তরে আমি এসএসই -> এভিএক্স থেকে যাচ্ছিলাম। আমি সমস্ত এসএসইকে এভিএক্স - একই লেটেন্সিগুলি, একই থ্রোপুটগুলি, 2x ফ্লপ দিয়ে প্রতিস্থাপন করেছি।