uint32_t作为向量索引在64位时比size_t有更好的性能



我有一些类似的性能关键代码:

void func(std::vector<int>& v, size_t i)
{
while(i > 0)
{
// do something with v[i]
// compute next i   
}
}

i的类型从size_t改为uint32_t后,代码的运行时间减少了10%以上。当这个函数被调用时,它的输入类型总是size_t。该代码是使用Clang在64位编译的。该函数由编译器在更复杂的上下文中的多个地方内联,因此很难比较汇编代码。为什么会发生这种情况?


我做了一些进一步的调查。首先,当使用uint32_t作为向量索引时,Clang似乎不能很好地优化代码。请看下面的例子:

void func(std::vector<int>& v, size_t i)
{
for(auto j = i; j < v.size(); ++j)
{
v[j] = 0;    
}
}

如果我们将size_t更改为uint32_t,将生成大量的汇编:https://godbolt.org/z/1qrrrs

对于我的性能关键代码,当使用size_t时,Clang可以进行更积极的循环展开。然而,事实证明,这种展开的性能更差,可能是由于一些分支没有像预期的那样经常被击中。我手动执行了一些展开操作,现在size_tuint32_t之间的性能是相同的。

有什么一般的想法为什么会发生?

  • 可能32位操作在CPU上更快
  • 变量大小的改变可能导致堆栈相对于内存页边界的对齐方式不同,并且/或者由于缓存冲突而导致一些特别关键的数据或指令没有丢失,从而导致性能的偶然变化。

最新更新