C++中的奇怪性能问题(VC 2010)

5

我有一个用C++编写的循环,在使用MSVC2010编译后运行时间很长。(300毫秒)

    for (int i=0; i<h; i++) {
    for (int j=0; j<w; j++) {
        if (buf[i*w+j] > 0) {
            const int sy = max(0, i - hr);
            const int ey = min(h, i + hr + 1);
            const int sx = max(0, j - hr);
            const int ex = min(w, j + hr + 1);
            float val = 0;
            for (int k=sy; k < ey; k++) {
                for (int m=sx; m < ex; m++) {
                    val += original[k*w + m] * ds[k - i + hr][m - j + hr];
                }
            }
            heat_map[i*w + j] = val;
        }
    }
}

这对我来说似乎有些奇怪,所以我进行了一些测试,然后将一些位数更改为内联汇编:(具体来说,是将“val”相加的代码)

    for (int i=0; i<h; i++) {
    for (int j=0; j<w; j++) {
        if (buf[i*w+j] > 0) {
            const int sy = max(0, i - hr);
            const int ey = min(h, i + hr + 1);
            const int sx = max(0, j - hr);
            const int ex = min(w, j + hr + 1);
            __asm {
                fldz
            }
            for (int k=sy; k < ey; k++) {
                for (int m=sx; m < ex; m++) {
                    float val = original[k*w + m] * ds[k - i + hr][m - j + hr];
                    __asm {
                        fld val
                        fadd
                    }
                }
            }
            float val1;
            __asm {
                fstp val1
            }
            heat_map[i*w + j] = val1;
        }
    }
}

现在它的运行时间减半,为150ms。它做的事情完全相同,但为什么速度提高了一倍? 在两种情况下,它都是在启用优化的发布模式下运行。 我原始的C ++代码有什么问题吗?


3
你尝试比较两种情况下生成的汇编代码了吗? - Dean Harding
2个回答

5
我建议在对原始代码做出任何结论之前,尝试使用编译器支持的不同浮点数计算模型-精确严格快速(参见/fp选项)进行测试。我怀疑您的原始代码是使用某种过度限制的浮点数模型编译的(未被第二个版本的汇编遵循),这就是为什么原始代码要慢得多的原因。
换句话说,如果原始模型确实过于严格,那么你只是在比较苹果和橙子。尽管乍一看两个版本似乎在做同样的事情,但实际上并非如此。
例如,请注意,在代码的第一个版本中,中间总和是累加在一个float值中的。如果它是使用精确模型编译的,则即使变量val已被优化掉并且内部FPU寄存器被使用,中间结果也必须舍入到float类型的精度。在你的汇编代码中,你不需要舍入累积结果,这可能有助于提高性能。
我建议您在/fp:fast模式下编译两个版本的代码,并查看它们在这种情况下的性能比较。

谢谢!我已经用Fast模式运行了我的原始代码,现在它只需要80毫秒就能运行完成了。而第二个版本在Fast模式下仍然需要150毫秒,所以我想编译器还是更懂得如何处理代码。我找到了这些针对MSVC的#pragma开关来切换函数的浮点精度(不适用于函数内部):#pragma float_control(precise, off, push) ... 这里放你的代码 ... #pragma float_control(pop)但更具体的信息可以参考:http://msdn.microsoft.com/en-us/library/45ec64h6(VS.80).aspx - raicuandi

3

以下是需要检查的几个事项:

  • 您需要检查是否确实是相同的代码。也就是说,您的内联汇编语句是否与编译器生成的完全相同?我可以看到三个潜在的差异(可能会被优化掉)。第一个是将 val 初始化为零,第二个是额外的变量 val1(不太可能,因为它很可能只会改变堆栈指针的常量减法),第三个是您的内联汇编版本可能不会将中间结果放回 val

  • 您需要确保样本空间足够大。您没有提及每个版本是否只运行了一次或一百次,但是运行次数越多越好,以消除统计数据中的“噪声”影响。

  • 比起经过时间的流逝,更好的度量方式是 CPU 时间。经过时间的流逝会受到环境变化的影响(例如您的病毒检查器或其中一个服务决定在您测试时执行某些操作)。大样本空间将缓解这种情况,但并不一定解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接