潜在的 C++ 编译器优化 with/without throw/noexcept 函数
Potential C++ compiler optimization with/without throw/noexcept function
假设以下 class:
class Example
{
public:
...
Example& operator=(const Example& rhs);
...
private:
other_type *m_content;
size_t m_content_size;
}
Example& Example::operator=(const Example& rhs)
{
if (this != &rhs)
{
delete m_content;
m_content = nullptr;
m_content = getCopiedContent(rhs);
}
return *this;
}
我知道这不是实施 operator=
的最佳方式,但这是故意的,因为我的问题是关于这两行:
m_content = nullptr;
m_content = getCopiedContent(rhs);
可以 编译器 优化掉 m_content = nullptr;
即使getCopiedContent
未定义为 throw()
或 noexcept
:
other_type* getCopiedContent(const Example& obj);
一方面,编译器可能假设如果在 m_content = nullptr;
之后我用 getCopiedContent
的 return 值覆盖 m_content
的值,它可能会优化整个 m_content = nullptr;
表达式。另一方面,如果编译器优化它并且 getCopiedContent
抛出异常,m_content
将包含一个无效值。
C++ 标准是否对这种情况有任何规定?
Can be that the compiler will optimize out m_content = nullptr; even though getCopiedContent is not defined as throw() or noexcept:
是的。这是一个没有副作用的冗余操作。任何自重的编译器都会优化冗余存储。事实上,你必须非常努力地工作以防止冗余存储被优化掉,例如:
- 使其
std::atomic
(如果它是原子的,写入必须传输到其他线程)
- 成功
volatile
- 出于与 (1)
相同的原因,用某种内存屏障(例如锁定 std::mutex
)包围写入
On the other hand if the compiler optimizes it out and getCopiedContent throws an exception, m_content will contain a non valid value
好观察。允许编译器在异常处理程序中执行 nullptr
的写入。即它可能会重新排序指令以保存操作,前提是总结果是 'as if' 而不是。
Does C++ standard states anything regarding such scenario?
是的。它有 'as-if' 规则。在对一个线程进行推理时,可见结果必须是 'as-if' 您的每个语句都是按顺序执行的,没有针对非流水线、非缓存、非常简单的内存模型进行优化。请注意,过去20年生产的计算机实际上没有这么简单,但程序的结果必须像它一样。
对此有一个例外 - 复制省略。在某些情况下不需要保留消除冗余副本的副作用。例如,在省略临时参数和 RVO 期间的参数副本时。
我相信这叫做Dead Store Elimination。
我不知道编译器优化是否包含在标准中,除了 as-if rule。
这里是消除死存储的 Clang 代码。
http://www.llvm.org/docs/doxygen/html/DeadStoreElimination_8cpp_source.html
这只会阻止本地的。
也许有一些工具可以内联函数并作为块本地进行分析以查看是否可以消除该nullptr存储。显然,在某个地方抛出函数会使分析保留 nullptr 存储。
显然,您描述的场景违反了假设规则,因此不符合标准。
假设以下 class:
class Example
{
public:
...
Example& operator=(const Example& rhs);
...
private:
other_type *m_content;
size_t m_content_size;
}
Example& Example::operator=(const Example& rhs)
{
if (this != &rhs)
{
delete m_content;
m_content = nullptr;
m_content = getCopiedContent(rhs);
}
return *this;
}
我知道这不是实施 operator=
的最佳方式,但这是故意的,因为我的问题是关于这两行:
m_content = nullptr;
m_content = getCopiedContent(rhs);
可以 编译器 优化掉 m_content = nullptr;
即使getCopiedContent
未定义为 throw()
或 noexcept
:
other_type* getCopiedContent(const Example& obj);
一方面,编译器可能假设如果在 m_content = nullptr;
之后我用 getCopiedContent
的 return 值覆盖 m_content
的值,它可能会优化整个 m_content = nullptr;
表达式。另一方面,如果编译器优化它并且 getCopiedContent
抛出异常,m_content
将包含一个无效值。
C++ 标准是否对这种情况有任何规定?
Can be that the compiler will optimize out m_content = nullptr; even though getCopiedContent is not defined as throw() or noexcept:
是的。这是一个没有副作用的冗余操作。任何自重的编译器都会优化冗余存储。事实上,你必须非常努力地工作以防止冗余存储被优化掉,例如:
- 使其
std::atomic
(如果它是原子的,写入必须传输到其他线程) - 成功
volatile
- 出于与 (1) 相同的原因,用某种内存屏障(例如锁定
std::mutex
)包围写入
On the other hand if the compiler optimizes it out and getCopiedContent throws an exception, m_content will contain a non valid value
好观察。允许编译器在异常处理程序中执行 nullptr
的写入。即它可能会重新排序指令以保存操作,前提是总结果是 'as if' 而不是。
Does C++ standard states anything regarding such scenario?
是的。它有 'as-if' 规则。在对一个线程进行推理时,可见结果必须是 'as-if' 您的每个语句都是按顺序执行的,没有针对非流水线、非缓存、非常简单的内存模型进行优化。请注意,过去20年生产的计算机实际上没有这么简单,但程序的结果必须像它一样。
对此有一个例外 - 复制省略。在某些情况下不需要保留消除冗余副本的副作用。例如,在省略临时参数和 RVO 期间的参数副本时。
我相信这叫做Dead Store Elimination。
我不知道编译器优化是否包含在标准中,除了 as-if rule。
这里是消除死存储的 Clang 代码。 http://www.llvm.org/docs/doxygen/html/DeadStoreElimination_8cpp_source.html 这只会阻止本地的。
也许有一些工具可以内联函数并作为块本地进行分析以查看是否可以消除该nullptr存储。显然,在某个地方抛出函数会使分析保留 nullptr 存储。
显然,您描述的场景违反了假设规则,因此不符合标准。