ダリアメモ@ブログ

日々思ったことや自分用のメモも含めプログラムの技術を淡々と書いていきます

C++での高速化の話 ②インクリメント編

今回はインクリメントでの高速化での実装です。

今までインクリメントする時、下のコードのようにインクリメント(++の事)を後ろに置いてばかりいました。

std::vector<int> v = {0,1,2,3,4,5,6,7,8,9};
for(int i =0, size = v.size(); i < size; i++)
{
    //処理内容
}

が、インクリメントが前か後ろによって速度が違うということを知りました。
計算結果は同じなのに速度が違うのは何故?と思って色々調べていたら、
インクリメントの処理がwikiにあったので載せておきます。

// 後置インクリメントのオーバーロード
T class T::operator++(int)
{ 
    T old = *this; 
    *this += 1; 
    return old;
}    

このコードを見る限り、インクリメントが後ろにある場合だと、
値を仮変数oldに保持してからインクリメントするという仕様みたいです。
この一時的に変数を作られるのが速度低下の原因みたいですね。

訂正:operator++に()を入れ忘れていました。申し訳ございません。

// 前置インクリメントのオーバーロード
T& class T::operator++()
{   *this += 1; 
    return *this; 
}    

一方で前の方にインクリメントがある場合だと
余計な変数が作られないので速いみたいですね。


特にイテレータ時に速度の違いが顕著になると聞いて、
C++11から標準で入ったchronoライブラリを使って実際に計測してみました。
実際にやったコードは下になります。
※速度の検証がしたいのでコードは適当です。ご了承ください。

#include <iostream>
#include <chrono>
#include <vector>

int main()
{
	const int N = 10000;
	std::vector<int> v;

	//N個サイズを確保
	v.resize(N);

	//計測開始時間を保存
	auto start = std::chrono::system_clock::now();	
	for (auto i = v.begin(), end = v.end(); i != end; ++i)
	{
		(*i) = 0;
	}
	//計測終了時間を保存
	auto end = std::chrono::system_clock::now();
	
	//かかった時間を計測
	auto dur = end - start;							
	
	//ミリ秒に変換
	auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();		//ミリ秒に変換
	//秒で見たい人用。今のままだと前置も後置も0秒なのでNの数を増やしてみてください。
	auto seconds = std::chrono::duration_cast<std::chrono::seconds>(dur).count();		//秒に変換

	std::cout << msec << " m/s" << std::endl;
	std::cout << seconds << " s" << std::endl;

	getchar();
	return 0;
}

計測結果

回数 ++ii++
1回目 16ms52ms
2回目 13ms48ms
3回目 15ms47ms
実際に実行してみると計測結果がたびたび違いますが、だいぶ違うようです。
このコードだとintだからいいですが、 構造体やclassになると速度の差が大幅に違う事になりそうなので気をつけたい所です。