相关疑难解决方法(0)

x86的MOV真的可以"免费"吗?为什么我不能重现这个呢?

我一直看到人们声称MOV指令可以在x86中免费,因为寄存器重命名.

对于我的生活,我无法在一个测试用例中验证这一点.每个测试用例我尝试揭穿它.

例如,这是我用Visual C++编译的代码:

#include <limits.h>
#include <stdio.h>
#include <time.h>

int main(void)
{
    unsigned int k, l, j;
    clock_t tstart = clock();
    for (k = 0, j = 0, l = 0; j < UINT_MAX; ++j)
    {
        ++k;
        k = j;     // <-- comment out this line to remove the MOV instruction
        l += j;
    }
    fprintf(stderr, "%d ms\n", (int)((clock() - tstart) * 1000 / CLOCKS_PER_SEC));
    fflush(stderr);
    return (int)(k + j + l);
}
Run Code Online (Sandbox Code Playgroud)

这为循环生成以下汇编代码(随意生成这个你想要的;你显然不需要Visual C++):

LOOP:
    add edi,esi
    mov …
Run Code Online (Sandbox Code Playgroud)

c x86 assembly cpu-registers micro-optimization

23
推荐指数
2
解决办法
2113
查看次数

为什么交换不在C++中使用Xor操作

我已经了解到Xor操作可用于实现有效的交换功能.像这样:

template<class T>
void swap(T& a, T& b)
{
    a = a^b;
    b = a^b;
    a = a^b;
}
Run Code Online (Sandbox Code Playgroud)

但是我可以在互联网上找到的交换实现基本上是这样的:

template<class T>
void swap(T& a, T& b)
{
    T temp(a);
    a = b;
    b = temp;
}
Run Code Online (Sandbox Code Playgroud)

似乎编译器没有为上面的两个表单生成相同的代码,因为我在VC++ 2010上测试了它,第一个更快地完成了工作(并且比std :: swap更快).第一个是便携式还是其他任何问题?随意纠正我的任何错误,因为我不是英语本地人,不擅长C++.

c++ swap xor premature-optimization micro-optimization

11
推荐指数
2
解决办法
3045
查看次数

为什么XCHG reg,注册了关于现代英特尔架构的3微操作指令?

我正在对代码的性能关键部分进行微优化,并且遇到了指令序列(在AT&T语法中):

add %rax, %rbx
mov %rdx, %rax
mov %rbx, %rdx
Run Code Online (Sandbox Code Playgroud)

我以为我终于有一个用例xchg可以让我刮一个指令并写:

add  %rbx, %rax
xchg %rax, %rdx
Run Code Online (Sandbox Code Playgroud)

然而,根据Agner Fog的指令表,我发现这xchg是一个3微操作指令,在Sandy Bridge,Ivy Bridge,Broadwell,Haswell甚至Skylake上有2个周期延迟.3个完整的微操作和2个周期的延迟!3微操作抛出了我的4-1-1-1的节奏和2周期延迟使得它比在最好的情况下原来的,因为在原来的并行执行可能最后2条指令差.

现在......我得知CPU可能会将指令分解为相当于以下内容的微操作:

mov %rax, %tmp
mov %rdx, %rax
mov %tmp, %rdx 
Run Code Online (Sandbox Code Playgroud)

哪里tmp是匿名内部寄存器,我想最后两个微操作可以并行运行,因此延迟是2个周期.

鉴于寄存器重命名发生在这些微架构上,但对我来说这是以这种方式完成的.为什么寄存器重命名器不会交换标签?理论上,这将只有1个周期(可能是0?)的延迟,并且可以表示为单个微操作,因此它会便宜得多.

performance x86 assembly intel

11
推荐指数
1
解决办法
1907
查看次数

C/C++ 中高效的防溢出算术平均值

两个无符号整数的算术平均值定义为:

mean = (a+b)/2
Run Code Online (Sandbox Code Playgroud)

直接在 C/C++ 中实现可能会溢出并产生错误的结果。正确的实现可以避免这种情况。一种编码方式可能是:

mean = a/2 + b/2 + (a%2 + b%2)/2
Run Code Online (Sandbox Code Playgroud)

但这会使用典型的编译器生成相当多的代码。在汇编程序中,这通常可以更有效地完成。例如,x86 可以通过以下方式执行此操作(汇编器伪代码,我希望您明白这一点):

ADD a,b   ; addition, leaving the overflow condition in the carry bit
RCR a,1   ; rotate right through carry, effectively a division by 2
Run Code Online (Sandbox Code Playgroud)

在这两条指令之后,结果位于 中a,除法的余数位于进位位中。如果需要正确的舍入,第三ADC条指令必须将进位添加到结果中。

请注意,使用了 RCR 指令,该指令通过进位来循环寄存器。在我们的例子中,它是旋转一个位置,以便前一个进位成为寄存器中的最高有效位,并且新的进位保存寄存器中的前一个LSB​​。看来 MSVC 甚至没有提供该指令的内在函数。

是否存在已知的 C/C++ 模式可以被优化编译器识别,从而生成如此高效的代码?或者,更一般地说,是否有一种合理的方法如何在 C/C++ 源代码级别进行编程,以便编译器使用进位位来优化生成的代码?

编辑:

1 小时的讲座std::midpointhttps://www.youtube.com/watch ?v=sBtAGxBh-XI

哇!

EDIT2:微软博客上的精彩讨论

c c++ optimization intrinsics compiler-optimization

10
推荐指数
2
解决办法
479
查看次数

在预测现代超标量处理器上的操作延迟时需要考虑哪些因素以及如何手动计算它们?

我希望能够手动预测任意算术的长度(即没有分支或内存,尽管这也很好)x86-64汇编代码将采用特定的体系结构,考虑到指令重新排序,超标量,延迟,消费者价格指数等

什么/描述必须遵循的规则才能实现这一目标?


我想我已经找到了一些初步规则,但是我没有找到任何关于将任何示例代码分解为这个详细程度的引用,所以我不得不做一些猜测.(例如,英特尔优化手册甚至几乎没有提到指令重新排序.)

至少,我正在寻找(1)确认每条规则是正确的,或者是每条规则的正确陈述,以及(2)我可能忘记的任何规则的列表.

  • 每个循环发出尽可能多的指令,从当前循环开始按顺序开始,并且可能与重新排序缓冲区大小一样远.
  • 如果出现以下情况,可以在给定周期发出指令:
    • 没有影响其操作数的指令仍在执行中.和:
    • 如果它是浮点指令,则它之前的每个浮点指令都被发出(浮点指令具有静态指令重新排序).和:
    • 该循环有一个功能单元可用于该指令.每个(?)功能单元是流水线的,这意味着它可以在每个周期接受1个新指令,并且对于给定功能类的CPI,总功能单元的数量是1/CPI(这里模糊不清:可能是例如addps并且subps使用相同的功能) unit?我如何确定?).和:
    • 4此循环已经发出少于超标量宽度(通常)指令的数量.
  • 如果不能发出指令,则处理器不会发出任何称为"停顿"的条件.

例如,请考虑以下示例代码(计算交叉产品):

shufps   xmm3, xmm2, 210
shufps   xmm0, xmm1, 201
shufps   xmm2, xmm2, 201
mulps    xmm0, xmm3
shufps   xmm1, xmm1, 210
mulps    xmm1, xmm2
subps    xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

我试图预测Haswell的延迟看起来像这样:

; `mulps`  Haswell latency=5, CPI=0.5
; `shufps` Haswell latency=1, CPI=1
; `subps`  Haswell latency=3, CPI=1

shufps   xmm3, xmm2, 210   ; cycle  1
shufps   xmm0, xmm1, 201   ; cycle  2
shufps   xmm2, xmm2, 201   ; …
Run Code Online (Sandbox Code Playgroud)

assembly pipeline latency x86-64 superscalar

8
推荐指数
1
解决办法
268
查看次数

为什么 rv32gc 针对 RISC-V 优化带有分支的无分支代码?

让我们尝试定义一个返回两个值 x 和 y 中的最大值的函数。这些公式有效的充分条件是,对于有符号整数,\xe2\x80\x932^30 <= x, y <= 2^30 \xe2\x80\x93 1,对于无符号整数,0 <= x, y <= 2^31 \xe2\x80\x93 1即,只需要处理有效减少一位的缩小整数范围)。最通用(且最简单)的实现是:

\n
int max(int x, int y) {\n    return (x > y) ? x : y\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在 Pentium Pro 之前,这将是 x86 中生成的程序集:

\n
max:  # GCC -O3 -march=pentium -m32\n  mov eax, DWORD PTR [esp+8]\n  cmp eax, DWORD PTR [esp+4]\n  jge .L4\n  mov eax, DWORD PTR [esp+4]\n.L4:\n  ret\n
Run Code Online (Sandbox Code Playgroud)\n

但从 Pentium Pro 开始,CMOVcc r32,r/m引入了一条新指令,它根据某些指定的状态标志执行条件移动: …

gcc clang compiler-optimization branch-prediction riscv

6
推荐指数
1
解决办法
460
查看次数

用 8086 汇编语言交换 2 个寄存器(16 位)

有人知道如何在不使用其他变量、寄存器、堆栈或任何其他存储位置的情况下交换 2 个寄存器的值吗?谢谢!

就像交换AX,BX。

assembly cpu-registers 16-bit x86-16

1
推荐指数
1
解决办法
2万
查看次数