関数指定子はnoexcept
、生成されたオブジェクトの例外を記録するコードがない可能性があるため、パフォーマンスの向上を目的としているので、可能な限り関数の宣言と定義に追加する必要がありますか? 最初に、呼び出し可能なオブジェクトのラッパーを考えますnoexcept
。チェック式によってソース コードが「肥大化」する可能性がありますが、ここで違いが生じる可能性があります。それは価値がありますか?
3 に答える
noexcept が違いを生む「現実世界」の例に出くわしました。他の人が意見を形成するのに役立つかもしれないので、ここで共有したいと思います。
最初に少し背景を説明します。標準ライブラリ コンテナーは「例外セーフ」になろうとします。つまり、例外が発生した (およびキャッチされた) 後のコンテナーの状態について、一定の保証が得られます。これの非常に良い例は std::vector::emplace_back です。何らかの理由で挿入が失敗した場合、 emplace_back はベクトルが変更されていないように見えることを保証します。のcppreferenceを参照してくださいemplace_back
。ただし、これは emplace に応答してベクトルを再配置する必要がある場合に興味深いものになります。既存のベクトル項目を再配置する (うまくいけば) 最速の方法は、move
それらを新しい拡大されたバッファに移動することです。残念ながら、move
-construction は例外を発生させる可能性があるため、値型のmove
-ctor が例外セーフでない場合、emplace_back
代わりにコピー操作に頼る必要があります。しかし、コンパイル時に型のmove-noexeptstd::vector
性をプローブすることは可能であるため、それが合法であることが判明した場合でも、より高速なアプローチが採用されます。
これをローカルで測定するために、次のGoogleベンチマークをまとめました。
#include "benchmark/benchmark.h"
#include <vector>
// This type really benefits from being moved instead of being copied
struct SlowCopy {
SlowCopy(const size_t theSize) {
for (int i = 0; i < theSize; ++i)
itsData.emplace_back(i);
}
SlowCopy(const SlowCopy &) = default;
SlowCopy(SlowCopy &&) noexcept = default;
std::vector<int> itsData;
};
// The template parameter specifies whether the move constructor is noexcept or not
template<bool YesNo>
struct MovableNoexcept {
MovableNoexcept(const size_t theSize) : itsData{theSize} {}
MovableNoexcept(const MovableNoexcept &) = default;
MovableNoexcept(MovableNoexcept &&) noexcept(YesNo) = default;
MovableNoexcept& operator=(const MovableNoexcept &) = default;
MovableNoexcept& operator=(MovableNoexcept &&) noexcept(false) = default;
SlowCopy itsData;
};
// This benchmark takes 2 arguments:
// 1. How many items do we push into a vector
// 2. How big are the items that are in the vector
template<bool IsNoexcept>
static void BM_MoveRelocateNoexcept(benchmark::State& state) {
std::vector<MovableNoexcept<IsNoexcept>> aExcepts;
for (auto _ : state) {
for (int i = 0; i < state.range(0); ++i)
aExcepts.emplace_back(state.range(1));
benchmark::ClobberMemory();
}
}
// Test 1k elements @ 64*sizeof(int) kb
BENCHMARK_TEMPLATE(BM_MoveRelocateNoexcept, false)->Args({1000, 1 << 16})->Repetitions(20);
BENCHMARK_TEMPLATE(BM_MoveRelocateNoexcept, true)->Args({1000, 1 << 16})->Repetitions(20);
// Test 100 elements @ 512*sizeof(int) kb
BENCHMARK_TEMPLATE(BM_MoveRelocateNoexcept, false)->Args({100, 1 << 19})->Repetitions(20);
BENCHMARK_TEMPLATE(BM_MoveRelocateNoexcept, true)->Args({100, 1 << 19})->Repetitions(20);
// Run the benchmark
BENCHMARK_MAIN();
ローカル システムで、ベンチマークを実行して次の結果を測定しました。
Running ./noexcept_bench
Run on (8 X 4400 MHz CPU s)
CPU Caches:
L1 Data 32 KiB (x4)
L1 Instruction 32 KiB (x4)
L2 Unified 256 KiB (x4)
L3 Unified 8192 KiB (x1)
Load Average: 0.58, 0.70, 0.69
------------------------------------------------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------------------------------------------------
BM_MoveRelocateNoexcept<false>/1000/65536/repeats:20_mean 157793886 ns 157556651 ns 20
BM_MoveRelocateNoexcept<false>/1000/65536/repeats:20_median 157752118 ns 157511285 ns 20
BM_MoveRelocateNoexcept<false>/1000/65536/repeats:20_stddev 294024 ns 292420 ns 20
BM_MoveRelocateNoexcept<true>/1000/65536/repeats:20_mean 119320642 ns 119235176 ns 20
BM_MoveRelocateNoexcept<true>/1000/65536/repeats:20_median 119256119 ns 119187012 ns 20
BM_MoveRelocateNoexcept<true>/1000/65536/repeats:20_stddev 190923 ns 180183 ns 20
BM_MoveRelocateNoexcept<false>/100/524288/repeats:20_mean 127031806 ns 126834505 ns 20
BM_MoveRelocateNoexcept<false>/100/524288/repeats:20_median 126939978 ns 126741072 ns 20
BM_MoveRelocateNoexcept<false>/100/524288/repeats:20_stddev 381682 ns 380187 ns 20
BM_MoveRelocateNoexcept<true>/100/524288/repeats:20_mean 95281309 ns 95175234 ns 20
BM_MoveRelocateNoexcept<true>/100/524288/repeats:20_median 95267762 ns 95152072 ns 20
BM_MoveRelocateNoexcept<true>/100/524288/repeats:20_stddev 176838 ns 176834 ns 20
これらの結果を見ると、noexcept-move が可能なテストでは、両方のベンチマークで noexcept-movable 以外の対応するテストに比べて最大1.3の高速化が見られました。