Performance 有没有必要在不使用第三个变量的情况下交换两个变量?
我知道不要使用它们,但是有一些技术可以在不使用第三个变量的情况下交换两个变量,例如Performance 有没有必要在不使用第三个变量的情况下交换两个变量?,performance,algorithm,assembly,language-agnostic,swap,Performance,Algorithm,Assembly,Language Agnostic,Swap,我知道不要使用它们,但是有一些技术可以在不使用第三个变量的情况下交换两个变量,例如 x ^= y; y ^= x; x ^= y; 及 在课堂上,教授提到,20年前,当内存非常有限时,这些应用程序非常流行,今天仍在高性能应用程序中使用。这是真的吗?我对为什么使用这些技术毫无意义的理解是: 使用第三个变量永远不会成为瓶颈 优化器无论如何都会这样做 那么,有没有一个不与第三个变量交换的好时机?它更快了吗 相比较而言,使用XOR的方法与使用+/-的方法相比是否更快?大多数体系结构都有一个加法/减法和
x ^= y;
y ^= x;
x ^= y;
及
在课堂上,教授提到,20年前,当内存非常有限时,这些应用程序非常流行,今天仍在高性能应用程序中使用。这是真的吗?我对为什么使用这些技术毫无意义的理解是:
相比较而言,使用XOR的方法与使用+/-的方法相比是否更快?大多数体系结构都有一个加法/减法和XOR的单元,所以这不意味着它们的速度都相同吗?或者仅仅因为CPU有一个操作单元并不意味着它们的速度都相同?这一点都没有意义。这是一种展示聪明的尝试。考虑到它在很多情况下(浮点、指针、结构)都不起作用,不可访问,并且使用三个依赖操作,这比仅仅交换值要慢得多,因此它完全没有意义,并且证明了它实际上并不聪明
您是对的,如果速度更快,那么优化编译器将在交换两个数字时检测模式,并替换它。这很容易做到。但编译器确实会注意到,当您交换两个变量时,可能根本不会生成任何代码,但之后会开始使用不同的变量。例如,如果您交换x和y,则写入a+=x;b+=y;编译器可能只是将其更改为a+=y;b+=x。另一方面,xor或加法/减法模式将无法识别,因为它非常罕见,不会得到改进 对于编写普通洗衣机固件的程序员来说,了解这些技术仍然很重要。很多这样的硬件仍然运行在Z80或类似的CPU上,通常内存不超过4K左右。在这个场景之外,知道这些算法的“诡计”,正如你所说,几乎没有实际用途
(尽管如此,我还是想说,记住并了解这类东西的程序员,即使是对于“常规”应用程序,也往往比那些不会费事的“同行”更好。正是因为后者往往采取“内存足够大”的态度太过分了。)如果您想交换内存中的两个整字或两个整寄存器,这些技巧不太可能有用。但是,如果没有可用寄存器(或者只有一个用于内存到内存交换的可用寄存器),并且没有可用的“交换”指令(如在x86中交换两个SSE寄存器时),或者“交换”指令过于昂贵(如x86中的寄存器内存
xchg
)不可能避免交换或降低寄存器压力
但如果您的变量是单个字中的两个位字段,则修改3-XOR方法可能是一个好主意:
y = (x ^ (x >> d)) & mask
x = x ^ y ^ (y << d)
y=(x^(x>>d))&掩码
当然,知道这一点仍然很有用。还有什么办法呢
c = a
a = b
b = c
三次行动使用三种资源,而不是三次行动使用两种资源
确保指令集可能有一个交换,但只有在1)编写程序集或2)优化器将其作为交换,然后对该指令进行编码时,才起作用。或者,您可以进行内联汇编,但这是不可移植的,而且维护起来很困难。如果您调用了asm函数,那么编译器必须为调用进行设置,并消耗大量的资源和指令。尽管可以这样做,但除非该语言具有交换操作,否则实际上不太可能利用指令集功能
一般程序员现在不需要知道这一点,就像以前一样,人们会抨击这种过早的优化,除非你知道诀窍并经常使用它,如果代码没有文档化,那么它就不明显了,因此它是糟糕的编程,因为它是不可读和不可维护的
例如,让一个人发明一个测试来证明它实际上交换了所有位模式的组合,这仍然是一种价值编程教育和实践。就像在x86上执行xor reg,reg以将寄存器归零一样,对于高度优化的代码,它有一个小但真实的性能提升。是的,有,特别是在汇编代码中
处理器只有有限数量的寄存器。当寄存器非常满时,这个技巧可以避免将寄存器溢出到另一个内存位置(可能在未蚀刻的缓存线中)
实际上,我使用了3路xor来交换一个寄存器,该寄存器位于x86的高性能手动编码锁例程的关键路径中,在该路径中,寄存器压力很高,并且没有(锁安全!)的位置来放置临时值。(在X86上,了解XCHG内存指令的相关成本很高是很有用的,因为它包含自己的锁,我不想看到它的效果。考虑到X86有锁前缀操作码,这确实是不必要的,但历史错误正是如此)
士气:每一种解决方案,无论孤立地站着看起来有多难看,都可能有一些用处。认识他们很好;如果不合适,您始终不能使用它们。在它们有用的地方,它们是非常有效的。这种结构在PIC系列微控制器的许多成员上是有用的,这些微控制器要求几乎所有的操作都通过一个累加器(“工作寄存器”)[请注意,虽然这有时可能是一个障碍,但每个指令只需要编码一个寄存器地址和一个目标位,而不是两个寄存器地址,这使得PIC的工作集可能比其他微控制器大得多]
c = a
a = b
b = c
xorwf other,w ; w=(w ^ other)
xorwf other,f ; other=(w ^ other)
xorwf other,w ; w=(w ^ other)
movwf temp1 ; temp1 = w
movf other,w ; w = other
movwf temp2 ; temp2 = w
movf temp1,w ; w = temp1 [old w]
movwf other ; other = w
movf temp2,w ; w = temp2 [old other]
subwf other,w ; w = other-w
btfss STATUS,C ; Skip next instruction if carry set (other >= W)
subwf other,f ; other = other-w [i.e. other-(other-oldW), i.e. old W]