0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

std::vector, std::deque, std::listの各操作に掛かる時間について計測してみた。

Posted at

はじめに

こんにちは。
42tokyoといったところで、コンピュータサイエンス?を学んでいます。
最近、Merge-insertion sort(Ford–Johnson algorithm)の実装を行う課題を通して、C++98のコンテナについて調べました。
今回は、std::vector, std::deque, std::listの各操作に掛かる時間を計測した結果をまとめます。

間違い等があれば、ご指摘ください。

参考

実行環境

環境
MacBook Pro
プロセッサ 2.3 GHz 8 コア Intel Core i9
c++ -v
Apple clang version 15.0.0 (clang-1500.3.9.4)
Target: x86_64-apple-darwin23.5.0
Thread model: posix

まとめ

各操作の実行結果は画像の通り
スクリーンショット 2024-06-19 15.10.40.png

各コンテナの概要

std::vector

  • 連続したメモリ領域を確保し要素を格納する
  • 必要に応じて動的にメモリを確保する
  • 末尾に対する操作が高速で行われる
各操作に掛かる計算量
操作 計算量($O(x)$)
ランダムアクセス 少ない(常に$O(1)$)
終端に要素の追加、または削除 少ない(常に$O(1)$)
終端以外に要素の追加、または削除 終端までの距離($n$)に比例($O(n)$)

std::deque

  • 固定サイズの配列を確保し要素を格納する
  • 必要に応じて動的にメモリを確保するが、各配列は連続したメモリ領域に配置されない
  • 先頭と末尾に対する操作が高速で行われる
各操作に掛かる計算量
操作 計算量($O(x)$)
ランダムアクセス 少ない(常に$O(1)$)
終端に要素の追加、または削除 少ない(常に$O(1)$)
終端以外に要素の追加、または削除 全体の要素数($n$)に比例($O(n)$)

std::list

  • 双方向リスト
各操作に関して
操作 計算量($O(x)$)
ランダムアクセス 高速なアクセスとは言えない
要素の追加、または削除 常に一定時間を要する

各テストのコード

事前準備

~ ~ ~
std::srand(std::time(NULL));
std::clock_t	start, finish;
double			time;

int nums[1000000];
for (int i = 0; i < 1000000; i++) { nums[i] = std::rand() % 1000000; }

std::vector<int>	vec;
std::deque<int>		deq;
std::list<int>		lst;
~ ~ ~

終端に要素を追加するテスト

計測した処理の実装のみ記述

start = std::clock();
for (int i = 0; i < 1000000; i++) { vec.push_back(nums[i]); }
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

start = std::clock();
for (int i = 0; i < 1000000; i++) { deq.push_back(nums[i]); }
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

start = std::clock();
for (int i = 0; i < 1000000; i++) { lst.push_back(nums[i]); }
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

ランダムな位置の要素にアクセスするテスト

このテストでは、終端に要素を追加するテストで作成したコンテナを使用

start = std::clock();
for (int i = 0; i < 1000000; i++) { vec[nums[i]] = nums[i]; }
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

start = std::clock();
for (int i = 0; i < 1000000; i++) { deq[nums[i]] = nums[i]; }
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

std::list<int>::iterator	beginIt = lst.begin();
std::list<int>::iterator	endIt = lst.end();
int							lstSize = lst.size();
int							middleIndex = lstSize / 2;
start = std::clock();
for (int i = 0; i < 1000; i++) {
    if (nums[i] < middleIndex) {
    	std::advance(beginIt, nums[i]);
    	*beginIt = nums[i];
    	beginIt = lst.begin();
    } else {
    	std::advance(endIt, nums[i] - lstSize);
    	*endIt = nums[i];
    	endIt = lst.end();
    }
}
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

ランダムな位置で要素を追加するテスト

このテストでは、終端に要素を追加するテストで作成したコンテナを使用

std::vector<int>::iterator	vecBeginIt, vecEndIt;
int							vecSize = vec.size();
int							vecMiddleIndex = vecSize / 2;
start = std::clock();
for (int i = 0; i < 1000; i++) {
	if (nums[i] < vecMiddleIndex) {
		vecBeginIt = vec.begin();
		std::advance(vecBeginIt, nums[i]);
		vec.insert(vecBeginIt, nums[i]);
	} else {
		vecEndIt = vec.end();
		std::advance(vecEndIt, nums[i] - vecSize);
		vec.insert(vecEndIt, nums[i]);
	}
	vecSize = vec.size();
	vecMiddleIndex = vecSize / 2;
}
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

std::deque<int>::iterator	deqBeginIt, deqEndIt;
int							deqSize = deq.size();
int							deqMiddleIndex = deqSize / 2;
start = std::clock();
for (int i = 0; i < 1000; i++) {
	if (nums[i] < deqMiddleIndex) {
		deqBeginIt = deq.begin();
		std::advance(deqBeginIt, nums[i]);
		deq.insert(deqBeginIt, nums[i]);
	} else {
		deqEndIt = deq.end();
		std::advance(deqEndIt, nums[i] - deqSize);
		deq.insert(deqEndIt, nums[i]);
	}
	deqSize = deq.size();
	deqMiddleIndex = deqSize / 2;
}
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

std::list<int>::iterator	lstBeginIt, lstEndIt;
int							lstSize = lst.size();
int							lstMiddleIndex = lstSize / 2;
start = std::clock();
for (int i = 0; i < 1000; i++) {
	if (nums[i] < lstMiddleIndex) {
		lstBeginIt = lst.begin();
		std::advance(lstBeginIt, nums[i]);
		lst.insert(lstBeginIt, nums[i]);
	} else {
		lstEndIt = lst.end();
		std::advance(lstEndIt, nums[i] - lstSize);
		lst.insert(lstEndIt, nums[i]);
	}
	lstSize = lst.size();
	lstMiddleIndex = lstSize / 2;
}
finish = std::clock();
time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

先頭に要素を追加するテスト

	std::vector<int>	vec;
	std::deque<int>		deq;
	std::list<int>		lst;

	start = std::clock();
	for (int i = 0; i < 100000; i++) { vec.insert(vec.begin(), nums[i]); }
	finish = std::clock();
	time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

	start = std::clock();
	for (int i = 0; i < 100000; i++) { deq.push_front(nums[i]); }
	finish = std::clock();
	time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

	start = std::clock();
	for (int i = 0; i < 100000; i++) { lst.push_front(nums[i]); }
	finish = std::clock();
	time = static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000.0;

さいごに

初めて、比較することを意識してコードを書いてみました。
正直なところ「これで正しく比較ができているのか?」といった気持ちです。
不備があればお申し付けください。
ありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?