我目前正在开发一种非常快速的算法,其中一部分是极快的扫描和统计功能。 在这个任务中,我追求任何性能优势。 因此,我也有兴趣保持代码“多线程”友好。
现在提出问题: 我注意到将一些经常访问的变量和数组放入“全局”或“静态本地”(它们都是相同的),有一个可测量的性能优势(在+ 10%的范围内)。 我试图理解为什么,并找到一个解决方案,因为我宁愿避免使用这些类型的分配。 请注意,我不认为差异来自“分配”,因为在堆栈上分配一些变量和小数组几乎是瞬时的。我认为差异来自“访问”和“修改”数据。
在这次搜索中,我发现了stackoverflow中的这篇旧帖子: C++ performance of global variables
但我对那里的答案感到非常失望。很少解释,大多是咆哮“你不应该这样做”(嘿,这不是问题!)和非常粗略的陈述,如'它不影响性能',这显然是不正确的,因为我用精确测量它基准工具。
如上所述,我正在寻找解释,如果存在,则解决此问题。到目前为止,我已经感觉到计算本地(动态)变量的内存地址比全局(或本地静态)花费更多。也许类似于ADD操作差异。但这无助于找到解决方案......
答案 0 :(得分:7)
如果它们是POD类型,那么创建局部变量就可以完全免费。您可能会溢出具有太多堆栈变量的缓存行或其他类似的基于对齐的原因,这些原因非常特定于您的代码段。我经常发现非局部变量会显着降低性能。
答案 1 :(得分:7)
这实际上取决于您的编译器,平台和其他细节。但是,我可以描述一个全局变量更快的场景。
在许多情况下,全局变量处于固定偏移量。这允许生成的指令直接简单地使用该地址。 (MOV AX,[MyVar]
的某些内容。)
但是,如果您有一个相对于当前堆栈指针或类或数组成员的变量,则需要一些数学运算来获取数组的地址并确定实际变量的地址。
显然,如果你需要在你的全局变量上放置某种互斥量以保持线程安全,那么你几乎肯定会失去任何性能提升。
答案 2 :(得分:2)
很难打破速度的静态分配,虽然10%是一个相当小的差异,但可能是由于地址计算。
但如果你正在寻找速度,
注释while(p<end)stats[*p++]++;
中的示例显然是展开的候选者,例如:
static int stats[M];
static int index_array[N];
int *p = index_array, *pend = p+N;
// ... initialize the arrays ...
while (p < pend-8){
stats[p[0]]++;
stats[p[1]]++;
stats[p[2]]++;
stats[p[3]]++;
stats[p[4]]++;
stats[p[5]]++;
stats[p[6]]++;
stats[p[7]]++;
p += 8;
}
while(p<pend) stats[*p++]++;
不要指望编译器为你做这件事。它可能会或可能不会弄明白。
其他可能的优化会浮现在脑海中,但它们取决于您实际上要做的事情。
答案 3 :(得分:1)
如果您有类似
的内容int stats[256]; while (p<end) stats[*p++]++;
static int stats[256]; while (p<end) stats[*p++]++;
你并没有真正比较同样的事情,因为对于第一个实例,你没有对数组进行初始化。明确写出第二行相当于
static int stats[256] = { 0 }; while (p<end) stats[*p++]++;
因此,为了公平比较,您应该先阅读
int stats[256] = { 0 }; while (p<end) stats[*p++]++;
如果他的变量处于已知状态,你的编译器可能会推断出更多的东西。
现在,static
情况可能具有运行时优势,因为初始化是在编译时(或程序启动)完成的。
为了测试这是否能弥补你的差异,你应该多次使用静态声明和循环运行相同的函数,看看如果你的调用数量增加,差异是否会消失。
但正如其他人所说,最好是检查编译器生成的汇编程序,以查看生成的代码中存在哪些有效差异。