loops - 由clang优化的简单循环,如何?
问题描述
来自CppCon 2014:Mike Acton “面向数据的设计和 C++”,他展示了这个简单的循环
int Foo::Bar(int count)
{
int value = 0;
for (int i=0;i<count;i++)
{
if ( m_NeedParentUpdate )
{
value++;
}
}
return (value);
}
像这样被clang优化
我不明白这里发生了什么。为什么这段代码不好,为什么它会被clang优化,为什么会这样?
关于循环,他还说“我相信你可以在脑海中优化它”。我不明白。我该如何优化呢?
解决方案
如果不是,则循环增加value
count
时间。m_NeedParentUpdate
false
从生成的代码来看,它似乎m_NeedParentUpdate
是一个布尔值,存储为一个无符号字节,偏移量0
为this
. 优化器可能会检测到m_NeedParentUpdate
循环中的常量,因此可以将测试移到循环之外。程序员应该已经用这种方式编写了代码,这可能是 Mike Acton 所指的我相信你可以在头脑中优化它。
这是一个重写的版本:
class Foo {
bool m_NeedParentUpdate;
int Bar(int count);
};
int Foo::Bar(int count) {
int value = 0;
if (m_NeedParentUpdate) {
for (int i = 0; i < count; i++) {
value++;
}
}
return value;
}
但是请注意,进一步优化您头脑中的代码可能会导致循环减少到value += count;
,但这对于 的负值是不正确的count
,乍一看并不那么明显。
优化器可以检测循环模式并优化为:
int Foo::Bar(int count) {
int value = 0;
if (m_NeedParentUpdate) {
if (count >= 0) {
value += count;
}
}
return value;
}
或等效地:
int Foo::Bar(int count) {
int value = 0;
if (count >= 0) {
if (m_NeedParentUpdate) {
value = count;
}
}
return value;
}
转换m_ParentNeedUpdate
为 anunsigned
并取反产生0
forfalse
和所有位为true
. 使用此值屏蔽count
将产生0
或count
。
int Foo::Bar(int count) {
int value = 0;
if (count >= 0) {
value = -(unsigned)m_NeedParentUpdate & count;
}
return value;
}
但是请注意,代码仍然有一个测试和一个分支指令。可以进一步优化为:
int Foo::Bar(int count) {
// equivalent code, but definitely not readable
return -(unsigned)m_NeedParentUpdate & -(unsigned)(count >= 0) & count;
}
用两者编译它gcc
并clang
生成无分支代码,如GodBolt 的 Compiler Explorer所示。但是这两个编译器都没有将原始代码简化为这一行。
推荐阅读
- python - 应用循环中的 Python 多处理 pool.map 导致重置和奇怪的行为
- ruby-on-rails - 如何在两个 Mutation 类之间共享参数
- javascript - 如何在没有框架的情况下将数据插入 HTML/JavaScript 中的模板元素
- html - 字体从 http 变为 https
- material-ui - React Styleguidist 包装器 - 模块解析失败:意外令牌
- javascript - 模拟 window.navigator 对象
- python - 有什么办法可以优化逻辑回归模型的代码
- json - 警告:列表中的每个孩子都应该有一个唯一的“key”道具,即使有一个键?
- excel - 在 VBA 中模拟 M 几何布朗运动路径
- excel - 删除 || (管道)在列内文本的末尾