gpt4 book ai didi

assembly - 零分配与异或,第二个真的更快吗?

转载 作者:行者123 更新时间:2023-12-02 11:13:36 26 4
gpt4 key购买 nike

几年前有人向我展示了以下将变量清零的命令。

xor i,i


他告诉我,这比为它分配零要快。
是真的吗
编译器是否进行优化以使代码执行此类操作?

最佳答案

您可以自己尝试一下以查看答案:

  movl $0,%eax
xor %eax,%eax


组装然后拆卸:

as xor.s -o xor.o
objdump -D xor.o


并得到

   0:   b8 00 00 00 00          mov    $0x0,%eax
5: 31 c0 xor %eax,%eax


用于32位寄存器的mov指令要大2.5倍,从ram加载所需的时间更长,并且会占用更多的缓存空间。早在加载时间就是杀手kill的今天,今天的存储周期时间和缓存空间可能并不那么引人注目,但是如果您的编译器和/或代码经常这样做,您会发现缓存丢失空间和/或更多驱逐,以及更多,较慢的系统内存周期。

在现代CPU中,较大的代码大小也会降低解码器的速度,可能会阻止它们在每个周期内解码最大数量的x86指令。 (例如,某些CPU在16B块中最多包含4条指令。)

也有 performance advantages to xor over mov in some x86 CPUs (especially Intel's) that have nothing to do with code-size,因此x86汇编中总是首选xor-zeroing。



另一组实验:

void fun1 ( unsigned int *a )
{
*a=0;
}
unsigned int fun2 ( unsigned int *a, unsigned int *b )
{
return(*a^*b);
}
unsigned int fun3 ( unsigned int a, unsigned int b )
{
return(a^b);
}


0000000000000000 <fun1>:
0: c7 07 00 00 00 00 movl $0x0,(%rdi)
6: c3 retq
7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
e: 00 00

0000000000000010 <fun2>:
10: 8b 06 mov (%rsi),%eax
12: 33 07 xor (%rdi),%eax
14: c3 retq
15: 66 66 2e 0f 1f 84 00 nopw %cs:0x0(%rax,%rax,1)
1c: 00 00 00 00

0000000000000020 <fun3>:
20: 89 f0 mov %esi,%eax
22: 31 f8 xor %edi,%eax
24: c3 retq


沿着显示您的问题可能导致的变量xor i,i的路径走下去。由于您未指定要使用的处理器或上下文,因此很难描绘出整个画面。例如,如果您正在谈论C代码,则必须了解编译器对该代码的处理方式,这在很大程度上取决于函数本身中的代码,如果在执行xor时,编译器在寄存器中具有操作数,并且取决于在编译器设置上,您可能会得到xor eax,eax。或编译器可以选择将其更改为mov reg,0,或将something = 0更改;到xor reg,reg。

还有更多需要考虑的序列:

如果变量的地址已经在寄存器中:

   7:   c7 07 00 00 00 00       movl   $0x0,(%rdi)

d: 8b 07 mov (%rdi),%eax
f: 31 c0 xor %eax,%eax
11: 89 07 mov %eax,(%rdi)


编译器将选择mov zero而不是xor。如果您尝试以下C代码,将会得到以下结果:

void funx ( unsigned int *a )
{
*a=*a^*a;
}


编译器将其替换为零。提取了相同数量的字节,但是需要访问两个内存而不是一个,并且烧掉了一个寄存器。和三个要执行的指令,而不是一个。因此,零位移动明显更好。

现在,如果它是字节大小并在寄存器中:

13: b0 00                   mov    $0x0,%al
15: 30 c0 xor %al,%al


代码大小没有区别。 (但是它们的执行方式仍然不同)。



现在,如果您正在谈论另一个处理器,那么可以说ARM

   0:   e3a00000    mov r0, #0
4: e0200000 eor r0, r0, r0
8: e3a00000 mov r0, #0
c: e5810000 str r0, [r1]
10: e5910000 ldr r0, [r1]
14: e0200000 eor r0, r0, r0
18: e5810000 str r0, [r1]


您不会通过使用xor(独占或eor)来保存任何内容:一条指令就是一条已获取并执行的指令。如果您将变量的地址保存在寄存器中,则可以像处理任何处理器一样对ram中的内容进行异或。如果您必须将数据复制到另一个寄存器以执行“异或”操作,那么最终仍然会有两个存储器访问和三个指令。如果您有一个可以执行内存存储操作的处理器,则零移动会更便宜,因为根据处理器的不同,您只有一个内存访问权限和一两个指令。

实际上,这比这更糟:由于内存排序规则, eor r0, r0, r0required to have an input dependency on r0(限制无序执行)。 Xor调零总是产生零,但仅有助于x86汇编的性能。



因此,最重要的是,如果您要在x86系统上的汇编程序中谈论寄存器(从8088到现在),则xor通常会更快,因为指令更小,获取速度更快,如果有一条指令则需要更少的缓存,而留下更多的缓存同样,要求在指令中将零编码的非x86可变指令长度处理器也将需要更长的指令,更长的获取时间,如果有缓存则消耗更多的缓存等。因此,xor为更快(通常取决于它的编码方式)。如果您有条件标志,并且希望将move / xor设置为零标志,那就更糟了,您可能必须刻录正确的指令(在某些处理器上,mov不会更改标志)。一些处理器具有特殊的零寄存器,这不是通用的,当您使用它时会得到零,这样您就可以编码这种非常常见的用例,而不会消耗更多的指令空间或燃烧额外的指令周期,将零立即数加载到寄存器中。例如,以msp430为例,移动0x1234将花费您两个字的指令,但是移动0x0000或0x0001以及其他一些常量可以编码为单个指令字。如果您正在谈论ram中的变量,则所有处理器都会受到双重打击,两次读取-修改-写入不计取指令的内存周期,并且如果读取导致高速缓存行填充,情况会更糟(然后写入速度非常快),但如果不进行读取操作,则仅写操作可能会在高速缓存中直接通过并执行得非常快,因为在并行执行写入操作时处理器可以继续运行(有时您会获得性能提升,有时不会,如果您进行调优,通常会获得这种性能提升)为了它)。 x86和可能更旧的处理器是您看到异或而不是移零的习惯的原因。对于那些特定的优化来说,今天的性能提升仍然存在,系统内存仍然极慢,并且任何额外的内存周期都非常昂贵,同样,丢弃的任何高速缓存也很昂贵。中途的编译器,即使是gcc,也将检测到xor i,i等于i = 0,并根据具体情况选择更好的指令序列(在平均系统上)。

获得迈克尔·阿布拉什(Michael Abrash)的《汇编的禅宗》。好的二手书价格合理(低于50美元),即使您购买80美元的书也很值得。尝试超越特定的8088“单食者”,并了解他正在尝试教授的一般思维过程。然后花费尽可能多的时间来分解代码,最好用于许多不同的处理器。应用所学...

关于assembly - 零分配与异或,第二个真的更快吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7695309/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com