使用asm代码在C++中交换两个变量

3

我有一个非常大的函数,用于对大量的int数据进行排序。代码除了速度慢之外一切正常。为了解决这个问题,我的第一步是在C++中嵌入一些asm代码。如何使用asm交换2个变量?我尝试了以下代码:

_asm{ push a[x]; push a[y]; pop a[x]; pop a[y];}

并且还有这个:

_asm(mov eax, a[x];mov ebx,a[y]; mov a[x],ebx; mov a[y],eax;}

但是两者都会崩溃。我如何在这些交换中节省时间?我使用的是VS_2010。


6
它应该有多快?“慢”的C++代码是什么样子? - CB Bailey
我使用第二个整数来交换它们,代码如下:int c=a[x];a[x]=a[y]; a[y]=c - user775476
3
你看过你的编译器生成的代码吗?你认为你手写的汇编代码能更好吗? - CB Bailey
7
编译器速度不可超越,你无法比编译器更快地交换两个整数的值。 - Puppy
1
使用std::swap来进行交换,而不是使用临时的int变量。我怀疑这样做可能不会更好,但它更明显你正在做什么,并且代码更简洁。 - Adam Badura
当你说“排序”时,你是指你只是简单地进行排序吗?如果是这样,也许可以尝试寻找一个优化的库。或者在这种情况下,“排序”意味着更多的东西吗? - JustJeff
4个回答

4

总的来说,对于像这样简单的代码,要想比编译器做得更好非常困难。

当编译器面对整数交换操作时,通常会发出类似以下代码:

mov eax, [x]
mov ebx, [y]
mov [x], ebx
mov [y], eax

在尝试覆盖之前,首先要检查编译器实际生成了什么。如果像这样,那就不必继续了;你无法做得比这更好。此外,如果留给编译器处理,如果这些变量接下来立即被使用,它可能会选择重用其中一个寄存器以节省变量加载/存储。但是,在手写汇编代码中,这是不可能的,编译器必须在手写汇编之后重新加载变量。请注意,push / push / pop / pop序列很可能会慢得多;它不仅向堆栈添加了四个额外的内存操作,而且还引入了对堆栈指针的依赖性,消除了任何可能的流水线。使用简单的mov序列,如果它们在不同的内存库或其中一个在缓存中,则至少可以并行运行读取和写入的一对,而且它也不会在后续代码中导致堆栈指针停滞。
因此,您不应该尝试微调换乘的成本;相反,应该减少执行的换乘次数。有许多可用的排序算法,每个算法具有略微不同的特点。您可能会发现,在您的数据集上,某些算法比其他算法更好(导致较少的交换)。

2

你认为你能比优化编译器更快地生成汇编代码?即使你成功了,你只会让优化器更加困惑,从而导致生成的代码更慢。


我在一个程序中编写了两段代码。第一段使用C++代码,利用辅助变量交换两个变量,第二段使用汇编代码进行交换。我对两者的时间进行了测量,发现第二段总是更快。问题是,当我尝试将其实现到我的主程序中时,它会崩溃。这可能是因为在我的主代码中我使用了数组而不是简单的变量吗? - user775476
5
也许代码更快是因为它没有做相同的事情(=有问题)? - bdonlan

1

在汇编代码中,您可以使用变量名、函数名和标签作为符号。请注意,像 a[x] 这样的内容不是有效的符号。

编写更高效的代码需要技巧和知识,使用汇编语言并不一定会对此有所帮助。

您可以比较编译器为具有内联汇编器和没有内联汇编器的函数生成的汇编代码,以查看您的代码出了什么问题。


1

当您进行内联汇编时,您可以更改某些内容,以便编译器对寄存器内容的假设不再成立。通常情况下,EAX用于传递参数或返回值,因此破坏EAX可能没有太大影响,但是如果您破坏了EBX并且没有将其放回,则可能会引起问题。在使用EBX之前尝试将其推入堆栈,然后在完成后弹出。


但老实说,这种操作对于优化编译器来说已经很老套了。在你采取激烈措施(比如汇编)之前,你应该真正考虑你程序的大规模结构。 - JustJeff
1
@user775475 - 你知道吗,EBX保存的额外时间可能会增加足够的成本,使C++编译器的版本更快,除非你将其从循环外部计算。 - JustJeff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接