আমি আমাদের মূল গণিতের কিছুটি একটি ইন্টেল কোর ডুওয়ের উপর লিখছি, এবং বর্গমূলের বিভিন্ন পদ্ধতির দিকে তাকানোর সময় আমি কিছু অদ্ভুত কিছু লক্ষ্য করেছি: এসএসই স্কেলার অপারেশনগুলি ব্যবহার করে, একটি পারস্পরিক স্কোয়ার রুট গ্রহণ করা এবং এটির সংখ্যাবৃদ্ধি করা আরও দ্রুত স্কয়ার্টটি পাওয়ার জন্য এটি দেশীয় স্কয়ার্ট অপকোড ব্যবহারের চেয়ে বেশি!
আমি এটির মতো লুপ দিয়ে এটি পরীক্ষা করছি:
inline float TestSqrtFunction( float in );
void TestFunc()
{
#define ARRAYSIZE 4096
#define NUMITERS 16386
float flIn[ ARRAYSIZE ]; // filled with random numbers ( 0 .. 2^22 )
float flOut [ ARRAYSIZE ]; // filled with 0 to force fetch into L1 cache
cyclecounter.Start();
for ( int i = 0 ; i < NUMITERS ; ++i )
for ( int j = 0 ; j < ARRAYSIZE ; ++j )
{
flOut[j] = TestSqrtFunction( flIn[j] );
// unrolling this loop makes no difference -- I tested it.
}
cyclecounter.Stop();
printf( "%d loops over %d floats took %.3f milliseconds",
NUMITERS, ARRAYSIZE, cyclecounter.Milliseconds() );
}
আমি এটি টেস্টস্করণ ফাংশনের জন্য কয়েকটি পৃথক সংস্থা নিয়ে চেষ্টা করেছি এবং আমার এমন কিছু সময় পেয়েছে যা সত্যিই আমার মাথা আঁচড়ে যাচ্ছে। এখন পর্যন্ত সবচেয়ে খারাপটি হ'ল দেশীয় স্কয়ার্ট () ফাংশনটি ব্যবহার করা এবং "স্মার্ট" সংকলকটিকে "অনুকূলিতকরণ" দেওয়া। 24ns / ফ্লোটে, x87 এফপিইউ ব্যবহার করে এটি করুণভাবে খারাপ হয়েছিল:
inline float TestSqrtFunction( float in )
{ return sqrt(in); }
পরেরটি আমি যা চেষ্টা করেছি তা হ'ল সংকলককে এসএসই এর স্কেলার স্কয়ার্ট অপকোড ব্যবহার করতে বাধ্য করার জন্য:
inline void SSESqrt( float * restrict pOut, float * restrict pIn )
{
_mm_store_ss( pOut, _mm_sqrt_ss( _mm_load_ss( pIn ) ) );
// compiles to movss, sqrtss, movss
}
এটি 11.9ns / ফ্লোটে ভাল ছিল। আমি কারম্যাকের ন্যাক্কারজনক নিউটন-র্যাফসন আনুমানিক কৌশলটিও চেষ্টা করেছিলাম , যা হার্ডওয়্যার থেকেও আরও ভালভাবে চলেছিল, ৪.৩ এনএস / ফ্লোটে, যদিও 2 10-এ 1 এর ত্রুটি ছিল (এটি আমার উদ্দেশ্যগুলির জন্য খুব বেশি))
ডুজিটি ছিল যখন আমি পারস্পরিক বর্গমূলের জন্য এসএসই অপ্ট চেষ্টা করেছিলাম এবং এর পরে বর্গমূল (x * 1 / √x = √x) পেতে বহুগুণ ব্যবহার করি। যদিও এটি দুটি নির্ভরশীল অপারেশন গ্রহণ করে, এটি খুব দ্রুত সমাধান ছিল 1.24ns / ফ্লোটে এবং 2 -14-এ সঠিক :
inline void SSESqrt_Recip_Times_X( float * restrict pOut, float * restrict pIn )
{
__m128 in = _mm_load_ss( pIn );
_mm_store_ss( pOut, _mm_mul_ss( in, _mm_rsqrt_ss( in ) ) );
// compiles to movss, movaps, rsqrtss, mulss, movss
}
আমার প্রশ্ন মূলত কি দেয় ? অন্য দুটি গণিত ক্রিয়াকলাপের মধ্যে এসএসই-র অন্তর্নির্মিত হার্ডওয়্যার স্কোয়ার রুটটি সিন্থেসাইজ করার চেয়ে ধীর কেন?
আমি নিশ্চিত যে এটি সত্যই অপের নিজের জন্য ব্যয়, কারণ আমি যাচাই করেছি:
- সমস্ত ডেটা ক্যাশে ফিট করে এবং অ্যাক্সেসগুলি ক্রমযুক্ত হয়
- কার্যাদি অন্তর্ভুক্ত করা হয়
- লুপটি আনলোল করা কোনও তাত্পর্যপূর্ণ করে না
- সংকলক পতাকাগুলি সম্পূর্ণ অপ্টিমাইজেশনে সেট করা আছে (এবং সমাবেশটি ভাল, আমি পরীক্ষা করেছি)
( সম্পাদনা : স্টেফেনটিরোন সঠিকভাবে উল্লেখ করেছে যে সংখ্যার দীর্ঘ স্ট্রিংয়ের ক্রিয়াকলাপগুলিতে ভেক্টরাইজিং সিমড প্যাকড অপস ব্যবহার করা উচিত, যেমন rsqrtps
- তবে এখানে অ্যারের ডেটা স্ট্রাকচারটি কেবল পরীক্ষার উদ্দেশ্যেই হয়: আমি যা চেষ্টা করতে চাইছি তা কোডে ব্যবহারের জন্য স্কেলার পারফরম্যান্স) এটি ভেক্টরাইজ করা যাবে না))
inline float SSESqrt( float restrict fIn ) { float fOut; _mm_store_ss( &fOut, _mm_sqrt_ss( _mm_load_ss( &fIn ) ) ); return fOut; }
,। তবে এটি একটি খারাপ ধারণা কারণ এটি সিপিইউ স্ট্যাকটিতে ভাসা লিখলে সহজেই লোড-হিট-স্টোর স্টলকে প্ররোচিত করতে পারে এবং তাৎক্ষণিকভাবে সেগুলি আবার পড়তে পারে - বিশেষত ফেরতের মূল্যের জন্য ভেক্টর রেজিস্টার থেকে ফ্ল্যাট রেজিস্টারে জাগলিং খারাপ খবর। এছাড়াও, অন্তর্নিহিত মেশিনটি অপকড করে যে এসএসই অভ্যন্তরীণভাবে যেভাবেই অ্যাড্রেস অপারেন্ডকে উপস্থাপন করে।
eax
) খুব খারাপ, যখন এক্সএমএম 0 এবং স্ট্যাকের মধ্যে একটি বৃত্তাকার ট্রিপ হয় between এবং ফিরে নয়, কারণ ইন্টেলের স্টোর-ফরোয়ার্ডিং। নিশ্চিত হয়ে দেখার জন্য আপনি নিজেই এটি সময় করতে পারেন। সাধারণত সম্ভাব্য এলএইচএস দেখার সহজতম উপায় হ'ল নির্গত সমাবেশটি দেখুন এবং দেখুন নিবন্ধক সংস্থাগুলির মধ্যে কোথা থেকে ডেটা জাগল রয়েছে; আপনার সংকলক স্মার্ট জিনিস করতে পারে, বা এটি নাও করতে পারে। ভেক্টরগুলিকে সাধারণকরণ হিসাবে, আমি এখানে আমার ফলাফলগুলি লিখেছিলাম: bit.ly/9W5zoU