noexcept是否提高了性能?

Mar*_*tin 26 c++ boost c++11

noexcept函数说明旨在改善的可能,因为在生成的对象异常没有簿记代码的性能,因此应增加尽可能函数声明和定义?我首先考虑可调用对象的包装器noexcept,虽然检查表达式可能会"膨胀"源代码,但可能会有所不同.这值得吗?

Mat*_*ang 16

从理论上讲,noexcept会提高绩效.但另一方面,它也可能导致一些问题.

在大多数情况下,不应该指定它,因为专业人员太少而无法考虑,这可能会使您的代码升级变得痛苦.这篇由Andrzej撰写的文章详细介绍了原因.

如果时间太长,请从以下结论中得出这些建议:

  1. 使用noexceptif 注释函数
    • 他们已经注释了throw(),
    • 或者他们是好的候选人(在帖子中列出)并且永远不会扔,
    • 或者它们是移动构造函数,移动赋值,其noexcept注释无法由编译器正确推导出来,并且它们的实例应该放入某个STL容器中.
  2. 不要使用noexceptif 注释函数
    • 你真的担心性能下降,
    • 或者是否有打电话的风险std::terminate,
    • 或者您只是不确定新功能,
    • 或者你怀疑自己是否应该发挥自己的作用noexcept.


Ste*_*sop 11

顶级编译器生成的代码已经过优化,就像无法抛出的代码一样,然后发生异常的情况由异常处理机制通过查看有关的元数据找到的外部代码处理.功能.我想在代码大小方面有一些好处,但是当知道不需要时,可以省略它.

在某些情况下,nothrow规范确实允许某些特定的优化:

int main() {
    int i = 0;
    try {
        ++i;
        thing_that_cannot_throw();
        ++i;
        thing_that_can_throw();
        ++i;
    } catch (...) {}
    std::cout << i << "\n";
}
Run Code Online (Sandbox Code Playgroud)

理论上,第二个++ i可以在调用之前重新排序thing_that_cannot_throw(并且i刚刚初始化为2).然而,它是否在实践中是另一回事,因为在调试器或函数调用之上的堆栈中保证变量状态的实现将希望在该调用期间i具有值1,即使它是不可观察的局部变量通过任何标准手段.

我怀疑nothrow保证对程序员比对编译器更有价值.如果您正在编写提供强大异常保证的代码,那么通常会执行某些关键操作,您需要提供nothrow保证(交换,移动和析构函数是常见的候选者).

  • @Martin:有,`std::string` 使用什么?问题是你不能在 `noexcept` 方法中创建一个新的 `std::string` ;不是因为您的代码有缺陷,而是因为您运行的硬件可能更加有限。这很烦人。 (2认同)
  • @MatthieuM:也许我误解了你,但当然你可以创建一个字符串,你只需要处理错误的可能性.如果你不相信会发生错误,因为你的硬件不是那么有限,那么`void myfunc()noexcept {try {myfunc_impl(); } catch(...){}}`会做到这一点,虽然你可能想写一些不那么极其自信的操作会成功!无论如何``myfunc_impl`不需要是'noexcept`函数.只有在编写异常安全代码总是很烦人的程度上才会烦人 - 你不能忽视错误的操作. (2认同)

sul*_*uke 9

我偶然发现了一个“真实世界”的例子,其中 noexcept 有所作为。我想在这里分享它,因为它可能有助于其他人形成意见。

首先是一点背景知识:标准库容器试图做到“异常安全”。这意味着在引发(并捕获)异常之后,它们为您提供了对容器状态的某些保证。一个很好的例子是 std::vector::emplace_back。如果由于某种原因插入失败, emplace_back 保证向量看起来没有改变。见cppreferenceemplace_back。然而,当向量需要重新定位以响应炮台时,这会变得有趣。(希望)将预先存在的向量项重新定位move到新的扩大缓冲区的最快方法。不幸的是,move-construction 可能会引发异常,所以如果值类型的move-ctor 不是异常安全的,emplace_back需要求助于复制操作。但是由于可以在编译时探测类型的移动不排除性std::vector如果结果证明是合法的,则仍将采用更快的方法。

我将以下谷歌基准放在一起以在本地进行测量:

#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();
Run Code Online (Sandbox Code Playgroud)

在我的本地系统上,我测量了以下运行基准测试的结果:

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
Run Code Online (Sandbox Code Playgroud)

查看这些结果,在两个基准测试中,与非 noexcept-movable 对应项相比,可以使用 noexcept-move 的测试的速度提高了约 1.3