相关疑难解决方法(0)

英特尔Broadwell处理器经历了显着的FMA性能异常

  • 代码1:

    vzeroall
    mov             rcx, 1000000
    startLabel1:
    vfmadd231ps     ymm0, ymm0, ymm0
    vfmadd231ps     ymm1, ymm1, ymm1
    vfmadd231ps     ymm2, ymm2, ymm2
    vfmadd231ps     ymm3, ymm3, ymm3
    vfmadd231ps     ymm4, ymm4, ymm4
    vfmadd231ps     ymm5, ymm5, ymm5
    vfmadd231ps     ymm6, ymm6, ymm6
    vfmadd231ps     ymm7, ymm7, ymm7
    vfmadd231ps     ymm8, ymm8, ymm8
    vfmadd231ps     ymm9, ymm9, ymm9
    vpaddd          ymm10, ymm10, ymm10
    vpaddd          ymm11, ymm11, ymm11
    vpaddd          ymm12, ymm12, ymm12
    vpaddd          ymm13, ymm13, ymm13
    vpaddd          ymm14, ymm14, ymm14
    dec             rcx
    jnz             startLabel1
    
    Run Code Online (Sandbox Code Playgroud)
  • 代码2:

    vzeroall
    mov             rcx, 1000000
    startLabel2:
    vmulps          ymm0, ymm0, ymm0 …
    Run Code Online (Sandbox Code Playgroud)

performance x86 assembly intel fma

35
推荐指数
2
解决办法
2265
查看次数

INC指令与ADD 1:重要吗?

来自Ira Baxter回答,为什么INC和DEC指令不会影响进位标志(CF)?

大多数情况下,我远离INCDEC现在,因为他们做的部分条件代码更新,这样就可以在管道中引起滑稽的摊位,和ADD/ SUB没有.因此,无关紧要(大多数地方),我使用ADD/ SUB避免失速.我使用INC/ DEC仅在保持代码较小的情况下,例如,适合高速缓存行,其中一个或两个指令的大小产生足够的差异.这可能是毫无意义的纳米[字面意思!] - 优化,但我在编码习惯上相当老派.

我想问一下为什么它会导致管道中的停顿,而添加不会?毕竟,无论是ADDINC更新标志寄存器.唯一的区别是INC不更新CF.但为什么重要呢?

performance x86 assembly increment micro-optimization

26
推荐指数
2
解决办法
4234
查看次数

AtomicInteger实现和代码重复

警告:问题有点长,但分离线下方的部分仅用于好奇.

Oracle的AtomicInteger的JDK 7实现包括以下方法:

public final int addAndGet(int delta) {
    for (;;) {
        int current = get();
        int next = current + delta;         // Only difference
        if (compareAndSet(current, next))
            return next;
    }
}

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;             // Only difference
        if (compareAndSet(current, next))
            return next;
    }
}
Run Code Online (Sandbox Code Playgroud)

很明显第二种方法可以写成:

public final int incrementAndGet() {
    return addAndGet(1);
}
Run Code Online (Sandbox Code Playgroud)

在该类中还有其他几个类似代码重复的例子.我想不出有任何理由这样做,而是考虑性能(*).我很确定作者在确定设计之前做了一些深入的测试.

为什么(或在什么情况下)第一个代码比第二个代码表现更好?


(*)我无法抗拒,但写了一个快速的微基准.它显示(后JIT)系统性差距为2-4%,有利于addAndGet(1)vs incrementAndGet()(虽然很小,但它非常一致).说实话,我无法真正解释这个结果......

输出:

incrementAndGet():905 …

java performance jvm

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

在某些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
查看次数

x86 inc与add指令的相对性能

快速提问,事先假设

mov eax, 0
Run Code Online (Sandbox Code Playgroud)

哪个更有效率?

inc eax
inc eax
Run Code Online (Sandbox Code Playgroud)

要么

add eax, 2
Run Code Online (Sandbox Code Playgroud)

另外,如果两个incs更快,编译器(比方说,GCC)通常(即没有积极的优化标志)是否优化var += 2呢?

谢谢你的时间!

PS:不要费心回答"不要过早优化",这仅仅是学术兴趣.

optimization performance x86 assembly

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

两个补码的长整数

我想用英特尔I64汇编程序做一些长整数数学运算(128位),需要创建一个2的补码.让我们说我的正面价值在于RDX:RAX.

2的补码是通过"翻转位并加1"来完成的.所以最天真的实现是(4条指令和14个字节的代码):

  NOT RAX
  NOT RDX
  ADD RAX,1   ; Can't use INC, it doesn't set Carry
  ADC RDX,0
Run Code Online (Sandbox Code Playgroud)

当我在RAX而不是NOT上使用NEG指令时,它对我来说是"+1"但是Carry是错误的,当RAX为零时NEG RAX清除了Carry,但是我需要携带JUST IN THIS CASE.所以下一个最好的方法可能是(4条指令和11个字节的代码):

  NOT RDX
  NEG RAX
  CMC
  ADC RDX,0                  ; fixed, thanks lurker
Run Code Online (Sandbox Code Playgroud)

还有4条说明.但是不是加+1,我可以减去-1,因为SBB将Carry-Bit加到减数上,当Carry清零时我会加+1.所以我的下一个最好的尝试是这个,有3个指令和10个字节的代码:

   NOT RDX
   NEG RAX
   SBB RDX,-1
Run Code Online (Sandbox Code Playgroud)

从我冗长的文字中可以看出,这一点并不明显.是否有一种更好,更易理解的方法来在汇编程序中进行级联2的补码?

assembly x86-64 micro-optimization twos-complement

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

为什么.Net Native以相反的顺序编译循环?

我正在研究.Net Native编译器执行的优化技术.我创建了一个示例循环:

        for (int i = 0; i < 100; i++)
        {
            Function();
        }
Run Code Online (Sandbox Code Playgroud)

我用Native编译了它.然后我.dll用IDA里面的机器代码反汇编了结果文件.结果,我有:

在此输入图像描述

(我删除了一些不必要的行,所以不要担心地址行不一致)

我明白这add esi, 0FFFFFFFFh意味着subtract one from esi and alter Zero Flag if needed,所以如果还没有达到零,我们可以跳到开头.

我不明白的是为什么编译器重新循环?

我得出结论

LOOP:
add esi, 0FFFFFFFFh
jnz LOOP
Run Code Online (Sandbox Code Playgroud)

比例如更快

LOOP:
inc esi
cmp esi, 064h
jl LOOP
Run Code Online (Sandbox Code Playgroud)

但是真的是因为这个并且速度差异真的很重要吗?

c# x86 assembly micro-optimization .net-native

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

什么是局部标志失速?

我刚刚查看了彼得·科德斯(Peter Cordes)的回答,他说,

如果读取标志,则部分标志停顿会发生,如果它们确实发生的话。P4永远不会有部分标志停顿,因为它们永远不需要合并。相反,它具有错误的依赖关系。几个答案/评论混淆了术语。它们描述了一个错误的依赖关系,但随后将其称为部分标志停顿。这是由于仅写入一些标志而导致的速度下降,但是术语“部分标志停顿”是指必须合并部分标志写入时在SnB之前的Intel硬件上发生的情况。英特尔SnB系列CPU插入一个额外的uop来合并标志而不会停顿。Nehalem和更早的失速约7个周期。我不确定AMD CPU会受到多大的损失。

我感觉我还不明白什么是“部分国旗摊位”。我怎么知道一个人发生了?除了读取标志的某些时间之外,什么触发事件?合并标志是什么意思?在什么情况下会“写一些标志”,但不会发生部分标志合并?我需要了解哪些有关旗位的知识才能理解它们?

x86 assembly

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

现代 CPU 是否需要花费大量资源来保持标志更新?

据我了解,在现代无序 CPU 上,最昂贵的事情之一是状态,因为必须在多个版本中跟踪该状态,并在许多指令中保持最新状态等。

一些像 x86 和 ARM 的指令集大量使用了标志,当成本模型不是今天的样子时引入了标志,而标志只需要几个逻辑门。诸如每个算术指令设置标志以检测零、进位和溢出之类的事情。

这些在现代乱序实现上保持更新是否特别昂贵?例如,一条 ADD 指令更新进位标志,这必须被跟踪,因为虽然它可能永远不会被使用,但有可能其他指令可以在 N 条指令后使用它,对 N 没有固定的上限?

在没有这些标志的指令集架构(如 MIPS)上,诸如加法和减法之类的整数运算是否更便宜?

performance assembly cpu-architecture micro-optimization eflags

5
推荐指数
1
解决办法
120
查看次数