这是错过的优化机会吗 [英] Is this a missed optimization opportunity or not
问题描述
我发布了此答案。代码:
#include <atomic>
#include <utility>
void printImpl(...);
std::atomic<bool> printLog = false;
class Log {
public:
template <typename T>
const auto& operator<<(T&& t) {
if (printLog) {
ulog.active = true;
return ulog << std::forward<T>(t);
} else {
ulog.active = false;
return ulog;
}
}
private:
struct unchecked_log {
template <typename T>
const auto& operator<<(T&& t) const {
if (active) {
printImpl(std::forward<T>(t));
}
return *this;
}
bool active{false};
};
unchecked_log ulog{};
};
// Instead of the macro. Doesn't break backward compatibility
Log LOG;
void test(bool) { LOG << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10; }
本质上,代码将忽略或记录所有数据。想法是将 atomic< bool>
记录在常规的 bool
中,可以更轻松地对其进行优化。我认为大多数编译器都可以轻松地优化 if(处于活动状态)
部分,因为无法在调用之间进行更改。尽管结果,但大多数编译器的确将函数调用内联到 unchecked_log :: operator< ;<
,但不优化分支。有什么阻碍这种优化的吗?这是非法的。
In essence, the code either ignores or logs all the data. The idea was to record the atomic<bool>
in a regular bool
which can be optimized out more easily. I thought most compilers could easily optimize out the if (active)
part since there is no way it can change between calls. Turns out though, most compilers do inline the function call to unchecked_log::operator<<
but do not optimize out the branching. Is there something preventing this optimization? Would it be illegal.
推荐答案
LOG
是具有外部链接的全局变量。因此,在另一个翻译单元中的 printImpl
的定义可以达到它,并且可以在两次调用之间修改 LOG.ulog.active
。
LOG
is a global variable with external linkage. Therefore printImpl
's definition in another translation unit can reach it and can potentially modify LOG.ulog.active
between calls.
在 test
中使 LOG
为局部变量,然后重复的检查将在 test
的条目中合并为一个,或将 LOG
保留在原来的位置并将其设为静态
,因此包含 printImpl
定义的其他编译单元无法到达此翻译单元的实例。
Make LOG
a local variable in test
and the repeated checks will be consolidated into one at the entry of test
or leave LOG
where it is and make it static
, so a different compilation unit containing printImpl
's definition cannot reach this translation unit's instance.
如下面的评论中所述,或者让 operator <<拷贝返回,这样它返回的实例(现在是临时实例)是不可达的对于
printImpl
。
As mentioned in a comment below, alternatively let operator<<
return by copy, so that the instance it returns (now a temporary) is unreachable for printImpl
.
请注意,可访问性(私人
等),而不是 ulog
和 ulog.active
。一旦 printImpl
能够获得指向相关实例的指针或引用, private
就无法保护其免受修改无论。以下是一些如何实现(非详尽无遗)的示例:
Note that the accessibility (private
, etc.) of ulog
and ulog.active
does not matter. As soon as printImpl
is able to obtain a pointer or reference to the instance of relevance, private
does not protect from modification no matter what. Here are few examples of how that is possible (non-exhaustive):
- 呼叫
operator<<
或通过LOG
上的code>可能会根据<$的中间修改而更改LOG.ulog.active
c $ c> printLogconst_cast
获得结果 - 调用默认的复制/移动赋值运算符
- (因为这是标准布局类)
reinterpret_cast
LOG
到对其ulog
成员 的引用 - (因为这些类可以轻松复制)
memcpy
将其他状态放入LOG
- 放置-新建
Log $ c $的新实例c>放入
LOG
,这将使先前的引用自动引用新对象,因为Log
满足该条件 - 等。
- Calling
operator<<
onLOG
which may now changeLOG.ulog.active
based on an intervening modification ofprintLog
or byconst_cast
ing the result - Calling the defaulted copy/move assignment operators
- (since this is a standard-layout class)
reinterpret_cast
LOG
to a reference to itsulog
member - (since the classes are trivially copyable)
memcpy
a different state intoLOG
- placement-new a new instance of
Log
intoLOG
, which will make previous references reference the new object automatically, becauseLog
satisfies the conditions for that - etc.
这篇关于这是错过的优化机会吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!