Assembly 汇编程序任务:翻译

Assembly 汇编程序任务:翻译,assembly,Assembly,为什么.L21(比.L27长)比.L27快 为什么flag-funroll循环会加速loop1,但不会加速loop2?无论如何,关于你的问题。。。第一个循环在循环内部没有依赖关系,即循环的每个迭代都是独立的,可以尽快计算(实际上,除了最后一个迭代之外的所有迭代都可以扔掉,因为它们根本不影响返回值) 每次迭代的第二个循环取决于上一个结果,因此CPU必须等待下一个imul,直到上一个结果就绪。我想现代x86上的imul的吞吐量仍在1.0左右,但延迟可能在1.0以上,并且不确定依赖项会做什么,这完全取

为什么.L21(比.L27长)比.L27快


为什么flag-funroll循环会加速loop1,但不会加速loop2?

无论如何,关于你的问题。。。第一个循环在循环内部没有依赖关系,即循环的每个迭代都是独立的,可以尽快计算(实际上,除了最后一个迭代之外的所有迭代都可以扔掉,因为它们根本不影响返回值)

每次迭代的第二个循环取决于上一个结果,因此CPU必须等待下一个
imul
,直到上一个结果就绪。我想现代x86上的
imul
的吞吐量仍在1.0左右,但延迟可能在1.0以上,并且不确定依赖项会做什么,这完全取决于您的目标CPU平台,您没有指定。(彼得·科尔德斯(Peter Cordes)这样的人肯定可以为特定的现代英特尔微体系结构回答这个问题,或者你也可以自己阅读Agner的表格,但由于你没有指定目标体系结构,我认为没有必要在现实世界中举任何特定的例子,对我来说,这种一般的聊天级别就足够了)

例如,在80386上,我想第二个循环会更快,因为它的指令更少,而80386内部仍然非常“简单”,在这两种情况下,
imul
都需要几个时钟。在最新的英特尔CPU上,这种依赖性可能会使它偏向于第一个CPU,但不会太大,因为
imul
在今天相当快

无论如何,这是一个很好的例子,如何首先整理算法,并对其进行调整,将为您带来最大的性能增益,因为第一个循环不是真正的循环,而将其编写为简单的公式将使代码更快

奇怪的是,我在godbolt explorer中试过,现代编译器是如何处理它的,gcc做了一些非常复杂的事情来读取每个数组成员,或者这堵指令墙到底做了什么(懒得签入调试器),而clang编译器确实看穿了它并生成了简化的公式:

注意:第一个回路可简化为:

int loop1_fix(int *a, int x, int n) {
    if (0 < n) return a[n-1]*x*x*x*x;
    else return x*x*x;
}
int loop1\u fix(int*a,int x,int n){
如果(0
因为第一个案例未正确优化。。。可以缩短为
返回a[n-1]*x*x*x*x,丢弃整个循环。=我的意思是,你的问题毫无意义,编译器必须首先生成正确的代码,是否接近最优解是次要的,取决于源代码有多大,编写得有多好,使用了哪些选项和编译器,等等。。。您甚至没有指定使用了哪些编译器和哪些选项,所以完全不可能说出为什么会出现特定的机器代码,但即使有细节也很难。也许我对缩短的看法是错误的,是吗?我必须检查一下,gcc 7.3没有找到它,我想这对我来说是个很大的错误,而不是一天中这么早就成为那个超人。。。哦,clang找到了,所以它实际上只是gcc没有认识到简化的公式。有趣的(我没有提到
n
的测试当然是必要的,为了
完全跳过
(n什么硬件有速度差?IvyBridge和更高版本应该能够以每时钟1
imul
的速度运行
loop1
(由于
mov
-消除,3个ALU UOP)因此,在最近的硬件上,
-funroll循环
应该只有在帮助gcc省略无用的
imul
循环迭代指令而不是上一次迭代时才有帮助,或者完全优化掉循环。是的,
-funroll循环
只是帮助gcc完成错过的优化。尽管这段代码似乎来自于gcc,与gcc4.1类似。即使gcc4.9也将
imul
排除在循环之外,但即使是gcc8.1也会执行每个加载。(尽管使用SSE2或AVX2进行自动矢量化!)根据
imul r32,r/m32
是3c延迟,在英特尔Core2和Ryzen上的吞吐量都是1c。(因此,对于较大的
n
,依赖环路比独立环路慢约3倍)。在Ryzen之前的AMD上,它的延迟为4c,吞吐量为2c,吞吐量与延迟瓶颈之间的差异仅为2倍。