এই উত্তরটি বিদ্যমান জবাবগুলির সেটটিতে অবদানের উদ্দেশ্যে, যা আমি স্ট্যান্ড :: ফাংশন কলগুলির রানটাইম ব্যয়ের জন্য আরও অর্থবহ মানদণ্ড হিসাবে বিশ্বাস করি।
স্টাড :: ফাংশন প্রক্রিয়াটি যা সরবরাহ করে তার জন্য স্বীকৃত হওয়া উচিত: যে কোনও কলযোগ্য সত্তা উপযুক্ত স্বাক্ষরের একটি স্ট্যান্ড :: ফাংশনে রূপান্তরিত হতে পারে। ধরুন আপনার কাছে এমন একটি লাইব্রেরি রয়েছে যা z = f (x, y) দ্বারা সংজ্ঞায়িত কোনও ফাংশনের সাথে একটি পৃষ্ঠের সাথে ফিট করে, আপনি এটি গ্রহণ করতে লিখতে পারেনstd::function<double(double,double)>
, এবং গ্রন্থাগারের ব্যবহারকারী যে কোনও কলযোগ্য সত্তাকে সহজেই রূপান্তর করতে পারবেন; এটি কোনও সাধারণ ক্রিয়াকলাপ, শ্রেণীর উদাহরণের পদ্ধতি, বা ল্যাম্বডা বা স্ট্যান্ড :: বাইন্ড দ্বারা সমর্থিত যে কোনও কিছু হোক।
টেমপ্লেটের পদ্ধতির থেকে পৃথক, এটি বিভিন্ন ক্ষেত্রে লাইব্রেরির ফাংশনটি পুনরায় কম্পাইল না করে কাজ করে; তদনুসারে, প্রতিটি অতিরিক্ত ক্ষেত্রে সামান্য অতিরিক্ত সংকলিত কোড প্রয়োজন। এটি ঘটানো সর্বদা সম্ভব ছিল তবে এটিতে কিছু বিশ্রী ব্যবস্থা প্রয়োজন হত এবং লাইব্রেরির ব্যবহারকারীর সম্ভবত এটির কাজটি করার জন্য তাদের ফাংশনটির চারপাশে একটি অ্যাডাপ্টার তৈরি করা প্রয়োজন। std :: ফাংশন একটি সাধারণ পেতে অ্যাডাপ্টারের যা প্রয়োজন তা স্বয়ংক্রিয়ভাবে তৈরি করে সমস্ত ক্ষেত্রে রানটাইম কল ইন্টারফেস করে, যা একটি নতুন এবং খুব শক্তিশালী বৈশিষ্ট্য।
আমার দৃষ্টিতে, পারফরম্যান্সের দিক থেকে এটি স্ট্যান্ড :: ফাংশনের জন্য সর্বাধিক গুরুত্বপূর্ণ ব্যবহারের ক্ষেত্র: আমি একটি স্ট্যান্ড :: ফাংশনটি একবার নির্মাণের পরে অনেকবার কল করার জন্য আগ্রহী, এবং এটি করা দরকার এমন একটি পরিস্থিতি হয়ে উঠুন যেখানে সংকলকটি আসলে কল করা ফাংশনটি জানতে পেরে কলটি অনুকূলিত করতে অক্ষম হয় (অর্থাত্ একটি যথাযথ মাপদণ্ড পেতে আপনাকে অন্য উত্স ফাইলে প্রয়োগটি আড়াল করতে হবে)।
আমি নীচে পরীক্ষা করেছিলাম, ওপি'র মতো; তবে প্রধান পরিবর্তনগুলি হ'ল:
- প্রতিটি কেস 1 বিলিয়ন বার লুপ করে তবে স্টাড :: ফাংশন অবজেক্টগুলি কেবল একবারই নির্মিত হয়। আমি আউটপুট কোডটি দেখে দেখেছি যে আসল স্টাডি :: ফাংশন কল নির্মাণের সময় 'অপারেটর নতুন' ডাকে optim
- অনাকাঙ্ক্ষিত অপ্টিমাইজেশন প্রতিরোধের জন্য পরীক্ষা দুটি ফাইলে বিভক্ত
- আমার কেসগুলি হ'ল: (ক) ফাংশনটি ইনলাইনড (খ) ফাংশনটি একটি সাধারণ ফাংশন পয়েন্টার (সি) ফাংশনটি স্ট্যান্ড :: ফাংশন (ডি) ফাংশনটি একটি স্ট্যান্ড :: সাথে সামঞ্জস্যপূর্ণ একটি বেমানান ফাংশন হিসাবে আবৃত একটি সামঞ্জস্যপূর্ণ ফাংশন) বাঁধাই, স্ট্যান্ড :: ফাংশন হিসাবে মোড়ানো
আমি যে ফলাফল পেয়েছি তা হ'ল:
কেস (ডি) সামান্য ধীর হতে থাকে তবে পার্থক্য (প্রায় 0.05 এনএসসি) শব্দে শোষিত হয়।
উপসংহারটি হল যে স্ট্যান্ড :: ফাংশনটি ফাংশন পয়েন্টারটি ব্যবহার করার সাথে ওভারহেডের (কল সময়ে) তুলনাযোগ্য, এমনকি যখন আসল ফাংশনে সহজ 'বাঁধাই' অভিযোজন থাকে when ইনলাইনটি অন্যদের চেয়ে 2 এনএস দ্রুততর তবে এটি একটি প্রত্যাশিত ট্রেড অফ since কারণ ইনলাইনটি কেবলমাত্র রান টাইমে 'হার্ড-ওয়্যার্ড' case
আমি যখন একই মেশিনে জোহান-লুন্ডবার্গের কোডটি চালাচ্ছি, তখন আমি লুপ প্রতি প্রায় 39 এনসিএস দেখতে পাচ্ছি, তবে সেখানে লুপে আরও অনেক কিছু রয়েছে, স্ট্যান্ড :: ফাংশনের প্রকৃত নির্মাতা এবং ডেস্ট্রাক্টর সহ, যা সম্ভবত বেশ উচ্চতর যেহেতু এটিতে একটি নতুন এবং মুছুন জড়িত।
-ও 2 জিসিসি 4.8.1, থেকে x86_64 লক্ষ্য (কোর আই 5)।
দ্রষ্টব্য, সংকলকটি যেখানে ডাকা হয় তার ফাংশনগুলি প্রসারিত করা থেকে বিরত রাখতে কোডটি দুটি ফাইলে বিভক্ত হয়ে গেছে (কেবল যেখানে এটির উদ্দেশ্যে তৈরি হয়েছে সেখানে বাদে)।
----- প্রথম উত্স ফাইল --------------
#include <functional>
// simple funct
float func_half( float x ) { return x * 0.5; }
// func we can bind
float mul_by( float x, float scale ) { return x * scale; }
//
// func to call another func a zillion times.
//
float test_stdfunc( std::function<float(float)> const & func, int nloops ) {
float x = 1.0;
float y = 0.0;
for(int i =0; i < nloops; i++ ){
y += x;
x = func(x);
}
return y;
}
// same thing with a function pointer
float test_funcptr( float (*func)(float), int nloops ) {
float x = 1.0;
float y = 0.0;
for(int i =0; i < nloops; i++ ){
y += x;
x = func(x);
}
return y;
}
// same thing with inline function
float test_inline( int nloops ) {
float x = 1.0;
float y = 0.0;
for(int i =0; i < nloops; i++ ){
y += x;
x = func_half(x);
}
return y;
}
----- দ্বিতীয় উত্স ফাইল -------------
#include <iostream>
#include <functional>
#include <chrono>
extern float func_half( float x );
extern float mul_by( float x, float scale );
extern float test_inline( int nloops );
extern float test_stdfunc( std::function<float(float)> const & func, int nloops );
extern float test_funcptr( float (*func)(float), int nloops );
int main() {
using namespace std::chrono;
for(int icase = 0; icase < 4; icase ++ ){
const auto tp1 = system_clock::now();
float result;
switch( icase ){
case 0:
result = test_inline( 1e9);
break;
case 1:
result = test_funcptr( func_half, 1e9);
break;
case 2:
result = test_stdfunc( func_half, 1e9);
break;
case 3:
result = test_stdfunc( std::bind( mul_by, std::placeholders::_1, 0.5), 1e9);
break;
}
const auto tp2 = high_resolution_clock::now();
const auto d = duration_cast<milliseconds>(tp2 - tp1);
std::cout << d.count() << std::endl;
std::cout << result<< std::endl;
}
return 0;
}
আগ্রহীদের জন্য, এখানে 'মুল_বি'কে একটি ভাসা (ভাসা) এর মতো দেখতে তৈরি করা সংযোজকটি এখানে তৈরি করা হয়েছে - বাঁধন (mul_by, _1,0.5) হিসাবে তৈরি ফাংশনটি যখন বলা হয় তখন এটিকে বলা হয়:
movq (%rdi), %rax ; get the std::func data
movsd 8(%rax), %xmm1 ; get the bound value (0.5)
movq (%rax), %rdx ; get the function to call (mul_by)
cvtpd2ps %xmm1, %xmm1 ; convert 0.5 to 0.5f
jmp *%rdx ; jump to the func
(সুতরাং আমি যদি বাইন্ডে 0.5f লিখে থাকি তবে এটি কিছুটা দ্রুত হতে পারে ...) নোট করুন যে 'x' প্যারামিটারটি% xmm0 এ আসে এবং কেবল সেখানেই থাকে।
টেস্ট_স্টেডফুনক কল করার আগে, ফাংশনটি তৈরি করা হয়েছে সেই অঞ্চলে এখানে কোডটি দেওয়া হয়েছে - সি ++ ফিল্টের মাধ্যমে চালানো:
movl $16, %edi
movq $0, 32(%rsp)
call operator new(unsigned long) ; get 16 bytes for std::function
movsd .LC0(%rip), %xmm1 ; get 0.5
leaq 16(%rsp), %rdi ; (1st parm to test_stdfunc)
movq mul_by(float, float), (%rax) ; store &mul_by in std::function
movl $1000000000, %esi ; (2nd parm to test_stdfunc)
movsd %xmm1, 8(%rax) ; store 0.5 in std::function
movq %rax, 16(%rsp) ; save ptr to allocated mem
;; the next two ops store pointers to generated code related to the std::function.
;; the first one points to the adaptor I showed above.
movq std::_Function_handler<float (float), std::_Bind<float (*(std::_Placeholder<1>, double))(float, float)> >::_M_invoke(std::_Any_data const&, float), 40(%rsp)
movq std::_Function_base::_Base_manager<std::_Bind<float (*(std::_Placeholder<1>, double))(float, float)> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation), 32(%rsp)
call test_stdfunc(std::function<float (float)> const&, int)
std::function
কেবলমাত্র যদি আপনার কলযোগ্যযোগ্য বস্তুর বিজাতীয় সংগ্রহের প্রয়োজন হয় তবেই ব্যবহার করুন (যেমন রানটাইমে কোনও বৈষম্যমূলক তথ্য উপলব্ধ নেই)।