সি ++ স্ট্যান্ডার্ড কমিটির সাথে এটি আলোচনা হওয়ার পরে আমি আরও কিছুটা বিস্তৃত উত্তর দেওয়ার চেষ্টা করতে চাই। সি ++ কমিটির সদস্য হওয়ার পাশাপাশি আমি এলএলভিএম এবং ক্ল্যাং সংকলকগুলির বিকাশকারীও।
মৌলিকভাবে, এই রূপান্তরগুলি অর্জন করতে ক্রমে কোনও বাধা বা কিছু ক্রিয়াকলাপ ব্যবহার করার উপায় নেই। মৌলিক সমস্যাটি হ'ল একটি পূর্ণসংখ্যা সংযোজনের মতো কোনও কিছুর অপারেশনাল সিনটিক্স বাস্তবায়নের জন্য পুরোপুরি পরিচিত । এটি তাদের অনুকরণ করতে পারে, এটি জানে যে তারা সঠিক প্রোগ্রামগুলি দ্বারা পর্যবেক্ষণ করতে পারে না এবং এগুলি সর্বদা ঘুরে বেড়াতে মুক্ত free
আমরা এটি প্রতিরোধের চেষ্টা করতে পারি, তবে এটির চূড়ান্ত নেতিবাচক ফলাফল থাকতে পারে এবং শেষ পর্যন্ত ব্যর্থ হবে।
প্রথমত, সংকলকটিতে এটি প্রতিরোধের একমাত্র উপায় হ'ল এটি বলা যে এই সমস্ত মৌলিক ক্রিয়াকলাপ পর্যবেক্ষণযোগ্য। সমস্যাটি হ'ল এটি তখন সংখ্যক সংকলক অপ্টিমাইজেশনের অপ্রতিরোধ্য অংশকে আটকায়। সংকলকটির অভ্যন্তরে, আমাদের কাছে মডেল করার জন্য প্রয়োজনীয় কোনও ভাল ব্যবস্থা নেই যে সময়টি পর্যবেক্ষণযোগ্য তবে অন্য কিছু নয়। কী অপারেশনগুলি সময় নেয় তার একটি ভাল মডেল আমাদের কাছে নেই । উদাহরণস্বরূপ, 32-বিট স্বাক্ষরযুক্ত পূর্ণসংখ্যাকে 64-বিট স্বাক্ষরবিহীন পূর্ণসংখ্যায় রূপান্তর করতে সময় কি লাগে? এটি x86-64 এ শূন্য সময় নেয়, তবে অন্যান্য আর্কিটেকচারে এটি শূন্য-সময় নেয়। এখানে সাধারণভাবে সঠিক উত্তর নেই।
তবে আমরা এই অপারেশনগুলিকে পুনরায় সাজানো থেকে সংকলককে আটকাতে কিছু বীরত্বের মাধ্যমে সাফল্য পেলেও এটি যথেষ্ট হবে তার কোনও গ্যারান্টি নেই। একটি এক্স 86 মেশিনে আপনার সি ++ প্রোগ্রাম চালানোর জন্য একটি বৈধ এবং অনুসারে উপায় বিবেচনা করুন: ডায়নামরিওও। এটি এমন একটি সিস্টেম যা প্রোগ্রামটির মেশিন কোডকে গতিশীলভাবে মূল্যায়ন করে। এটি করতে পারে এমন একটি জিনিস হ'ল অনলাইন অপ্টিমাইজেশান এবং এটি এমনকি সময়কালের বাইরে মৌলিক গাণিতিক নির্দেশাবলীর পুরো পরিসীমা অনুমানমূলকভাবে সম্পাদন করতে সক্ষম। এবং এই আচরণটি গতিশীল মূল্যায়নকারীদের কাছে অনন্য নয়, আসল x86 সিপিইউ নির্দেশনাগুলি (একটি খুব কম সংখ্যক) নির্দেশনাও গতিবেগের সাথে পুনরায় অর্ডার করবে।
অপরিহার্য উপলব্ধি হ'ল পাটিগণিতটি পর্যবেক্ষণযোগ্য নয় (এমনকি সময়সীমার স্তরেও) এমন একটি যা কম্পিউটারের স্তরগুলিকে ঘিরে ফেলে। এটি সংকলক, রানটাইম এবং প্রায়শই হার্ডওয়ারের ক্ষেত্রেও সত্য। এটি পর্যবেক্ষণযোগ্য হতে বাধ্য করা উভয়ই নাটকীয়ভাবে সংকলককে সীমাবদ্ধ করবে, তবে এটি হার্ডওয়্যারকে নাটকীয়ভাবে সীমাবদ্ধও করবে।
তবে এই সমস্ত কিছুর কারণে আপনার আশা হারাতে হবে না। আপনি যখন মৌলিক গাণিতিক ক্রিয়াকলাপের সময় প্রয়োগ করতে চান, আমরা নির্ভরযোগ্যতার সাথে কার্যকর কৌশলগুলি অধ্যয়ন করেছি। সাধারণত এগুলি মাইক্রো-বেঞ্চমার্কিংয়ের সময় ব্যবহৃত হয় । CppCon2015 এ আমি এই বিষয়ে একটি কথা বলেছি: https://youtu.be/nXaxk27zwlk
সেখানে প্রদর্শিত কৌশলগুলি গুগলের মতো বিভিন্ন মাইক্রো-বেঞ্চমার্ক লাইব্রেরি দ্বারা সরবরাহ করা হয়েছে: https://github.com/google/benchmark#preventing-optimization
এই কৌশলগুলির মূল বিষয় হ'ল ডেটাতে ফোকাস করা। আপনি অপটিমাইজারের কাছে গণনাটিকে অস্বচ্ছ এবং ইনপুটটিকে অপ্টিমাইজারের কাছে গণনাটির অস্বচ্ছ করতে পারেন। একবার আপনি এটি করেনি, আপনি এটি নির্ভরযোগ্যভাবে সময় করতে পারেন। আসুন আসল প্রশ্নে উদাহরণের একটি বাস্তব সংস্করণটি দেখুন foo
তবে বাস্তবায়নের জন্য সম্পূর্ণরূপে দৃশ্যমান সংজ্ঞা দিয়ে । আমি DoNotOptimize
গুগল বেনমার্ক লাইব্রেরি থেকে একটি (বহনযোগ্য) সংস্করণও বের করেছি যা আপনি এখানে পেতে পারেন: https://github.com/google/benchmark/blob/master/incolve/benchmark/benchmark_api.h#L208
#include <chrono>
template <class T>
__attribute__((always_inline)) inline void DoNotOptimize(const T &value) {
asm volatile("" : "+m"(const_cast<T &>(value)));
}
// The compiler has full knowledge of the implementation.
static int foo(int x) { return x * 2; }
auto time_foo() {
using Clock = std::chrono::high_resolution_clock;
auto input = 42;
auto t1 = Clock::now(); // Statement 1
DoNotOptimize(input);
auto output = foo(input); // Statement 2
DoNotOptimize(output);
auto t2 = Clock::now(); // Statement 3
return t2 - t1;
}
এখানে আমরা নিশ্চিত করে নিই যে ইনপুট ডেটা এবং আউটপুট ডেটা গণনার আশেপাশে অপ-অপটিমাইজযোগ্য হিসাবে চিহ্নিত হয়েছে foo
এবং কেবলমাত্র সেই চিহ্নিতকারীদের কাছাকাছি সময় গণনা করা। যেহেতু আপনি গণনাটি মুদ্রণ করতে ডেটা ব্যবহার করছেন, এটি দুটি সময়ের মধ্যে থাকার গ্যারান্টিযুক্ত এবং তবুও গণনাটি নিজেই অনুকূলিতকরণের অনুমতিপ্রাপ্ত। ক্ল্যাং / এলএলভিএমের সাম্প্রতিক বিল্ড দ্বারা উত্পাদিত ফলাফল x86-64 সমাবেশটি হ'ল:
% ./bin/clang++ -std=c++14 -c -S -o - so.cpp -O3
.text
.file "so.cpp"
.globl _Z8time_foov
.p2align 4, 0x90
.type _Z8time_foov,@function
_Z8time_foov: # @_Z8time_foov
.cfi_startproc
# BB#0: # %entry
pushq %rbx
.Ltmp0:
.cfi_def_cfa_offset 16
subq $16, %rsp
.Ltmp1:
.cfi_def_cfa_offset 32
.Ltmp2:
.cfi_offset %rbx, -16
movl $42, 8(%rsp)
callq _ZNSt6chrono3_V212system_clock3nowEv
movq %rax, %rbx
#APP
#NO_APP
movl 8(%rsp), %eax
addl %eax, %eax # This is "foo"!
movl %eax, 12(%rsp)
#APP
#NO_APP
callq _ZNSt6chrono3_V212system_clock3nowEv
subq %rbx, %rax
addq $16, %rsp
popq %rbx
retq
.Lfunc_end0:
.size _Z8time_foov, .Lfunc_end0-_Z8time_foov
.cfi_endproc
.ident "clang version 3.9.0 (trunk 273389) (llvm/trunk 273380)"
.section ".note.GNU-stack","",@progbits
এখানে আপনি সংকলকটি foo(input)
একক নির্দেশকে কলটি অনুকূল করে তুলতে পারবেন addl %eax, %eax
, কিন্তু সময়টির বাইরে না সরিয়ে বা অবিচ্ছিন্ন ইনপুট সত্ত্বেও এটি সম্পূর্ণরূপে অপসারণ না করে।
আশা করি এটি সহায়তা করে এবং সি ++ স্ট্যান্ডার্ড কমিটি DoNotOptimize
এখানকার অনুরূপ এপিআইগুলিকে মানক করার সম্ভাবনা দেখছে ।