コードのパフォーマンス測定
chronoでベンチマークを行います
「コードのパフォーマンス測定」はCoddyKit上の無料C++ Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC++ Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C++ Academyコースには全4レッスンが含まれています。
なぜベンチマークを行うのか
コードの実行時間を測定すると、ボトルネックを見つけたり、最適化の効果を確認したりできます。<chrono>を使うと、移植性があり型安全な時間計測を行えます。
- 最適化する前に測定します。
- 信頼できる間隔の計測にはsteadyなClockを使用します。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
auto start = steady_clock::now();
auto end = steady_clock::now();
std::cout << "Timed a block: " << ((end - start).count() >= 0) << '\n';
return 0;
}基本パターン
ベンチマークのパターンは常に同じです。処理の前にnow()を取得し、処理を実行し、処理の後にもう一度now()を取得して減算します。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
auto start = steady_clock::now();
long sum = 0;
for (int i = 0; i < 100000; ++i) sum += i;
auto end = steady_clock::now();
std::cout << "Sum: " << sum << ", elapsed >= 0: " << ((end - start).count() >= 0) << '\n';
return 0;
}単位の選択
処理内容に合った単位を選びます。短時間のループにはmicrosecondsまたはnanosecondsを、大きな処理にはmillisecondsを使用します。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
auto start = steady_clock::now();
long sum = 0;
for (int i = 0; i < 500000; ++i) sum += i;
auto end = steady_clock::now();
auto us = duration_cast<microseconds>(end - start);
std::cout << "Result: " << sum << ", us non-negative: " << (us.count() >= 0) << '\n';
return 0;
}浮動小数点数による時間計測
小数を含む、人間に読みやすい秒単位の値が必要な場合は、duration<double>を使用して1秒未満の精度を保ちます。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
auto start = steady_clock::now();
long sum = 0;
for (int i = 0; i < 200000; ++i) sum += i % 7;
auto end = steady_clock::now();
duration<double> secs = end - start;
std::cout << "Computed " << sum << ", seconds >= 0: " << (secs.count() >= 0) << '\n';
return 0;
}再利用可能なタイマー
このパターンを小さなヘルパーにまとめると、任意の呼び出し可能オブジェクトの時間を簡潔に計測でき、同じコードの繰り返しを避けられます。
#include <iostream>
#include <chrono>
long long timeWork() {
using namespace std::chrono;
auto s = steady_clock::now();
long acc = 0;
for (int i = 0; i < 100000; ++i) acc += i;
auto e = steady_clock::now();
return duration_cast<microseconds>(e - s).count();
}
int main() {
std::cout << "Microseconds non-negative: " << (timeWork() >= 0) << '\n';
return 0;
}デッドコード削除を避ける
コンパイラーは、結果が使われない処理を削除することがあり、ベンチマークが無意味になる可能性があります。必ず結果を消費してください。たとえば、結果を出力したり、観測可能な変数へ累積したりします。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
auto s = steady_clock::now();
volatile long sink = 0;
for (int i = 0; i < 100000; ++i) sink += i;
auto e = steady_clock::now();
std::cout << "Sink used: " << (sink > 0) << ", elapsed >= 0: " << ((e - s).count() >= 0) << '\n';
return 0;
}ウォームアップ実行
キャッシュや遅延初期化の影響で、最初の実行は遅くなることがあります。より安定した数値を得るため、測定前にウォームアップのパスを実行してください。
#include <iostream>
#include <chrono>
long work() {
long a = 0;
for (int i = 0; i < 50000; ++i) a += i;
return a;
}
int main() {
using namespace std::chrono;
work();
auto s = steady_clock::now();
long r = work();
auto e = steady_clock::now();
std::cout << r << " measured, ok: " << ((e - s).count() >= 0) << '\n';
return 0;
}複数回の実行結果を平均する
1回の測定にはノイズが含まれます。処理を何度も実行し、経過時間の合計を求めてから回数で割ると、安定した平均値を得られます。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
long long total = 0;
const int runs = 5;
for (int r = 0; r < runs; ++r) {
auto s = steady_clock::now();
long acc = 0;
for (int i = 0; i < 100000; ++i) acc += i;
auto e = steady_clock::now();
total += duration_cast<microseconds>(e - s).count();
}
std::cout << "Average us non-negative: " << ((total / runs) >= 0) << '\n';
return 0;
}2つのアプローチの比較
ベンチマーク測定は、実装を比較するときに威力を発揮します。それぞれを同じ方法で計測し、得られた所要時間を比較します。
#include <iostream>
#include <chrono>
#include <vector>
int main() {
using namespace std::chrono;
std::vector<int> v;
auto s1 = steady_clock::now();
for (int i = 0; i < 10000; ++i) v.push_back(i);
auto e1 = steady_clock::now();
v.clear();
v.reserve(10000);
auto s2 = steady_clock::now();
for (int i = 0; i < 10000; ++i) v.push_back(i);
auto e2 = steady_clock::now();
std::cout << "Both timed: " << (((e1 - s1).count() >= 0) && ((e2 - s2).count() >= 0)) << '\n';
return 0;
}分解能に注意
測定対象の処理がクロックの1ティックより短い場合、ゼロと読み取られることがあります。クロックの分解能を超えるように、処理を十分な回数繰り返します。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
auto s = steady_clock::now();
int x = 1 + 1;
auto e = steady_clock::now();
auto ns = duration_cast<nanoseconds>(e - s).count();
std::cout << "x=" << x << ", tiny work may read low: " << (ns >= 0) << '\n';
return 0;
}結果の報告
最終的な所要時間を読みやすい単位に変換して出力します。オプティマイザーが処理を削除できないように、消費した結果を見える状態にしておきます。
#include <iostream>
#include <chrono>
int main() {
using namespace std::chrono;
auto s = steady_clock::now();
long acc = 0;
for (int i = 0; i < 300000; ++i) acc += (i * 3) % 11;
auto e = steady_clock::now();
auto ms = duration_cast<milliseconds>(e - s).count();
std::cout << "acc=" << acc << ", ms >= 0: " << (ms >= 0) << '\n';
return 0;
}クイックチェック
ベンチマーク測定の落とし穴について理解度を確認します。
まとめ
<chrono>を使ったコードのベンチマーク測定方法を学びました。
- 前後で
steady_clock::now()を取得し、その差を計算する duration_castで適切な単位を選ぶ- ウォームアップを行い、複数回の実行を平均し、結果を消費してデッドコード除去を防ぐ
- 小さな処理ではクロックの分解能に注意する
次は、C++20のカレンダー型とタイムゾーン型について学びます。
よくある質問
「コードのパフォーマンス測定」レッスンは無料ですか?
はい。「コードのパフォーマンス測定」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C++ Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C++ Academyコースには全4レッスンが含まれています。
「コードのパフォーマンス測定」で何を学びますか?
chronoでベンチマークを行います ブラウザで直接実行するハンズオンコードでC++ Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C++ Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC++ Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「コードのパフォーマンス測定」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC++ Academyレッスンでコードを書いて実行できますか?
はい。すべてのC++ Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 継続時間とクロック
- 時点
- コードのパフォーマンス測定
- カレンダーとタイムゾーン