相关疑难解决方法(0)

ADD 1真的比INC快吗?86

我已经阅读了各种优化指南,声称ADD 1比在x86中使用INC更快.这是真的吗?

optimization performance x86 assembly

17
推荐指数
2
解决办法
4856
查看次数

为什么MSVC在执行此位测试之前会发出无用的MOVSX?

在MSVC 2013中编译以下代码,64位版本构建,/O2优化:

while (*s == ' ' || *s == ',' || *s == '\r' || *s == '\n') {
    ++s;
}
Run Code Online (Sandbox Code Playgroud)

我得到了以下代码 - 使用64位寄存器作为带有bt(位测试)指令的查找表,它具有非常酷的优化.

    mov     rcx, 17596481020928             ; 0000100100002400H
    npad    5
$LL82@myFunc:
    movzx   eax, BYTE PTR [rsi]
    cmp     al, 44                          ; 0000002cH
    ja      SHORT $LN81@myFunc
    movsx   rax, al
    bt      rcx, rax
    jae     SHORT $LN81@myFunc
    inc     rsi
    jmp     SHORT $LL82@myFunc
$LN81@myFunc:
    ; code after loop...
Run Code Online (Sandbox Code Playgroud)

但我的问题是:movsx rax, al第一个分支后的目的是什么?

首先,我们从字符串中加载一个字节rax并对其进行零扩展:

movzx eax, BYTE …
Run Code Online (Sandbox Code Playgroud)

c++ x86 assembly x86-64 visual-studio

15
推荐指数
2
解决办法
918
查看次数

在某些CPU的紧密循环中出现ADC/SBB和INC/DEC问题

我在Delphi中编写一个简单的BigInteger类型.它主要由TLimb的动态数组组成,其中TLimb是32位无符号整数,32位大小字段,它还保存BigInteger的符号位.

要添加两个BigIntegers,我创建一个适当大小的新BigInteger然后,在一些簿记之后,调用以下过程,将三个指针传递给左右操作数和结果的数组的相应开始,以及左右肢的数量分别为.

普通代码:

class procedure BigInteger.PlainAdd(Left, Right, Result: PLimb; LSize, RSize: Integer); 
asm
// EAX = Left, EDX = Right, ECX = Result
        PUSH    ESI
        PUSH    EDI
        PUSH    EBX
        MOV     ESI,EAX                 // Left
        MOV     EDI,EDX                 // Right
        MOV     EBX,ECX                 // Result
        MOV     ECX,RSize               // Number of limbs at Left
        MOV     EDX,LSize               // Number of limbs at Right
        CMP     EDX,ECX
        JAE     @SkipSwap
        XCHG    ECX,EDX                 // Left and LSize should be largest
        XCHG    ESI,EDI                 // so swap
@SkipSwap:
        SUB     EDX,ECX                 // …
Run Code Online (Sandbox Code Playgroud)

delphi x86 assembly

15
推荐指数
2
解决办法
919
查看次数

为什么INC和ADD 1有不同的表现?

多年来我多次读过你应该做XOR ax,ax因为它更快......或者用C编程时使用counter ++或counter + = 1因为它们会INC或ADD ......或者在Netburst Pentium中4 INC比ADD 1慢,因此必须警告编译器你的目标是一个Netburst所以它会将所有var ++翻译成ADD 1 ......

我的问题是:为什么INC和ADD有不同的表现?为什么例如INC声称在Netburst上速度较慢而在其他处理器中比ADD快?

hardware optimization x86 assembly cpu-architecture

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

为什么GCC会为减法发出"lea"而不是"sub"?

我正在看一些通过反汇编一些C程序生成的程序集,我对我经常重复看到的单个优化感到困惑.

当我没有对GCC编译器进行优化时使用subl减法指令,但是当我确实打开了优化(-O3准确地说)时,编译器使用leal指令而不是减法,例如:

没有优化:

83 e8 01     subl $0x1, %eax 
Run Code Online (Sandbox Code Playgroud)

优化

8d 6f ff     leal -0x1(%edi), %ebp 
Run Code Online (Sandbox Code Playgroud)

这两条指令都是3个字节长,所以我没有在这里看到优化.有人可以帮助我并尝试解释编译器的选择吗?

任何帮助,将不胜感激.

x86 assembly gcc gnu-assembler compiler-optimization

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

了解有关i ++和i = i + 1的更多信息

我想知道两种增量形式之间是否存在差异.一些链接说i ++比i = i + 1更快;

另外作为人之一,我的观察对于汇编代码也是一样的.请检查图像,其中汇编代码对于i ++和i = i + 1都是相同的 - 在此输入图像描述

还有另一个链接说,之前的增量运算符比加法和赋值更快,但现在编译器优化i ++和i = i + 1相同.

是否有任何官方文件/文件我们可以参考确认什么是正确的?(我通常会使用信用卡和一个人在stackoverflow上接受的答案数量.在我提供的链接上找不到任何此类信息).

c c++ optimization assembly post-increment

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

分支错误预测是否会冲洗整个管道,即使是非常短的if语句主体?

我读过的所有内容似乎都表明分支错误预测总会导致整个管道被刷新,这意味着浪费了很多周期.我从来没有听到任何人提到短期if条件的任何例外情况.

这似乎在某些情况下会非常浪费.例如,假设您有一个单独的if语句,其中包含一个非常简单的主体,该主体被编译为1个CPU指令.if子句将被编译为一条指令的条件跳转.如果CPU预测分支不被采用,则它将开始执行if-body指令,并可立即开始执行以下指令.现在,一旦if条件的评估已经到达管道的末端,也就是说,例如,12个周期之后,CPU现在知道它的预测是对还是错.如果它被错误预测,并且分支实际被占用,则CPU实际上只需要丢弃来自管道的1条指令(if-body中的指令).但是,如果它刷新整个管道,那么在以下指令中完成的所有工作也都被浪费了,并且必须无缘无故地重复.这是一个深度流水线架构上浪费的大量周期.

那么现代CPU有没有任何机制可以只丢弃短if体内的少数指令?或者它真的冲洗整个管道?如果是后者,那么我认为使用条件移动指令会获得更好的性能.顺便说一下,有没有人知道现代编译器是否善于将短if语句转换为cmov指令?

performance x86 branch cpu-architecture branch-prediction

9
推荐指数
2
解决办法
3584
查看次数

为什么英特尔这些年来改变了静态分支预测机制?

这里我知道英特尔近年来实施了几种静态分支预测机制:

  • 80486年龄:永远不被采取

  • Pentium4年龄:未采取后退/前锋

  • 像Ivy Bridge,Haswell这样的新型CPU变得越来越无形,请参阅Matt G的实验.

英特尔似乎不想再谈论它,因为我在英特尔文档中找到的最新资料大约是十年前写的.

我知道静态分支预测(远远不是)比动态更重要,但在很多情况下,CPU将完全丢失,程序员(使用编译器)通常是最好的指南.当然,这些情况通常不是性能瓶颈,因为一旦频繁执行分支,动态预测器就会捕获它.

由于英特尔不再在其文档中明确声明动态预测机制,因此GCC的builtin_expect()只能从热路径中删除不太可能的分支.

我不熟悉CPU的设计,我不知道究竟是什么机制,目前英特尔使用其静态预测,但我还是觉得英特尔的最佳机制应该清楚地记录他的CPU",我打算去当动态预测失败,向前或向后',因为通常程序员是当时最好的指南.

更新:
我发现你提到的主题逐渐超出我的知识范围.这里涉及一些动态预测机制和CPU内部细节,我在两三天内无法学习.所以请允许我暂时退出你的讨论并充电.
这里仍然欢迎任何答案,也许会帮助更多人

compiler-construction x86 intel cpu-architecture branch-prediction

9
推荐指数
3
解决办法
1637
查看次数

虽然(i--)通过gcc和clang进行优化:为什么他们不使用sub/jnc?

有些人在需要没有计数器或计数器的循环时编写这样的代码n-1, ..., 0:

while (i--) { ... }
Run Code Online (Sandbox Code Playgroud)

一个具体的例子:

volatile int sink;
void countdown_i_used() {
    unsigned i = 1000;
    while (i--) {
         sink = i;  // if i is unused, gcc optimizes it away and uses dec/jnz
    }
}
Run Code Online (Sandbox Code Playgroud)

在GCC 8.2(在Godbolt编译器资源管理器上),它被编译成

# gcc8.2 -O3 -march=haswell
.L2:
    mov     DWORD PTR sink[rip], eax
    dec     eax                      # with tune=generic,  sub eax, 1
    cmp     eax, -1
    jne     .L2
Run Code Online (Sandbox Code Playgroud)

在clang(https://godbolt.org/z/YxYZ95)上,如果不使用计数器,它会变成

if(i) do {...} while(--i);
Run Code Online (Sandbox Code Playgroud)

但如果使用,就像GCC一样

add esi, -1
cmp …
Run Code Online (Sandbox Code Playgroud)

c performance x86 assembly gcc

9
推荐指数
1
解决办法
154
查看次数

为什么Clang只从Sandy Bridge开始做这个优化技巧?

我注意到Clang为以下片段做了一个有趣的部门优化技巧

int64_t s2(int64_t a, int64_t b)
{
    return a/b;
}
Run Code Online (Sandbox Code Playgroud)

如果指定march为Sandy Bridge或更高版本,则下面是装配输出

        mov     rax, rdi
        mov     rcx, rdi
        or      rcx, rsi
        shr     rcx, 32
        je      .LBB1_1
        cqo
        idiv    rsi
        ret
.LBB1_1:
        xor     edx, edx
        div     esi
        ret
Run Code Online (Sandbox Code Playgroud)

以下是签名版本未签名版本的Godbolt链接

根据我的理解,它检查两个操作数的高位是否为零,如果是真的则进行32位除法

我检查了这个表,看到Core2和Nehalem上32/64位分区的延迟分别为40/116和26/89.因此,如果操作数确实通常不宽,那么通过执行32位除法而不是64位除法的节省可能与SnB一样值得

那么为什么它仅适用于SnB和后来的微体系结构?为什么GCC或ICC等其他编译器不这样做呢?

optimization assembly x86-64 division clang

7
推荐指数
1
解决办法
123
查看次数