首页 > 解决方案 > 由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优化,为什么会这样?

关于循环,他还说“我相信你可以在脑海中优化它”。我不明白。我该如何优化呢?

标签: loopsoptimizationclang

解决方案


如果不是,则循环增加value count时间。m_NeedParentUpdatefalse

从生成的代码来看,它似乎m_NeedParentUpdate是一个布尔值,存储为一个无符号字节,偏移量0this. 优化器可能会检测到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并取反产生0forfalse和所有位为true. 使用此值屏蔽count将产生0count

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;
}

用两者编译它gccclang生成无分支代码,如GodBolt 的 Compiler Explorer所示。但是这两个编译器都没有将原始代码简化为这一行。


推荐阅读