我写了一段C代码来展示关于优化和分支预测的讨论中的一个观点。然后我注意到比我预期的更多样化的结果。我的目标是用C和C之间的通用子集编写它,这两种语言都符合标准,并且相当可移植。它在不同的Windows PC上进行了测试:
#include <stdio.h>
#include <time.h>
/// @return - time difference between start and stop in milliseconds
int ms_elapsed( clock_t start, clock_t stop )
{
return (int)( 1000.0 * ( stop - start ) / CLOCKS_PER_SEC );
}
int const Billion = 1000000000;
/// & with numbers up to Billion gives 0, 0, 2, 2 repeating pattern
int const Pattern_0_0_2_2 = 0x40000002;
/// @return - half of Billion
int unpredictableIfs()
{
int sum = 0;
for ( int i = 0; i < Billion; ++i )
{
// true, true, false, false ...
if ( ( i & Pattern_0_0_2_2 ) == 0 )
{
++sum;
}
}
return sum;
}
/// @return - half of Billion
int noIfs()
{
int sum = 0;
for ( int i = 0; i < Billion; ++i )
{
// 1, 1, 0, 0 ...
sum += ( i & Pattern_0_0_2_2 ) == 0;
}
return sum;
}
int main()
{
clock_t volatile start;
clock_t volatile stop;
int volatile sum;
printf( "Puzzling measurements:\n" );
start = clock();
sum = unpredictableIfs();
stop = clock();
printf( "Unpredictable ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
start = clock();
sum = unpredictableIfs();
stop = clock();
printf( "Unpredictable ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
start = clock();
sum = noIfs();
stop = clock();
printf( "Same without ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
start = clock();
sum = unpredictableIfs();
stop = clock();
printf( "Unpredictable ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
}
用VS2010编制/英特尔酷睿2、WinXP的O2优化结果:
Puzzling measurements:
Unpredictable ifs took 1344 msec; answer was 500000000
Unpredictable ifs took 1016 msec; answer was 500000000
Same without ifs took 1031 msec; answer was 500000000
Unpredictable ifs took 4797 msec; answer was 500000000
编辑:编译器的完整开关:
/Zi/no logo/W3/WX-/O2/Oi/Oy-/GL/D " WIN32 "/D " NDEBUG "/D " _ CONSOLE "/D " _ UNICODE "/D " UNICODE "/Gm-/EHsc/GS/Gy/Fp:precise/Zc:wchar _ t/Zc:for scope/Fp " Release \ trying . PCH "/Fa " Release \ "/Fo " Release \ "/Fd " Release \ VC 100 . pdb "/Gd/analyze-/error report:queue
其他人发布了这样的…用MinGW编译,g 4.71,-O1优化英特尔酷睿2,WinXP结果:
Puzzling measurements:
Unpredictable ifs took 1656 msec; answer was 500000000
Unpredictable ifs took 0 msec; answer was 500000000
Same without ifs took 1969 msec; answer was 500000000
Unpredictable ifs took 0 msec; answer was 500000000
他还发布了-O3优化的结果:
Puzzling measurements:
Unpredictable ifs took 1890 msec; answer was 500000000
Unpredictable ifs took 2516 msec; answer was 500000000
Same without ifs took 1422 msec; answer was 500000000
Unpredictable ifs took 2516 msec; answer was 500000000
现在我有个问题。这是怎么回事?
更具体地说…一个固定的函数怎么能占用如此不同的时间?我的代码有问题吗?英特尔处理器有什么棘手的问题吗?编译器是否在做一些奇怪的事情?这是因为64位处理器上运行的32位代码吗?
感谢关注!
编辑:我接受g -O1只是在另外两个调用中重用返回值。我也承认g -O2和g -O3有缺陷,没有优化。测量速度的显著差异(450%!!!)好像还是很神秘。
我查看了VS2010生成的代码的反汇编。它确实内联了不可预测的如果
3次。内联代码非常相似;html" target="_blank">循环是相同的。它没有内联noIfs
。它确实滚动了
一点。它在一次迭代中需要4个步骤。noIfs
计算像是在不可预测
时编写的如果使用 jne
跳过增量。
关于Windows上的结果范围(从1016毫秒到4797毫秒):您应该知道MSVC中的时钟()
返回经过的墙上时间。该标准说clock()
应该返回进程花费的CPU时间的近似值,其他实现在这方面做得更好。
鉴于MSVC给出了墙时间,如果您的流程在运行测试的一次迭代时被抢占,它可能会给出一个大得多的结果,即使代码在大约相同的CPU时间内运行。
还请注意,许多Windows PC上的<code>clock()</code>分辨率非常差,通常为11-19毫秒。您已经完成了足够多的迭代,这仅为1%左右,因此我不认为这是差异的一部分,但在编写基准测试时要注意这一点。我知道您正在追求可移植性,但如果您需要在Windows上进行更好的测量,您可以使用<code>QueryPerformanceCounter</code>这几乎肯定会给您带来更好的分辨率,尽管它仍然只是经过的墙时间。
更新:在我了解到一个案例的长时间运行持续发生之后,我启动了VS2010并重现了结果。我通常在某些运行中获得大约1000毫秒,对于其他运行,我得到750毫秒,对于莫名其妙的运行,我得到5000毫秒。
观察:
和
,而不是ecx
)我不确定从这一切中得出什么结论,除了易失性
对MSVC具有不可预测的性能影响,因此您应该仅在必要时应用它。
更新2:我看到与使用易失性相关的一致的运行时差异,即使反汇编几乎相同。
具有挥发性:
Puzzling measurements:
Unpredictable ifs took 643 msec; answer was 500000000
Unpredictable ifs took 1248 msec; answer was 500000000
Unpredictable ifs took 605 msec; answer was 500000000
Unpredictable ifs took 4611 msec; answer was 500000000
Unpredictable ifs took 4706 msec; answer was 500000000
Unpredictable ifs took 4516 msec; answer was 500000000
Unpredictable ifs took 4382 msec; answer was 500000000
每个实例的拆解如下所示:
start = clock();
010D1015 mov esi,dword ptr [__imp__clock (10D20A0h)]
010D101B add esp,4
010D101E call esi
010D1020 mov dword ptr [start],eax
sum = unpredictableIfs();
010D1023 xor ecx,ecx
010D1025 xor eax,eax
010D1027 test eax,40000002h
010D102C jne main+2Fh (10D102Fh)
010D102E inc ecx
010D102F inc eax
010D1030 cmp eax,3B9ACA00h
010D1035 jl main+27h (10D1027h)
010D1037 mov dword ptr [sum],ecx
stop = clock();
010D103A call esi
010D103C mov dword ptr [stop],eax
无易失性:
Puzzling measurements:
Unpredictable ifs took 644 msec; answer was 500000000
Unpredictable ifs took 624 msec; answer was 500000000
Unpredictable ifs took 624 msec; answer was 500000000
Unpredictable ifs took 605 msec; answer was 500000000
Unpredictable ifs took 599 msec; answer was 500000000
Unpredictable ifs took 599 msec; answer was 500000000
Unpredictable ifs took 599 msec; answer was 500000000
start = clock();
00321014 mov esi,dword ptr [__imp__clock (3220A0h)]
0032101A add esp,4
0032101D call esi
0032101F mov dword ptr [start],eax
sum = unpredictableIfs();
00321022 xor ebx,ebx
00321024 xor eax,eax
00321026 test eax,40000002h
0032102B jne main+2Eh (32102Eh)
0032102D inc ebx
0032102E inc eax
0032102F cmp eax,3B9ACA00h
00321034 jl main+26h (321026h)
stop = clock();
00321036 call esi
// The only optimization I see is here, where eax isn't explicitly stored
// in stop but is instead immediately used to compute the value for the
// printf that follows.
除了寄存器选择,我看不出有什么显著差异。
右,查看64位Linux上gcc的汇编程序代码,第一种情况是使用-O1,函数<code>UnpredictableIfs</code>确实只调用了一次,结果被重用。
不可预测如果:
movl %eax, %ecx
andl $1073741826, %ecx
cmpl $1, %ecx
adcl $0, %edx
addl $1, %eax
NOIF:
xorl %ecx, %ecx
testl $1073741826, %eax
sete %cl
addl $1, %eax
addl %ecx, %edx
如您所见,它并不完全相同,但它做了非常相似的事情。
对于< code>-O1,gcc-4.7.1只调用一次< code > uncompactleifs 并重新获得结果,因为它认为这是一个纯函数,所以每次调用的结果都是相同的。(我是这样做的,通过查看生成的程序集来验证。)
使用更高的优化级别时,函数是内联的,编译器不再识别它是相同的代码,因此每次在源代码中出现函数调用时都会运行它。
除此之外,我的gcc-4.7.1在使用-O1
或-O2
时最好处理unpretableIfs
(除了重用问题,两者都产生相同的代码),而noIfs
在-O3
中处理得更好。然而,相同代码的不同运行之间的时间在这里是一致的-等于或相差10毫秒(时钟
的颗粒度),因此我不知道是什么原因导致您为-O3
报告的unpretableIfs
时间大相径庭。
使用 -O2
时,不可预测的循环如果
与使用 -O1
生成的代码相同(寄存器交换除外):
.L12:
movl %eax, %ecx
andl $1073741826, %ecx
cmpl $1, %ecx
adcl $0, %edx
addl $1, %eax
cmpl $1000000000, %eax
jne .L12
对于< code>noIfs,情况类似:
.L15:
xorl %ecx, %ecx
testl $1073741826, %eax
sete %cl
addl $1, %eax
addl %ecx, %edx
cmpl $1000000000, %eax
jne .L15
它在哪里
.L7:
testl $1073741826, %edx
sete %cl
movzbl %cl, %ecx
addl %ecx, %eax
addl $1, %edx
cmpl $1000000000, %edx
jne .L7
使用-O1
。两个循环的运行时间相似,但<code>不可预测的ifs<code>要快一点。
使用 -O3
,不可预测的循环如果
变得更糟,
.L14:
leal 1(%rdx), %ecx
testl $1073741826, %eax
cmove %ecx, %edx
addl $1, %eax
cmpl $1000000000, %eax
jne .L14
对于< code>noIfs(包括此处的设置代码),它变得更好:
pxor %xmm2, %xmm2
movq %rax, 32(%rsp)
movdqa .LC3(%rip), %xmm6
xorl %eax, %eax
movdqa .LC2(%rip), %xmm1
movdqa %xmm2, %xmm3
movdqa .LC4(%rip), %xmm5
movdqa .LC5(%rip), %xmm4
.p2align 4,,10
.p2align 3
.L18:
movdqa %xmm1, %xmm0
addl $1, %eax
paddd %xmm6, %xmm1
cmpl $250000000, %eax
pand %xmm5, %xmm0
pcmpeqd %xmm3, %xmm0
pand %xmm4, %xmm0
paddd %xmm0, %xmm2
jne .L18
.LC2:
.long 0
.long 1
.long 2
.long 3
.align 16
.LC3:
.long 4
.long 4
.long 4
.long 4
.align 16
.LC4:
.long 1073741826
.long 1073741826
.long 1073741826
.long 1073741826
.align 16
.LC5:
.long 1
.long 1
.long 1
.long 1
它一次计算四次迭代,因此,noIfs
的运行速度几乎是当时的四倍。
问题内容: 谁能推荐一种工具来衡量Web App的UI级别的性能? 我并不是在专门进行负载测试,我们的应用一次最多只能容纳5个用户,我希望能够自动执行并重新运行的指标是诸如页面加载时间,从单击按钮到事件发生,滑出疼痛的反应时间等。我们正在分别衡量API性能,希望能够确定速度下降是API问题还是可以在UI中解决的问题。 理想情况下,我可以将某些东西与Selenium结合使用,单击一个按钮,然后确定预
目标 在图像处理中,由于每秒要处理大量操作,因此必须使代码不仅提供正确的解决方案,而且还必须以最快的方式提供。因此,在本章中,你将学习 衡量代码的性能。 一些提高代码性能的技巧。 你将看到以下功能:cv.getTickCount,cv.getTickFrequency等。 除了OpenCV,Python还提供了一个模块time,这有助于衡量执行时间。另一个模块profile有助于获取有关代码的详细
本文向大家介绍golang、python、php、c++、c、java、Nodejs性能对比,包括了golang、python、php、c++、c、java、Nodejs性能对比的使用技巧和注意事项,需要的朋友参考一下 本人在PHP/C++/Go/Py时,突发奇想,想把最近主流的编程语言性能作个简单的比较, 至于怎么比,还是不得不用神奇的斐波那契算法。可能是比较常用或好玩吧。 好了,tal
下面的代码是我的程序的一部分。它不执行,它说C++禁止将字符串常量转换为字符*。我如何修复这个错误?
variable是某个值的占位符。 所有变量都有一些与之关联的类型,它们表示可以分配的值的“类型”。 C提供了丰富的变量 - 类型 格式字符串 描述 char %c 字符类型变量(ASCII值) int %d 机器最自然的整数大小。 float %f 单精度浮点值。 double %e 双精度浮点值。 void - N/A - 表示缺少类型。 C中的字符( char )变量 Character(
我有以下向量: 现在我想按奇指数对向量进行字典排序(如果奇指数相等,则按偶数指数)。使得排序向量“vec”为: 我知道d::排序将完全排序“vec”。是否可以使用d::排序来选择性地对向量进行排序。d::lower_bound类似。是否可以仅使用奇数索引来查找lower_bound。 我想要与对向量相同的效果。出于效率原因,我不将vec存储为对向量。