レベルアップ!

備忘録として綴ります。

一番効率のいいループ文はどれ?(std::vector)

C++11からauto型という型推論が使えるようになります(何年前の話だよって話ではありますが)
「auto型は便利だけど型が分かってるならそっち使った方がいいんじゃ?」という疑問から、検証しようと思いました。
しかしその検証にて、いろんなループ文を利用してみたところ結果に差が出たのでそちらを記事にします。
尚、効率の良い=処理速度が速いです。

検証したループ文はこちら

std::vector<int> intvector;
①for (int i = 0; i < intvector.size(); i++)

②for (std::vector<int>::iterator i = intvector.begin(); i != intvector.end(); i++)

③for (auto i = intvector.begin(); i != intvector.end(); i++)

④for (int i : intvector)

⑤for (auto i : intvector)

⑥std::for_each(intvector.begin(), intvector.end(),Hoge);

私は⑤ばかりつかってます。求めている内容によっては①の時もありますが基本は⑤ですね。
したい処理によってどれを使うかはかなり変わってくると思いますが、とりあえず処理速度の観点のみで見ていきたいと思います。

用意したコードはこちら

#include <iostream>
#include <vector>
#include <algorithm>
#include <Windows.h>

//for_each用関数
void Hoge(int x)
{
	int a = x;
}

int main()
{
	std::vector<int> intvector;
	const int MAX = 1000*1000;

	//値を代入
	for (int i = 0; i < MAX; i++)
	{
		intvector.push_back(i);
	}

	{//初期化int型、サイズ分回す、増分処理
		std::cout << "int ; size ; i++___";
		DWORD start =  GetTickCount();
		int count = 0;
		for (int i = 0; i < intvector.size(); i++)
		{
			int a = intvector[count++];
		}
		DWORD end =  GetTickCount();
		std::cout << (double)(end - start) / 1000 << "\n" << std::endl;
	}

	{//初期化イテレータ、終わりまで回す、増分処理
		std::cout << "iterator ; end ; i++___";
		DWORD start =  GetTickCount();
		for (std::vector<int>::iterator i = intvector.begin(); i != intvector.end(); i++)
		{
			int a = *i;
		}
		DWORD end =  GetTickCount();
		std::cout << (double)(end - start) / 1000 << "\n" << std::endl;
	}

	{//初期化auto型、終わりまで回す、増分処理
		std::cout << "auto ; end ; i++___";
		DWORD start =  GetTickCount();
		for (auto i = intvector.begin(); i != intvector.end(); i++)
		{
			int a = *i;
		}
		DWORD end =  GetTickCount();
		std::cout << (double)(end - start) / 1000 << "\n" << std::endl;
	}

	{//初期化int型、サイズ分まわす(終わりまで回す)
		std::cout << "int : vector___";
		DWORD start =  GetTickCount();
		for (int i : intvector)
		{
			int a = i;
		}
		DWORD end =  GetTickCount();
		std::cout << (double)(end - start) / 1000 << "\n" << std::endl;
	}

	{//初期化auto型、サイズ分まわす(終わりまで回す)
		std::cout << "auto : vector___";
		DWORD start =  GetTickCount();
		for (auto i : intvector)
		{
			int  a = i;
		}
		DWORD end =  GetTickCount();
		std::cout << (double)(end - start) / 1000 << "\n" << std::endl;
	}

	{//for_each
		std::cout << "for_each___";
		DWORD start = GetTickCount();
		std::for_each(intvector.begin(), intvector.end(),Hoge);
		DWORD end = GetTickCount();
		std::cout << (double)(end - start) / 1000 << "\n" << std::endl;
	}
	std::getchar();
	return 0;
}

前回記事同様、秒で処理結果を出します。
結果を顕著に出すため100万回回してみました。
その結果がこちらっ!
f:id:jagabeeInitialize:20180123235143p:plain
④と⑤の0秒は驚きを隠せないです。表示される数字から0.001未満秒なのかもしれません。
④と⑤は

範囲ベースのfor文(range-based for statement)または、範囲ベースのforループ(range-based for loop)は、C++11で新たに取り入れられた言語機能です。for (要素 : コンテナ)という形式で利用します。

C++のforeach文(範囲ベースfor)【range-based for, for_each】 | MaryCore
とのことです。

初期化式 ; 終了条件 ; 増分処理 と一々書くよりかは④や⑤の書き方の方がすっきりすると思います。
ループ文の中の処理があまりに長くなりそうであればstd::for_eachの方が可読性が高そうですね。


参考
auto型
auto - cpprefjp C++日本語リファレンス
怖いものなんてない!!: C++の「auto型」について
C++11 auto の使い方 - プログラミングの教科書を置いておくところ

for文
C++のforeach文(範囲ベースfor)【range-based for, for_each】 | MaryCore
C++11 範囲ベース for ループ 入門