C++中异常处理

663 阅读2分钟

在c++/java/python 等语言中,我们可以选择throw关键词来抛出异常,在c++中它遇到throw时,编译器要生成一些代码来存储堆栈信息,其次运行时如果遇到异常也需要内存中记录一些上下文堆栈信息,导致函数性能劣化严重!

测试

#include <benchmark/benchmark.h>
#include <cstdlib>
#include <outcome.hpp>
#include <stdexcept>
#include <string>
#include <system_error>

namespace outcome = OUTCOME_V2_NAMESPACE;

// 使用 exception
static std::string to_string(int num) {
    if (num > 100) {
        throw std::invalid_argument("");
    }
    return std::to_string(num);
}

// 使用 outcome::result
static outcome::result<std::string> to_string_no_excpt(int num) noexcept {
    if (num > 100) {
        return outcome::failure(std::errc::invalid_argument);
    }
    return std::to_string(num);
}

// 使用 c 标准写法,把输出参数放到输入上
static int to_string_std(int num, std::string& output) noexcept {
    if (num > 100) {
        return -1;
    }
    output.append(std::to_string(num));
    return 0;
}

static void test_exception(benchmark::State& state) {
    for (auto _ : state) {
        try {
            auto x = to_string(std::rand());
        } catch (...) {
        }
    }
}

static void test_noexception(benchmark::State& state) {
    for (auto _ : state) {
        auto x = to_string_no_excpt(std::rand());
        if (x) {
            x.value(); // todo
        } else {
            x.error(); // todo
        }
    }
}

static void test_noexception_std(benchmark::State& state) {
    for (auto _ : state) {
        std::string output;
        auto x = to_string_std(std::rand(), output);
        if (x) {
            // todo
        }
    }
}

BENCHMARK(test_exception);
BENCHMARK(test_noexception);
BENCHMARK(test_noexception_std);
BENCHMARK_MAIN();

测试开启-O2优化测试两次的结果,发现throw exception 性能劣化非常严重!

---------------------------------------------------------------
Benchmark                     Time             CPU   Iterations
---------------------------------------------------------------
test_exception             3517 ns         3211 ns       218450
test_noexception           6.05 ns         5.98 ns    117859007
test_noexception_std       6.01 ns         5.93 ns    113924875
---------------------------------------------------------------
test_exception             3254 ns         3200 ns       213889
test_noexception           6.05 ns         5.98 ns    116138238
test_noexception_std       6.04 ns         5.97 ns    116865338

那么假如我们代码干脆就没有异常,都走不到except里,测试下其实性能三者差距不大!

---------------------------------------------------------------
test_exception             12.7 ns         11.8 ns     58565644
test_noexception           13.3 ns         13.1 ns     53467767
test_noexception_std       19.0 ns         18.8 ns     36429872
---------------------------------------------------------------
test_exception             11.9 ns         11.7 ns     60039454
test_noexception           15.5 ns         13.2 ns     53046780
test_noexception_std       19.7 ns         19.2 ns     36500537

结论

代码中经可能少的使用 throw exception,可以使用 c写法 或者 使用outcome::result 库,来避免大量throw exception带来的性能劣化!