这是DV的典型问题,所以我在发布之前犹豫了很多。。。
我知道这个问题被标记为重复,但我的测试(如果它们是好的:它们好吗?这是问题的一部分)倾向于表明情况并非如此。
一开始,我做了一些测试,将 for 循环与 while 循环进行了比较。
这表明for循环更好。
但更进一步,为了或暂时不是重点:差异与以下方面有关:
for (int l = 0; l < loops;l++) {
或
for (int l = 0; l != loops;l++) {
如果你运行它(在Windows 10,Visual Studio 2017版本下),你会看到第一个比第二个快两倍多。
(对于像我这样的新手来说)很难理解编译器由于某些原因是否能够优化更多的一个或另一个。但是…
为什么?
完整的代码如下:
为'
int forloop_inf(int loops, int iterations)
{
int n = 0;
int x = n;
for (int l = 0; l < loops;l++) {
for (int i = 0; i < iterations;i++) {
n++;
x += n;
}
}
return x;
}
为了!= '循环:
int forloop_diff(int loops, int iterations)
{
int n = 0;
int x = n;
for (int l = 0; l != loops;l++) {
for (int i = 0; i != iterations;i++) {
n++;
x += n;
}
}
return x;
}
在这两种情况下,内部计算只是为了避免编译器跳过所有的循环。
这分别由以下各项调用:
printf("for loop inf %f\n", monitor_int(loops, iterations, forloop_inf, &result));
printf("%d\n", result);
和
printf("for loop diff %f\n", monitor_int(loops, iterations, forloop_diff, &result));
printf("%d\n", result);
其中循环=10*1000,迭代=1000*1000。
其中monitor_int为:
double monitor_int(int loops, int iterations, int(*func)(int, int), int *result)
{
clock_t start = clock();
*result = func(loops, iterations);
clock_t stop = clock();
return (double)(stop - start) / CLOCKS_PER_SEC;
}
以秒为单位的结果是:
for loop inf 2.227 seconds
for loop diff 4.558 seconds
所以,即使所有的利益,都是相对于循环内部所做的事情的权重,相比于循环本身,为什么会有这样的差异呢?
编辑:
您可以在此处找到经过审查的完整源代码,以便多次以随机顺序调用函数。
相应的反汇编在这里(通过dumpbin/DISASM CPerf2.exe获得)。
运行它,我现在获得:
我不知道如何在Visual Studio中设置O3,编译命令行如下:
/permissive- /Yu“stdafx.h” /GS /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd“x64\Release\vc141.pdb” /Zc:inline /fp:precise /D “NDEBUG” /D “_CONSOLE” /D “_UNICODE” /D “UNICODE” /errorReport:prompt /WX- /Zc:forScope /gd /Oi /MD /FC /Fa“x64\Release\” /EHsc /nologo /Fo“x64\Release\” /Ot /fp“x64\Release\CPerf2.pch” /诊断:经典
上面是循环的代码,下面是运行它的随机方法:
typedef int(loop_signature)(int, int);
void loops_compare()
{
int loops = 1 * 100;
int iterations = 1000 * 1000;
int result;
loop_signature *functions[2] = {
forloop_diff,
forloop_inf
};
int n_rand = 1000;
int n[2] = { 0, 0 };
double cum[2] = { 0.0, 0.0 };
for (int i = 0; i < n_rand;i++) {
int pick = rand() % 2;
loop_signature *fun = functions[pick];
double time = monitor(loops, iterations, fun, &result);
n[pick]++;
cum[pick] += time;
}
printf("'!=' %f (%d) / '<' %f (%d)\n", cum[0] / (double)n[0], n[0], cum[1] / (double)n[1], n[1]);
}
和反汇编(循环仅起作用,但不确定它是上面链接的良好摘录):
?forloop_inf@@YAHHH@Z:
0000000140001000: 48 83 EC 08 sub rsp,8
0000000140001004: 45 33 C0 xor r8d,r8d
0000000140001007: 45 33 D2 xor r10d,r10d
000000014000100A: 44 8B DA mov r11d,edx
000000014000100D: 85 C9 test ecx,ecx
000000014000100F: 7E 6F jle 0000000140001080
0000000140001011: 48 89 1C 24 mov qword ptr [rsp],rbx
0000000140001015: 8B D9 mov ebx,ecx
0000000140001017: 66 0F 1F 84 00 00 nop word ptr [rax+rax]
00 00 00
0000000140001020: 45 33 C9 xor r9d,r9d
0000000140001023: 33 D2 xor edx,edx
0000000140001025: 33 C0 xor eax,eax
0000000140001027: 41 83 FB 02 cmp r11d,2
000000014000102B: 7C 29 jl 0000000140001056
000000014000102D: 41 8D 43 FE lea eax,[r11-2]
0000000140001031: D1 E8 shr eax,1
0000000140001033: FF C0 inc eax
0000000140001035: 8B C8 mov ecx,eax
0000000140001037: 03 C0 add eax,eax
0000000140001039: 0F 1F 80 00 00 00 nop dword ptr [rax]
00
0000000140001040: 41 FF C1 inc r9d
0000000140001043: 83 C2 02 add edx,2
0000000140001046: 45 03 C8 add r9d,r8d
0000000140001049: 41 03 D0 add edx,r8d
000000014000104C: 41 83 C0 02 add r8d,2
0000000140001050: 48 83 E9 01 sub rcx,1
0000000140001054: 75 EA jne 0000000140001040
0000000140001056: 41 3B C3 cmp eax,r11d
0000000140001059: 7D 06 jge 0000000140001061
000000014000105B: 41 FF C2 inc r10d
000000014000105E: 45 03 D0 add r10d,r8d
0000000140001061: 42 8D 0C 0A lea ecx,[rdx+r9]
0000000140001065: 44 03 D1 add r10d,ecx
0000000140001068: 41 8D 48 01 lea ecx,[r8+1]
000000014000106C: 41 3B C3 cmp eax,r11d
000000014000106F: 41 0F 4D C8 cmovge ecx,r8d
0000000140001073: 44 8B C1 mov r8d,ecx
0000000140001076: 48 83 EB 01 sub rbx,1
000000014000107A: 75 A4 jne 0000000140001020
000000014000107C: 48 8B 1C 24 mov rbx,qword ptr [rsp]
0000000140001080: 41 8B C2 mov eax,r10d
0000000140001083: 48 83 C4 08 add rsp,8
0000000140001087: C3 ret
0000000140001088: CC CC CC CC CC CC CC CC ÌÌÌÌÌÌÌÌ
?forloop_diff@@YAHHH@Z:
0000000140001090: 45 33 C0 xor r8d,r8d
0000000140001093: 41 8B C0 mov eax,r8d
0000000140001096: 85 C9 test ecx,ecx
0000000140001098: 74 28 je 00000001400010C2
000000014000109A: 44 8B C9 mov r9d,ecx
000000014000109D: 0F 1F 00 nop dword ptr [rax]
00000001400010A0: 85 D2 test edx,edx
00000001400010A2: 74 18 je 00000001400010BC
00000001400010A4: 8B CA mov ecx,edx
00000001400010A6: 66 66 0F 1F 84 00 nop word ptr [rax+rax]
00 00 00 00
00000001400010B0: 41 FF C0 inc r8d
00000001400010B3: 41 03 C0 add eax,r8d
00000001400010B6: 48 83 E9 01 sub rcx,1
00000001400010BA: 75 F4 jne 00000001400010B0
00000001400010BC: 49 83 E9 01 sub r9,1
00000001400010C0: 75 DE jne 00000001400010A0
00000001400010C2: C3 ret
00000001400010C3: CC CC CC CC CC CC CC CC CC CC CC CC CC ÌÌÌÌÌÌÌÌÌÌÌÌÌ
再次编辑:
我感到惊讶的还有以下几点:
始终查看生成的代码。
很多年前有些μP没有一些条件分支指令或者标志很少的时候曾经是真理。所以,一些条件必须被编译成一组比较和跳转。
但这不再是事实,因为现代处理器有非常丰富的条件分支指令(其中一些也有许多“常规”条件指令,例如ARM指令)和许多标志。
你可以在这里玩不同的游戏:https://godbolt.org/g/9DsqJm
typedef int(signature)(int, int);
...
int main() {
int loops, iterations, runs;
fprintf(stderr, "Loops: ");
scanf("%d", &loops);
fprintf(stderr, "Iterations: ");
scanf("%d", &iterations);
fprintf(stderr, "Runs: ");
scanf("%d", &runs);
fprintf(stderr, "Running for %d loops and %d iterations %d times.\n", loops, iterations, runs);
signature *functions[2] = {
forloop_inf,
forloop_diff
};
int result = functions[0](loops, iterations);
for( int i = 0; i < runs; i++ ) {
int pick = rand() % 2;
signature *function = functions[pick];
int new_result;
printf("%d %f\n", pick, monitor_int(loops, iterations, function, &new_result));
if( result != new_result ) {
fprintf(stderr, "got %d expected %d\n", new_result, result);
}
}
}
有了这个,我们可以按随机顺序运行 1000 次并找到平均时间。
打开优化后进行基准测试也很重要。询问未优化代码的运行速度没有什么意义。我将尝试-O2
和-O3
。
我的发现是,使用 Apple LLVM 版本 8.0.0 (clang-800.0.42.1) 在 -O2
上执行 10000 次循环和 1000000 次迭代forloop_inf
确实比 forloop_diff
快约 50%。
forloop_inf: 0.000009
forloop_diff: 0.000014
查看 -O2 生成的汇编代码,使用 clang -O2 -S -mllvm --x86-asm-syntax=intel test.c
我可以看到两种实现之间的许多差异。也许了解组装的人可以告诉我们为什么。
但是在-O3
,性能差异不再明显。
forloop_inf: 0.000002
forloop_diff: 0.000002
这是因为在< code>-O3处,它们几乎完全相同。一个使用< code>je,一个使用< code>jle。就是这样。
总之,当进行基准测试时。。。
最重要的是。
<代码>i
如图所示,打开优化后,它们都非常快,即使没有完全优化,它们也可以在0.000009秒内完成10,000,000,000次迭代。i
但是
i!=max
可能会导致错误。
translated_page: https://github.com/PX4/Devguide/blob/master/en/simulation/airsim.md translated_sha: RETRANSLATE AirSim仿真 AirSim是一个基于虚幻引擎(Unreal Engine)的开源、跨平台无人机模拟器。它可以使用硬件在环(HITL)或软件在环(SITL)的方式为Pixh
translated_page: https://github.com/PX4/Devguide/blob/master/en/simulation/hitl.md translated_sha: 95b39d747851dd01c1fe5d36b24e59ec865e323e 硬件在环仿真 硬件在环仿真指的自驾仪与仿真器相连并且所有的代码运行在自驾仪上的仿真。这种方法的优点是可以测试代码在实际处
Gazebo是一个自主机器人3D仿真环境。它可以与ROS配套用于完整的机器人仿真,也可以单独使用。本文简要介绍单独的使用方法。 {% raw %} To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video {% endraw %} {%
translated_page: https://github.com/PX4/Devguide/blob/master/en/simulation/sitl.md translated_sha: b522243efef9deb5e2d3ae7bd03ae9ed0eee3418 Multi-Vehicle Simulation This tutorial explains how to simul
translated_page: https://github.com/PX4/Devguide/blob/master/en/simulation/sitl.md translated_sha: 95b39d747851dd01c1fe5d36b24e59ec865e323e 软件在环仿真 (SITL) 软件在环仿真是在主机上运行一个完整的系统并模拟自驾仪。它通过本地网络连接到仿真器。 设置成如
3.3.1.仿真器 vs 真机 在大多数情况下,应用在仿真器上执行,与在真机上是没有区别的。少数的例外情况则往往是因为难以模拟,比如传感器。一般的硬件相关特性比如电话呼叫、地理定位等等,都可以由仿真器模拟。