std::function のオーバーヘッド計測
C++ の std::function による呼び出しオーバーヘッドを計測してみました.
実験環境
手持ちのノートパソコンで実験しました.
- NEC LAVIE Direct HZ
- Ubuntu 18.04
- Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz
- Samsung SSD 960 EVO 500GB
- Clang 6.0.0
結果概要
std::function を経由したラムダ式の呼び出しは,直接呼び出した場合に比べ 5 倍程度のオーバーヘッドがある.
検証ソースコード
main.cpp
#include <iostream>
#include <functional>
#include <chrono>
using namespace std::chrono;
int main(int argc, char** argv) {
const int kLoop = 300000000;
int count = 0;
auto f{
[&]{ count += argc; }
};
std::function g{
[&]{ count += argc; }
};
system_clock::time_point start, end;
count = 0;
start = system_clock::now();
for (int i = 0; i < kLoop; ++i) {
f();
}
end = system_clock::now();
std::cout << count << ", "
<< duration_cast<milliseconds>(end - start).count() << "ms" << std::endl;
count = 0;
start = system_clock::now();
for (int i = 0; i < kLoop; ++i) {
g();
}
end = system_clock::now();
std::cout << count << ", "
<< duration_cast<milliseconds>(end - start).count() << "ms" << std::endl;
}
検証手順
$ clang++ -std=c++17 -Wall -g -O0 -o stdfunc_opt0 main.cpp $ clang++ -std=c++17 -Wall -g -O3 -o stdfunc_opt3 main.cpp $ ./stdfunc_opt0 300000000, 856ms 300000000, 4508ms $ ./stdfunc_opt0 300000000, 853ms 300000000, 4540ms $ ./stdfunc_opt3 300000000, 0ms 300000000, 621ms $ ./stdfunc_opt3 300000000, 0ms 300000000, 618ms
結果詳細
最適化なし(-O0)でコンパイルした場合,出力された機械語を見ると f も g も call 命令にエンコードされています.
したがって,最適化なしの場合の比率が std::function の呼び出しオーバーヘッドとなると思って良いと思います.
最適化あり(-O3)でコンパイルした場合, f の呼び出しは eax レジスタに kLoop を乗ずるという処理に最適化されてしまっており全く参考になりません.
一方で g の呼び出しは std::function の処理自体はインライン展開されているものの,最後にラムダ式を呼ぶ部分は call が残っていました.
したがって,g の呼び出しに 600 ミリ秒強というのはある程度参考になる値かと思います.
620 msec / 300000000 ≒ 2.07 nsec
ですので,std::function を使ったラムダ式の呼び出しは 1 回あたり 2.07 nsec ということになります.
おそらくほとんどがキャッシュに載っている世界の話でしょうから,時間の絶対値に意味があるとは思いませんが.参考値として.