noexcept
函数说明符是否旨在提高性能,因为生成的对象中可能没有异常的簿记代码,因此应尽可能将其添加到函数声明和定义中?我首先想到了可调用对象的包装器,其中noexcept
可能会有所不同,尽管检查表达式可能会“膨胀”源代码。值得吗?
答案 0 :(得分:15)
从理论上讲,noexcept
会提高效果。但另一方面也可能会引起一些问题。
在大多数情况下,不应指定,因为专业人员太少而无法考虑,这可能会使您的代码升级变得痛苦。 Andrzej撰写的This post详细介绍了原因。
如果时间太长,请从我的结论中得出这些建议:
noexcept
if注释函数
throw()
,noexcept
注释的移动赋值,它们的实例应放入某个STL容器中。noexcept
注释函数
std::terminate
,noexcept
功能。答案 1 :(得分:10)
顶级编译器生成的代码已经过优化,就像无法抛出的代码一样,然后发生异常的情况由异常处理机制通过查看元代码找到的外部代码处理有关功能的数据。我想在代码大小方面有一些好处,但是当知道不需要时,可以省略它。
有些情况下,nothrow规范允许进行一些特定的优化:
int main() {
int i = 0;
try {
++i;
thing_that_cannot_throw();
++i;
thing_that_can_throw();
++i;
} catch (...) {}
std::cout << i << "\n";
}
这里理论上第二个++我可以在调用thing_that_cannot_throw
之前重新排序(而i
刚刚初始化为2
)。然而,它是否在实践中是另一回事,因为在调试器或函数调用之上的堆栈中保证变量状态的实现会希望i
在期间具有值1
即使它是任何标准意味着无法观察到的局部变量,也会调用。
我怀疑nothrow保证对程序员比对编译器更有价值。如果您编写的代码提供强大的异常保证,那么通常会执行某些关键操作,您需要提供nothrow保证(交换,移动和析构函数是常见的候选者)。
答案 2 :(得分:2)
我偶然发现了一个“真实世界”示例,其中noexcept有所作为。我想在这里分享它,因为它可能会帮助其他人形成意见。
首先介绍一下背景知识:
标准库容器尝试“异常安全”。这意味着在引发(并捕获)异常之后,它们会为您提供有关容器状态的某些保证。一个很好的例子是std :: vector :: emplace_back。如果由于某种原因插入失败,则emplace_back保证该向量看起来没有改变。请参见emplace_back
上的cppreference。
但是,当向量需要响应位置而重新定位时,这变得很有趣。重新定位先前存在的矢量项的(希望)最快的方法是将它们move
放置到新的扩大缓冲区中。不幸的是,move
的构造可能会引发异常,因此,如果值类型的move
-ctor不是异常安全的,则emplace_back
需要采取复制操作。但是由于可以在编译时为move-noexept'ness的类型进行探查,std::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 @ 64kb
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 @ 512kb
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
查看这些结果,在两个基准中均可以移动的情况下,进行测试的速度比未移动的情况下的速度要快〜1.3 。