++i is better than i++ in C++?
タイトルの答えは条件付きの Yes。その条件は「インクリメントする式の値を無視する場合」というもの。
たとえば次のように単にi
をインクリメントしたい場合++i
と書くほうが良い。
int i = 0; while (true) { // use i i++; }
何故「良い」のかはGoogleのC++スタイルガイドに書かれている。
要約するとi++
の場合i
のコピーをつくる必要があり*1、その必要がない++i
と比べ*2メモリがお得だからという理由。
さらにC++の場合オペレータのオーバーロードができる。そのため i
が整数でない場合、より両者の性能差が広がる可能性がある。ガイドの次の文はおそらくこのことを述べている。
If i is an iterator or other non-scalar type, copying i could be expensive.
それでは Proof Of Concept として次の実験をしてみよう。
実験
前置++
と後置++
をオーバーロードしたクラスを用意し、両者の性能差を計測する。
クラス
クラス | 説明 |
---|---|
HugeClass |
実験対象のクラス |
Timer |
実行時間の計測用クラス |
なお今回のTimer
の実装はC++でフリープラットフォームな時間計測を参考にした。
コード
#include <iostream> #include <chrono> using namespace std; class HugeClass { public: // prefix increment HugeClass& operator ++() { for (int &num : this->nums) { ++num; } return *this; } // postfix increment HugeClass operator ++(int) { HugeClass ret = *this; for (int &num : this->nums) { num++; } return ret; } private: int nums[100000]; }; class Timer { private: chrono::system_clock::time_point startTime; chrono::system_clock::time_point endTime; public: void start(){ this->startTime = chrono::system_clock::now();}; void end(){ this->endTime = chrono::system_clock::now();}; chrono::microseconds diff(){ return chrono::duration_cast<chrono::microseconds>( this->endTime - this->startTime ); }; }; int main() { int REPEAT_NUMBER = 10000; // post increment HugeClass hc_post = HugeClass(); Timer t_post = Timer(); t_post.start(); for (int i = 0; i < REPEAT_NUMBER; i++) { hc_post++; } t_post.end(); // pre increment HugeClass hc_pre = HugeClass(); Timer t_pre = Timer(); t_pre.start(); for (int i = 0; i < REPEAT_NUMBER; i++) { ++hc_pre; } t_pre.end(); cout << "post inc: " << t_post.diff().count() << " " << endl; cout << "pre inc: " << t_pre.diff().count() << " ticks" << endl; }
結果
wandbox上で試した結果、期待通り後置インクリメントが遅かった。
post inc: 3606617 ticks pre inc: 3338294 ticks
今回はインクリメントに話題を限定したが、デクリメントも同様の議論がなりたつ。