C++异常与值返回错误处理性能基准测试及疑问
C++错误处理方式性能基准测试与疑问
测试方案
针对两种C++错误处理方式的性能差异开展基准测试:
- 值返回(错误汇总):使用
std::variant适配C++17标准,未采用std::expected - 抛出异常
测试场景为大型整数数组求和,设计了err_a和err_b两种错误类型,完整测试代码如下:
#include <benchmark/benchmark.h> #include <random> #include <variant> #include <vector> namespace { constexpr int k_err_a_value = 8; constexpr int k_err_b_value = 15; struct err_a {}; struct err_b {}; [[maybe_unused]] std::variant<int, err_a, err_b> sum_raw(const std::vector<int> &v) { for (const auto &i : v) { if (k_err_a_value == i) return {err_a{}}; } for (const auto &i : v) { if (k_err_b_value == i) return {err_b{}}; } int sum = 0; for (int i: v) sum += i; return {sum}; } [[maybe_unused]] int sum_exceptions(const std::vector<int> &v) { for (const auto &i : v) { if (k_err_a_value == i) throw err_a{}; } for (const auto &i : v) { if (k_err_b_value == i) throw err_b{}; } int sum = 0; for (int i: v) sum += i; return sum; } std::vector<int> generate(const size_t n, const double err_perc, bool use_err_b) { std::mt19937 gen32; std::uniform_int_distribution<decltype(gen32)::result_type> dist{}; std::vector<int> generated; generated.reserve(n); for (size_t i = 0; i < n; ++i) { auto g = dist(gen32); while (k_err_a_value == g || k_err_b_value == g) g = dist(gen32); generated.emplace_back(g); } if (err_perc <= 100.0) generated[n / 100.0 * err_perc] = use_err_b ? k_err_b_value : k_err_a_value; return generated; } class bm_sum : public ::benchmark::Fixture { public: void SetUp(benchmark::State &state) override { const size_t elems = state.range(0); const double err_perc = state.range(1); const bool use_err_b = state.range(2); _generated = generate(elems, err_perc, use_err_b); } const std::vector<int> &generated() const { return _generated; } constexpr static int from_variant(std::variant<int, err_a, err_b> v) noexcept { return std::visit([](auto i) { if constexpr (std::is_same_v<decltype(i), int>) return i; else return 0; }, std::move(v)); } private: std::vector<int> _generated; }; BENCHMARK_DEFINE_F(bm_sum, raw)(benchmark::State &state) { for (auto _ : state) { benchmark::DoNotOptimize(from_variant(sum_raw(generated()))); benchmark::ClobberMemory(); } } BENCHMARK_DEFINE_F(bm_sum, exceptions)(benchmark::State &state) { for (auto _ : state) { benchmark::DoNotOptimize([&] { try { return sum_exceptions(generated()); } catch (...) { return 0; } }()); benchmark::ClobberMemory(); } } // todo: how not to repeat? BENCHMARK_REGISTER_F(bm_sum, raw) ->ArgsProduct({benchmark::CreateRange(1'000, 10'000'000, 10), {101, 80}, // perc of range where to put error {0, 1}}); // which error to put BENCHMARK_REGISTER_F(bm_sum, exceptions) ->ArgsProduct({benchmark::CreateRange(1'000, 10'000'000, 10), {101, 80}, // perc of range where to put error {0, 1}}); // which error to put } // namespace BENCHMARK_MAIN();
测试环境与结果
测试环境为g++ 11.4.0,得到以下结果:
- 无异常抛出场景(参数101):两种错误处理方式性能几乎一致
- 在处理80%输入时抛出异常场景:小数据集下异常方式比值返回方式慢2-3倍;但大数据集下,异常方式的性能反而更优
技术疑问
- 上述观察到的性能现象是否合理?
- 如何构造极简示例来展示异常处理的实际性能劣势?
注:这类带实际代码和性能数据的测试,对参与“异常优劣论战”的开发者有参考价值。
内容的提问来源于stack exchange,提问作者Sergey Kolesnik
相关产品推荐
相关产品推荐

