【问题标题】:Extremely bizarre code generation in Visual C++, for nearly identical code; 3x speed differenceVisual C++ 中极其奇怪的代码生成,几乎相同的代码; 3倍速差
【发布时间】:2012-07-25 20:06:25
【问题描述】:

下面的代码(从我的较大代码中减少,在我惊讶于它的速度与 std::vector 相比显得苍白之后)有两个独特的特点:

  • 当我对源代码进行非常微小的修改(始终使用 Visual C++ 2010 使用 /O2 编译它)时,它的运行速度超过 三倍

    注意:为了让这更有趣,我在最后给出了修改的提示,这样你就可以花一些时间自己找出改变。原始代码大约 500 行,所以我花了很长时间才确定它,因为修复看起来与性能无关。

  • /MTd 的运行速度比 /MT 快​​ 20%即使输出循环看起来相同!!!

tiny-modification 情况的汇编代码的区别是:

  • 循环没有修改(~300 ms):

    00403383  mov         esi,dword ptr [esp+10h] 
    00403387  mov         edx,dword ptr [esp+0Ch] 
    0040338B  mov         dword ptr [edx+esi*4],eax 
    0040338E  add         dword ptr [esp+10h],ecx 
    00403392  add         eax,ecx 
    00403394  cmp         eax,4000000h 
    00403399  jl          main+43h (403383h) 
    
  • 循环 /MTd(看起来相同!但约270毫秒):

    00407D73  mov         esi,dword ptr [esp+10h] 
    00407D77  mov         edx,dword ptr [esp+0Ch] 
    00407D7B  mov         dword ptr [edx+esi*4],eax 
    00407D7E  add         dword ptr [esp+10h],ecx 
    00407D82  add         eax,ecx 
    00407D84  cmp         eax,4000000h 
    00407D89  jl          main+43h (407D73h)    
    
  • 循环修改(~100 ms!!):

    00403361  mov         dword ptr [esi+eax*4],eax 
    00403364  inc         eax  
    00403365  cmp         eax,4000000h 
    0040336A  jl          main+21h (403361h) 
    

现在我的问题是,为什么上述更改会产生它们的效果?这完全是奇怪的!

尤其是第一个 - 它根本不应该影响任何东西(一旦你看到代码中的差异),但它会显着降低速度。

对此有解释吗?

#include <cstdio>
#include <ctime>
#include <algorithm>
#include <memory>
template<class T, class Allocator = std::allocator<T> >
struct vector : Allocator
{
    T *p;
    size_t n;
    struct scoped
    {
        T *p_;
        size_t n_;
        Allocator &a_;
        ~scoped() { if (p_) { a_.deallocate(p_, n_); } }
        scoped(Allocator &a, size_t n) : a_(a), n_(n), p_(a.allocate(n, 0)) { }
        void swap(T *&p, size_t &n)
        {
            std::swap(p_, p);
            std::swap(n_, n);
        }
    };
    vector(size_t n) : n(0), p(0) { scoped(*this, n).swap(p, n); }
    void push_back(T const &value) { p[n++] = value; }
};
int main()
{
    int const COUNT = 1 << 26;
    vector<int> vect(COUNT);
    clock_t start = clock();
    for (int i = 0; i < COUNT; i++) { vect.push_back(i); }
    printf("time: %d\n", (clock() - start) * 1000 / CLOCKS_PER_SEC);
}

提示(将鼠标悬停在下方)

它与分配器有关。

回答

Allocator &amp;a_ 更改为Allocator a_

【问题讨论】:

  • 什么小修改?这应该是一个谜题还是一个真正的问题?
  • 关于循环1和2之间的时间差;您是否进行了大量时间测量并计算了平均值? (您可能已经知道,即使是完全相同的代码,每次运行的时间也会有很大差异。)
  • @ManofOneWay:是的,我有,非常一致。
  • 显然编译器对可能的别名很保守,但我仍然不确定你的问题到底是什么。
  • 出于某种奇怪的原因,如果您删除 std::swap(n_, n); 行,那么您总是会得到快速循环。另请注意,在vector 构造函数中,您有两个名为n 的变量:成员和参数!这很令人困惑。

标签: c++ performance visual-c++ code-generation


【解决方案1】:

对于它的价值,我对 /MT/MTd 之间区别的猜测是,/MTd 堆分配将绘制堆内存用于调试目的,使其更有可能被分页 - 这发生在你之前开始计时。

如果你“预热”向量分配,你会得到 /MT/MTd 的相同数字:

vector<int> vect(COUNT);

// make sure vect's memory is warmed up
for (int i = 0; i < COUNT; i++) { vect.push_back(i); }
vect.n = 0; // clear the vector

clock_t start = clock();
for (int i = 0; i < COUNT; i++) { vect.push_back(i); }
printf("time: %d\n", (clock() - start) * 1000 / CLOCKS_PER_SEC);

【讨论】:

  • +1 很棒的答案,看起来很准确。绝对可以解决问题。 :)
【解决方案2】:

奇怪的是 Allocator& 会破坏别名链,而 Allocator 不会。

你可以试试

for(int i=vect.n; i<COUNT;++i){
    ...
}

强制 i 和 n 同步。 这将使 vc 更容易优化。

【讨论】:

  • Lenx,我不确定我是否听懂了你的意思——这如何解释分配器的问题? (另外,这有点随机,但是……我们认识吗?!)
  • 嗨,Mehrdad,很高兴在这里见到你 :) 哦,我明白你的问题了。对于vc2010来说真的很奇怪。 Allocator& 使 vc 无法识别 n 与 i 同步。或许你可以试试:for(int i=vect.n; i
  • 哈哈也很高兴在这里见到你! :) 这是一个了不起的发现——我刚刚尝试了你提到的,它把时间从 270 毫秒提高到了 180 毫秒!它仍然不是我看到的~90 ms,但它更接近了。 +1 我从来没有想过这一点,感谢您指出!
【解决方案3】:

emm...看来“最快”的代码

00403361  mov         dword ptr [esi+eax*4],eax 
00403364  inc         eax  
00403365  cmp         eax,4000000h 
0040336A  jl          main+21h (403361h) 

有些过度优化。在这个循环中,vect.n 完全被忽略了...... 如果循环发生异常,vect.n 将无法正确更新。

所以答案可能是:当你使用 Allocator 时,vc 发现 vect.n 将永远不会被再次使用, 以至于可以忽略。这很神奇,但总的来说它并没有那么有用和危险。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-10
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多