TL;DR:为什么乘法/转换数据size_t
速度很慢,为什么每个平台会有所不同?
我遇到了一些我不完全理解的性能问题。上下文是一个相机帧采集器,其中以几个 100 Hz 的速率读取和后处理 128x128 uint16_t 图像。
在后处理中,我生成了一个直方图frame->histo
,它uint32_t
包含thismaxval
= 2^16 个元素,基本上我计算了所有强度值。使用此直方图,我计算总和和平方和:
double sum=0, sumsquared=0;
size_t thismaxval = 1 << 16;
for(size_t i = 0; i < thismaxval; i++) {
sum += (double)i * frame->histo[i];
sumsquared += (double)(i * i) * frame->histo[i];
}
使用配置文件分析代码我得到以下内容(示例、百分比、代码):
58228 32.1263 : sum += (double)i * frame->histo[i];
116760 64.4204 : sumsquared += (double)(i * i) * frame->histo[i];
或者,第一行占用 32% 的 CPU 时间,第二行占用 64%。
我做了一些基准测试,这似乎是有问题的数据类型/转换。当我将代码更改为
uint_fast64_t isum=0, isumsquared=0;
for(uint_fast32_t i = 0; i < thismaxval; i++) {
isum += i * frame->histo[i];
isumsquared += (i * i) * frame->histo[i];
}
它的运行速度快了约 10 倍。但是,这种性能影响也因平台而异。在工作站上,Core i7 CPU 950 @ 3.07GHz,代码快了 10 倍。在我的 Macbook8,1 上,它有一个 Intel Core i7 Sandy Bridge 2.7 GHz (2620M),代码只快 2 倍。
现在我想知道:
- 为什么原始代码如此缓慢且容易加速?
- 为什么每个平台的差异如此之大?
更新:
我编译了上面的代码
g++ -O3 -Wall cast_test.cc -o cast_test
更新2:
我通过分析器(Mac 上的Instruments ,如Shark)运行优化的代码,发现了两件事:
1) 在某些情况下,循环本身需要相当长的时间。thismaxval
是类型size_t
。
for(size_t i = 0; i < thismaxval; i++)
占用我总运行时间的 17%for(uint_fast32_t i = 0; i < thismaxval; i++)
占 3.5%for(int i = 0; i < thismaxval; i++)
没有出现在分析器中,我认为它小于 0.1%
2) 数据类型和转换事项如下:
sumsquared += (double)(i * i) * histo[i];
15%(含size_t i
)sumsquared += (double)(i * i) * histo[i];
36%(含uint_fast32_t i
)isumsquared += (i * i) * histo[i];
13%(带uint_fast32_t i
,uint_fast64_t isumsquared
)isumsquared += (i * i) * histo[i];
11%(带int i
,uint_fast64_t isumsquared
)
令人惊讶的是,int
比uint_fast32_t
?
更新4:
我在一台机器上用不同的数据类型和不同的编译器运行了更多测试。结果如下。
对于 testd 0 -- 2 相关代码是
for(loop_t i = 0; i < thismaxval; i++)
sumsquared += (double)(i * i) * histo[i];
对于测试 0、1 和 2,使用sumsquared
double 和loop_t
size_t
, uint_fast32_t
and 。int
对于测试 3--5,代码是
for(loop_t i = 0; i < thismaxval; i++)
isumsquared += (i * i) * histo[i];
isumsquared
类型为and uint_fast64_t
,loop_t
用于测试3、4size_t
和5。uint_fast32_t
int
我使用的编译器是 gcc 4.2.1、gcc 4.4.7、gcc 4.6.3 和 gcc 4.7.0。时间是代码总 cpu 时间的百分比,所以它们显示的是相对性能,而不是绝对的(尽管运行时间在 21 秒时相当稳定)。cpu 时间是两行的,因为我不太确定分析器是否正确分离了这两行代码。
海湾合作委员会:4.2.1 4.4.7 4.6.3 4.7.0 ---------------------------------- 测试0:21.85 25.15 22.05 21.85 测试1:21.9 25.05 22 22 测试2:26.35 25.1 21.95 19.2 测试 3:7.15 8.35 18.55 19.95 测试4:11.1 8.45 7.35 7.1 测试 5:7.1 7.8 6.9 7.05
或者:
基于此,无论我使用什么整数类型,转换似乎都很昂贵。
此外,gcc 4.6 和 4.7 似乎无法正确优化循环 3(size_t 和 uint_fast64_t)。