C++のforループの種類ごとの性能比較

++11になってforeach文が使えるようになり、for文が非常に書きやすくなりましたが、
フォーマンスのほうはどうなっているのか少々気になったので検証してみました。

今回行った検証は、vectorの全要素探索処理を下記5種類のfor文を使った場合の、各処理
の速度比較です。


・for文の種類

今回、下記4種類のfor文の速度の比較を行いました

  • size関数+カウンタの加算による探索
    • for (size_t i = 0; i < v.size(); ++i) ...
  • sizeをキャッシュしたカウンタ加算
    • for (size_t s = v.size(), i = 0; i < v.size(); ++i) ...
  • イテレータ 前方++
    • for (vector::iterator i = v.begin(); i != v.end(); ++i) ...
  • イテレータ 後方++
    • for (vector::iterator i = v.begin(); i != v.end(); i++) ...
  • foreach
    • for (int& i : v) ...



・検証コード

#include "stdafx.h"

#include <stdio.h>

#include <Windows.h>

#include <vector>
#include <iostream>
using namespace std;

static const int ARRAY_SIZE = 100000000;

std::vector<int> a;

// 指定した関数の処理時間を測定する
void FuncSpeedTest(wchar_t* name, void (*func)(void))
{
  a.assign(ARRAY_SIZE, 0);

  DWORD t = GetTickCount();

  func();

  wcout << name << L" : " << GetTickCount() - t << endl;
}

// size関数直接呼出し+カウンタ加算
void IntegerIncrement(void)
{
  for (size_t i = 0; i < a.size(); ++i)
  {
    a[i]++;
  }
}

// sizeをキャッシュしておく
void IntegerIncrementPreserve(void)
{
  for (size_t s = a.size(), i = 0; i < s; ++i)
  {
    a[i]++;
  }
}

// iteratorで前++
void IteratorF(void)
{
  for (vector<int>::iterator i = a.begin(); i != a.end(); ++i)
  {
    (*i)++;
  }
}

// iteratorで後++
void IteratorB(void)
{
  for (vector<int>::iterator i = a.begin(); i != a.end(); i++)
  {
    (*i)++;
  }
}

// foreach
void IncrementCx11(void)
{
  for (int& i : a)
  {
    i++;
  }
}

int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _DEBUG
  wcout << L"DebugMode" << endl;
#else
  wcout << L"ReleaseMode" << endl;
#endif

  wcout << L"ARRAY_SIZE : " << ARRAY_SIZE << endl;

  FuncSpeedTest(L"counter      ", IntegerIncrement);
  FuncSpeedTest(L"counter cache", IntegerIncrementPreserve);
  FuncSpeedTest(L"iterator ++i ", IteratorF);
  FuncSpeedTest(L"iterator i++ ", IteratorB);
  FuncSpeedTest(L"foreach      ", IncrementCx11);

  wcout << L"[END]" << endl;
  getchar();
  return 0;
}


若干長いですが、特に難しいことはしておらず、適当にvectorを定義し、全要素に対して
アクセスする処理に必要な時間を測定しています。


・測定結果

snapshot_265_140405_233159.png

上記の検証コードを、VisualStudio2012のDebugモード、Releaseモード両方で実行した場合の出力を
エクセルで表示+各処理の処理時間の比較を追加した表です。

実際やってみると私の予想とはかなり違う結果になってしまいました。

この検証を行う前は、おそらくイテレータの前置++とforeachが同じ速度になると予想していたのですが、
実際には、カウンタ、イテレータ、foreachそれぞれ明らかに処理速度が異なります。

表の数値を見ると、他にも結構色々面白い事実があらわれています。


  • Debugだとカウンタが、Releaseだとイテレータが最速、foreachは中間の速度。
  • カウンタ式がDebug時の速度の落ちが最も少ない。
  • カウンタ式の場合のsize関数のキャッシュはDebug、Releaseともに一定の効果がある。
  • イテレータはデバッグ時の速度の落ちが極めて大きい。
    • 後置++ならDebugはReleaseの2000倍遅い。
  • Releaseモードではカウンタはイテレータの3倍も遅い。
  • イテレータを使ったときに、前置++と後置++はDebug時には2倍の速度差があるが、Releaseでは殆ど違いはない。


人それぞれコーディングに対する考え方ががあると思いますが、私はこの結果を見ても、ソースの可読性を重視して

foreach > カウンタ > イテレータ

の順で、優先して使用すべきパターンなのではないかと思います。

リリース時に究極の速度を目指すのであれば、イテレータを優先的に使ったほうが良いようですが、
その場合、デバッグモードでは、foreachの1/2、カウンタの1/10の速度になってしまうようなので、注意が
必要です。
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

カレンダー
09 | 2017/10 | 11
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -
最新記事
カテゴリ
Qt (21)
SDL (2)
MFC (2)
検索フォーム
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
リンク
リンク(管理用)
FC2カウンター