🧐
引数の渡し方、どれが速いんだろう?
よく stackoverflow で見る質問について書こうと思います。
引数を渡す時は、以下のの3つのパターンがあると思います。
- 値渡し
- 値渡しを使うと渡す変数がコピーされます。
- ポインタ渡し
- ポインタ渡しは変数のメモリ上のアドレスのみを渡すことになるため、コピーは発生しないです。パフォーマンス的に良いのですが、
NULL
ポインタの可能性があるためNULL
チェックしないといけないせいで、できれば避けた方がいいです。 - 入力引数の場合は
const
を付けるといいです。
- ポインタ渡しは変数のメモリ上のアドレスのみを渡すことになるため、コピーは発生しないです。パフォーマンス的に良いのですが、
- 参照渡し
- 参照渡しは C言語にはなくて、C++ で新たに追加された機能で、基本は
NULL
になれないポインタのような認識でいいと思います。 - 入力引数の場合は
const
を付けるといいです。
- 参照渡しは C言語にはなくて、C++ で新たに追加された機能で、基本は
大きな型を渡す時は、値渡しは避けた方がいいのは常識だと思いますが。基本型の場合は実はコピーの方が速いから値渡しの方が良いと言われます。
でもその境目はどこでしょう?
基本型は値渡しの方が速いって本当でしょうか?
// 値渡し
int f1(int number)
{
return number * number;
}
// 参照渡し
int f2(const int& number)
{
return number * number;
}
// ポインタ渡し
int f3(const int* number)
{
return *number * *number;
}
こういう時は実際にベンチマークテストで実験するに限るので、以下のサイトでパフォーマンスを計ってみました。
以下の結果になりました。
ポインタ渡しの方が僅かに速いぐらいで、殆ど変わらなかったです。やはりこの場合は一番簡単な方法でいいと思うので、値渡しでいい気がします。
このサイトは複数のコードのパフォーマンスを比較したい時に非常に簡単にベンチマークを作って、結果をグラフとして見せてくれる神サイトなのです。色々なオプションとコンパイラで比較できます。
因みにstd::string
だったら、どちらが早いでしょうか?
// 値渡し
std::string f1(std::string str)
{
return str.substr(5, 3);
}
// 参照渡し
std::string f2(const std::string& str)
{
return str.substr(5, 3);
}
// ポインタ渡し
std::string f3(const std::string* str)
{
return str->substr(5, 3);
}
こちらもquick-bench
で比べてみたら、以下の結果になりました。
値渡しの方が明らかに遅かったです。ポインタ渡しと参照渡しはほぼ同じでした。
もっと複雑なデータを使ったり、処理を変えたり、コンパイラやオプションを変えたりすると結果が変わるかもしれないですが、本当は今回このサイトを紹介したかっただけです。是非使ってみてください。
Discussion
そのレベルの速度を計測する場合は、コンパイラなどの処理系や実行環境、ビルド時のオプションや実行方法を合わせないと全く意味のない結論になります。また、結果の理由を知るには、最低限アセンブラまで見ないといけません。現象から傾向だけ知るにしても、もっと複数の環境やオプションについて大量の結果が必要になるでしょう。
もしサイトの紹介をしたいのであれば、最低限そういう前提の話をしてからでないといけないと思いますよ。