我正在做一个实验来测量“for loop”在微控制器上的执行时间。这个''for循环‘’包含一些整数和指针操作。
案例1:当我将编译器优化标志设置为'‘none'’(没有优化)时,会生成汇编代码,并且我可以测量执行时间。
情况2:当我将编译器优化设置为“速度”(针对速度进行了优化)时,不会为这个循环生成汇编代码。看起来,编译器抛出了这个''for循环‘’
/* the basic concept behind this code is data manipulation in an array.Therefore I created an array then with the help of loops, tried to manipulate data*/
int abc[1000];
for(n=0; n<1000; n++)
{
abc[n]= 0xaa;
}
for(n=2; n<1000; n=n+2)
{
abc[n]= 0xbb;
}
for(n=5; n<1000; n=n+2)
{
for(i=(n+n); i<1000; i++)
{
abc[i]= i;
}
}
谁能解释一下为什么编译器会抛出这个循环,当我将编译器标志设置为速度时。
发布于 2017-01-29 12:26:27
如果之后不使用abc
,优化器可能会将其识别为“已死”(以及所有对它的写入),并将其完全删除。
发布于 2017-02-01 21:25:20
编译器查看您的代码,发现abc已设置,并且从未使用过。一些编译器会对此给出警告。由于abc从不使用,编译器会对其进行优化,因为如果您从未使用过变量,那么设置变量的意义何在。
您可以使abc易失性,但这可能会违背测试的目的。将变量设为volatile会告诉编译器,它不能对其使用做出任何假设。当您将变量设置为volatile时,编译器可能无法进行任何优化,因此在进行优化和不进行优化时,时间将是相同的。
发布于 2017-02-02 14:20:08
这里有几个完整的例子,因为你的是不完整的,我们不能准确地回答你的问题。
unsigned int fun ( void )
{
unsigned int x[8];
unsigned int ra;
unsigned int rb;
for(ra=0;ra<5;ra++) x[ra]=ra;
rb=0; for(ra=0;ra<5;ra++) rb+=x[ra];
return(rb);
}
void more_fun ( void )
{
unsigned int x[8];
unsigned int ra;
for(ra=0;ra<5;ra++) x[ra]=ra;
}
和优化编译输出的示例
00000000 <fun>:
0: e3a0000a mov r0, #10
4: e12fff1e bx lr
00000008 <more_fun>:
8: e12fff1e bx lr
第二个函数首先,很容易看到,ra和x都没有在函数外部使用,它们所做的一切都没有产生任何有实际价值的东西,它是带有未使用变量的死代码,所以所有这些都消失了,整个函数都优化为:
void more_fun ( void )
{
}
它应该是这样的。
更进一步,我已经使用随机化器和其他算法进一步研究了这类事情,有时编译器会计算出来,有时则不会。在这种情况下,它非常简单。
因此,fun()函数中的旋转没有任何运行时的值,它都是死代码,结果不会根据输入或全局的东西而变化,它是完全包含在函数中的数学函数,具有可以预先计算的精确答案。因此,编译器在编译时(0+1+2+3+4 = 10)计算答案,并删除所有死代码。基本上得出了正确的答案
unsigned int fun ( void )
{
return(10);
}
如果你想使用循环来浪费时间,或者甚至想看看循环是如何实现的,那么你可以尝试几种方法。
void dummy ( unsigned int );
unsigned int fun ( void )
{
unsigned int ra;
volatile unsigned int rb;
rb=0; for(ra=0;ra<5;ra++) rb+=ra;
return(rb);
}
void more_fun ( void )
{
unsigned int ra;
for(ra=0;ra<5;ra++) dummy(ra);
}
它可以给出类似这样的结果(编译器不同)
00000000 <fun>:
0: e3a03000 mov r3, #0
4: e24dd008 sub sp, sp, #8
8: e58d3004 str r3, [sp, #4]
c: e59d3004 ldr r3, [sp, #4]
10: e58d3004 str r3, [sp, #4]
14: e59d3004 ldr r3, [sp, #4]
18: e2833001 add r3, r3, #1
1c: e58d3004 str r3, [sp, #4]
20: e59d3004 ldr r3, [sp, #4]
24: e2833002 add r3, r3, #2
28: e58d3004 str r3, [sp, #4]
2c: e59d3004 ldr r3, [sp, #4]
30: e2833003 add r3, r3, #3
34: e58d3004 str r3, [sp, #4]
38: e59d3004 ldr r3, [sp, #4]
3c: e2833004 add r3, r3, #4
40: e58d3004 str r3, [sp, #4]
44: e59d0004 ldr r0, [sp, #4]
48: e28dd008 add sp, sp, #8
4c: e12fff1e bx lr
00000050 <more_fun>:
50: e92d4010 push {r4, lr}
54: e3a04000 mov r4, #0
58: e1a00004 mov r0, r4
5c: e2844001 add r4, r4, #1
60: ebfffffe bl 0 <dummy>
64: e3540005 cmp r4, #5
68: 1afffffa bne 58 <more_fun+0x8>
6c: e8bd4010 pop {r4, lr}
70: e12fff1e bx lr
易失性的解决方案,正如你所看到的,是相当丑陋的,它说,我希望你每次使用这个变量的时候,都去触摸ram,当你想对它做任何事情的时候,从ram中读取它,并在每一步之后写回它。more_fun()解决方案并不依赖于希望编译器像您希望的那样尊重volatile (为什么编译器在一个局部的、死的变量上尊重volatile,这似乎是错误的),而是如果您强制编译器调用外部函数(该函数不在优化域中,并且不能作为结果内联,并且如果dummy()不使用输入变量,则可能会显示死代码)。作为一个如此小的循环,编译器可以实现一个循环,或者可以展开它,你可能会要求它尝试展开,如果它有意义的话,那么就像上面那样将它实现为一个循环,或者可能将它实现为一系列调用
void more_fun ( void )
{
dummy(0);
dummy(1);
dummy(2);
dummy(3);
dummy(4);
}
所有这一切的美妙之处在于,有了像gnu这样的免费工具,虽然不是最好的编译器,但就最紧凑/最快的代码而言(一种大小的代码不适合任何人),它确实可以编译成对象或二进制文件,显然,它有一个反汇编程序,可以反汇编对象和二进制文件,这样你就可以尝试使用这些简单的函数,检查编译选项的作用,并开始了解死代码是什么或看起来像什么。除了时间,你什么都不用花。
大多数人在某种程度上得到了易失性解决方案,如果你试图做一些手动优化,并且开始缓慢地构建,易失性将会弄乱你的结果,而不是帮助你,它以一种不自然的方式使处理器超负荷工作,而如果你没有它,你最终可能会想出真正的代码,调用循环中的其他函数,其性能在使用和不使用其中一个变量的易失性的情况下将有很大的差异。
总之,基准测试是假的,即使在相同的计算机上使用相同的源代码,使用相同的编译器,操作结果也太容易了。重要的是执行实际任务的实际程序具有这样的性能。以一种不同的方式实现它,如果可能的话,测量一下,并添加一些余量,并决定哪个更快并与之相伴,或者如果大致相同,则更容易阅读和/或维护。
https://stackoverflow.com/questions/41908524
复制相似问题