【问题标题】:C++ Is this a form of micro optimizationC++ 这是一种微优化的形式吗
【发布时间】:2013-04-29 16:46:12
【问题描述】:

这是微优化,还是根本优化?

void Renderer::SetCamera(FLOAT x, FLOAT y, FLOAT z) {
    // Checking for zero before doing addition?
    if (x != 0) camX += x;
    if (y != 0) camY += y;
    if (z != 0) camZ += z;

    // Checking if any of the three variables are not zero, and performing the code below.
    if (x != 0 | y != 0 | z != 0) {
        D3DXMatrixTranslation(&w, camX, camY, camZ);
    }
}

在具有 vector.size() 条件的情况下运行 for.. 循环会强制应用程序在每个循环中重新计算向量中的元素吗?

std::vector<UINT> vect;

INT vectorSize = vect.size();
for (INT Index = 0; Index < vectorSize; Index++) {
// Do vector processing
}

// versus:

std::vector<UINT> vect;

for (INT Index = 0; Index < vect.size(); Index++) {
// Do vector processing
}

我正在使用 Visual Studio,至于第二个问题,编译器似乎可以优化,但我不确定。

【问题讨论】:

    标签: c++ micro-optimization


    【解决方案1】:

    根据向量的实现,编译器可能理解也可能不理解大小没有改变。毕竟,你在循环中调用了不同的向量函数,其中任何一个都可能改变大小。

    既然向量是一个模板,那么编译器就知道它的一切,所以如果它真的很努力,它可以理解大小不会改变,但这可能是太多的工作。 p>

    通常,你会想这样写:

    for (size_t i = 0, size = vect.size(); i < size; ++i)
        ...
    

    虽然我们这样做了,但迭代器也使用了类似的方法:

    for (list<int>::iterator i = lst.begin(), end = lst.end(); i != end; ++i)
        ...
    

    编辑:我错过了第一部分:

    这是优化吗?

    if (x != 0) camX += x;
    if (y != 0) camY += y;
    if (z != 0) camZ += z;
    

    没有。首先,即使它们是 int,也不会是优化,因为当值可能大部分时间不为零时检查和分支是更多的工作。

    其次,更重要的是,它们是浮动的。这意味着除了you shouldn't directly compare them to 0 之外,它们基本上几乎永远不会完全等于 0。所以ifs 的正确率为 99.9999%。

    同样的事情也适用于:

    if (x != 0 | y != 0 | z != 0)
    

    但是,在这种情况下,由于矩阵转换的成本可能很高,您可以这样做:

    #define EPS 1e-6 /* epsilon */
    if (x > EPS || x < -EPS || y > EPS || y < -EPS || z > EPS || z < -EPS)
    

    现在是的,与矩阵乘法相比,这可能是一种优化。

    还要注意,我使用了||,如果例如从一开始x &gt; EPS 为真(它不会计算其余部分),它就会短路,但| 不会发生。

    【讨论】:

    • 如果值是int 并且大部分时间为零,它是否仍然会适得其反?我的意思是,检查和分支似乎并不比添加零快。
    • @zakinster,如果他们是int 并且大部分时间为零,那真的取决于架构。我同意你的想法,很可能直接添加它会更快,但你永远不知道所有(奇怪的)架构。
    • 即使它是int 并且大部分时间为零,但只有在camX 上的缓存读取未命中率接近 100% 时才会更快。 @zakinster
    • @zakinster:通常会适得其反,或者至少没有帮助。如果编译器足够聪明,它会将代码转换为camX += x;
    • @zakinster:我很确定 compare-and-branch 通常比较慢,至少在 x86-{32,64} 上是这样(假设它没有以某种方式优化)。分支预测没有机会启动,因此阅读cam? 必须付出相当高的成本才能使分支有价值。
    【解决方案2】:

    我怀疑在许多架构上,前三行是反优化,因为它们可能会引入浮点比较然后分支,这可能比总是做加法要慢(即使它是浮点数)。

    另一方面,在进行转换之前确保至少有一个分量不为零。

    对于您的第二种情况,size 必须是恒定时间,并且几乎可以肯定会被内联以直接访问vector 的大小。它很可能是完全可优化的。也就是说,有时它可以通过保存大小来使代码/循环更易于阅读,因为这清楚地表明您在断言循环期间大小不会改变。

    【讨论】:

      【解决方案3】:

      首先,关于vector.size(),见 this SO question。附带说明一下,我还没有看到 std::vector::size() 不是 O(1) 的实现。

      if (x != 0) camX += x; 这个cmp 和随后的jne 但是无论如何都比简单地添加变量x 要慢。 编辑:除非您预计camX 上的缓存未命中率超过 50%

      【讨论】:

        【解决方案4】:

        第一个可能是悲观的,检查 0 可能比加法慢。最重要的是,在调用D3DXMatrixTranslation 之前的检查中,您使用| 而不是短路逻辑或||。由于函数调用之前的检查可能会节省时间(甚至在语义上是必要的),因此将整个代码包装在该检查中,

        void Renderer::SetCamera(FLOAT x, FLOAT y, FLOAT z) {
        
            if (x != 0 || y != 0 || z != 0) {
                camX += x;
                camY += y;
                camZ += z;
                D3DXMatrixTranslation(&w, camX, camY, camZ);
            }
        }
        

        如果xyz全部为零,则无需执行任何操作,否则,执行所有操作。

        其次,如果编译器可以确定循环运行时大小没有改变,则编译器可以将vector.size() 提升到循环之外。如果编译器无法确定,则不得将size() 计算提升到循环之外。

        当您知道大小不会改变时自己这样做是一种好习惯。

        【讨论】:

        • 我真的很喜欢你的回答。悲观是我必须添加到我的字典中的一个新词。 :) 我真的很喜欢。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-14
        • 2016-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-01
        • 1970-01-01
        相关资源
        最近更新 更多