相关疑难解决方法(0)

为什么循环指令慢?英特尔无法有效实施吗?

LOOP(英特尔参考手动输入)递减ecx/rcx,然后如果非零则跳转.这很慢,但是英特尔不能廉价地把它变得很快吗? dec/jnz已经将宏观融合成 Sandybridge家族的一个 uop; 唯一的区别是设置标志.

loop关于各种微体系结构,来自Agner Fog的说明表:

  • K8/K10:7 m-ops
  • Bulldozer-family/Ryzen:1 m-op(与宏观融合测试和分支相同,或者jecxz)

  • P4:4次(相同jecxz)

  • P6(PII/PIII):8次
  • Pentium M,Core2:11 uops
  • Nehalem:6个uops.(11为loope/ loopne).吞吐量= 4c(loop)或7c(loope/ne).
  • SnB家族:7个uops.(11为loope/ loopne). 吞吐量=每5个循环一个,这是将循环计数器保留在内存中的瓶颈!jecxz只有2 uops,吞吐量与普通吞吐量相同jcc
  • Silvermont:7次
  • AMD Jaguar(低功耗):8 uops,5c吞吐量
  • 通过Nano3000:2 uops

难道解码器不能像lea rcx, [rcx-1]/ 那样解码jrcxz吗?这将是3 uops.至少那是没有地址大小前缀的情况,否则它必须使用ecx和截断RIP,EIP如果跳转; 也许奇怪的地址大小选择控制减量的宽度解释了许多uops?

或者更好,只需将其解码为不设置标志的融合分支和分支? dec ecx …

performance x86 assembly intel cpu-architecture

53
推荐指数
3
解决办法
6096
查看次数

微融合和寻址模式

我使用英特尔®架构代码分析器(IACA)发现了一些意想不到的东西(对我而言).

以下指令使用[base+index]寻址

addps xmm1, xmmword ptr [rsi+rax*1]
Run Code Online (Sandbox Code Playgroud)

根据IACA没有微熔丝.但是,如果我用[base+offset]这样的

addps xmm1, xmmword ptr [rsi]
Run Code Online (Sandbox Code Playgroud)

IACA报告它确实融合了.

英特尔优化参考手册的第2-11节给出了以下"可以由所有解码器处理的微融合微操作"的示例

FADD DOUBLE PTR [RDI + RSI*8]
Run Code Online (Sandbox Code Playgroud)

Agner Fog的优化装配手册也给出了使用[base+index]寻址的微操作融合的例子.例如,请参见第12.2节"Core2上的相同示例".那么正确的答案是什么?

cpu x86 assembly intel iaca

44
推荐指数
4
解决办法
4504
查看次数

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

使用CMP reg测试寄存器是否为零,0与OR reg,reg?

使用以下代码是否存在任何执行速度差异:

cmp al, 0
je done
Run Code Online (Sandbox Code Playgroud)

以下内容:

or al, al
jz done
Run Code Online (Sandbox Code Playgroud)

我知道JE和JZ指令是相同的,并且使用OR可以提供一个字节的大小改进.但是,我也关心代码速度.逻辑运算符似乎比SUB或CMP更快,但我只是想确定.这可能是规模和速度之间的权衡,或双赢(当然代码将更加不透明).

optimization x86 assembly micro-optimization

13
推荐指数
2
解决办法
3755
查看次数

了解lfence对具有两个长依赖链的循环的影响,以增加长度

我正在玩这个答案的代码,稍微修改一下:

BITS 64

GLOBAL _start

SECTION .text

_start:
 mov ecx, 1000000

.loop:

 ;T is a symbol defined with the CLI (-DT=...)

 TIMES T imul eax, eax
 lfence
 TIMES T imul edx, edx


 dec ecx
jnz .loop

 mov eax, 60           ;sys_exit
 xor edi, edi
 syscall
Run Code Online (Sandbox Code Playgroud)

没有lfence我,我得到的结果与答案中的静态分析一致.

当我介绍一个单一 lfence我期望的CPU执行imul edx, edx的序列的第k个平行于迭代imul eax, eax的下一个(的序列K + 1个)迭代.
像这样的东西(调用一个imul eax, eax序列和dimul edx, edx一个): …

performance x86 assembly cpu-architecture perf

13
推荐指数
2
解决办法
472
查看次数

多维数组和数组的数组的性能

我一直认为并且知道,由于更好的局部性和节省空间,仅通过乘法完成一次索引的多维数组比通过两个指针取消引用完成索引的数组数组更快。

前一段时间我做了一个小测试,结果很令人惊讶。至少我的 callgrind 分析器报告说使用数组数组的相同函数运行速度稍快一些。

我想知道是否应该更改矩阵类的定义以在内部使用数组数组。这个类几乎在我的模拟引擎中随处使用(?不太确定如何调用..),我确实想找到节省几秒钟的最佳方法。

test_matrix成本为350 200 020test_array_array成本为325 200 016-O3该代码由编译clang++。所有成员函数都是根据探查器内联的。

#include <iostream>
#include <memory>

template<class T>
class BasicArray : public std::unique_ptr<T[]> {
public:
    BasicArray() = default;
    BasicArray(std::size_t);
};

template<class T>
BasicArray<T>::BasicArray(std::size_t size)
: std::unique_ptr<T[]>(new T[size]) {}

template<class T>
class Matrix : public BasicArray<T> {
public:
    Matrix() = default;
    Matrix(std::size_t, std::size_t);
    T &operator()(std::size_t, std::size_t) const;
    std::size_t get_index(std::size_t, std::size_t) const;
    std::size_t get_size(std::size_t) const;

private:
    std::size_t sizes[2];
};

template<class T>
Matrix<T>::Matrix(std::size_t i, …
Run Code Online (Sandbox Code Playgroud)

c++ arrays performance multidimensional-array

6
推荐指数
2
解决办法
274
查看次数

使用单个 aarch64 指令获取余数?

我正在为 ARM8 (aarch64) 编写一些汇编代码。我想做一个除法并使用获得的余数进行进一步计算。在 x86 中,当我使用 'div' 时,我知道我的剩余部分保存在RDX 中。我的问题是 - 是否有与 aarch64 指令集中的等价物?我知道 'udiv' 和 'sdiv' 做无符号和有符号的除法,并得到商数。是否有一条指令可以给我余数?(我想要 c 中的 % 模运算符)。我知道我可以使用代数获得它,只是想确认我没有错过更简单的方法。

assembly arm modulo integer-division arm64

5
推荐指数
2
解决办法
2202
查看次数

在 x86 ASM 中测试零通常更快:“TEST EAX, EAX”与“TEST AL, AL”?

测试 AL 中的字节是否为零/非零通常哪个更快?

  • TEST EAX, EAX
  • TEST AL, AL

假设前面的"MOVZX EAX, BYTE PTR [ESP+4]"指令加载了一个带有零扩展的字节参数到 EAX 的其余部分,从而防止了我已经知道的组合值惩罚。

因此 AL=EAX 并且读取 EAX 不会受到部分寄存器的影响。

直观上,仅检查 AL 可能会让您认为它更快,但我敢打赌,对于 >32 位寄存器的字节访问,需要考虑更多的惩罚问题。

任何信息/细节表示赞赏,谢谢!

performance x86 assembly micro-optimization

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

最近英特尔微架构中的简单解码器能否处理所有 1-µop 指令?

最近的 Intel CPU 的前端包含一个复杂的解码器和许多简单的解码器。复杂解码器可以处理解码为多个微操作的指令,而简单解码器仅支持解码为单个(融合域)微操作的指令。

是否可以通过简单解码器来解码所有 1-μop 指令,或者是否存在只能由复杂解码器处理的 1-μop 指令?

cpu x86 x86-64 intel cpu-architecture

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

两个可熔断对可以在同一时钟周期内解码吗?

我正在尝试使用我的 Intel i7-10700 和 ubuntu 20.04 来验证两个可熔断对可以在同一时钟周期内解码的结论。

测试代码排列如下,复制了8000次左右,以避免LSD和DSB的影响(主要使用MITE)。

ALIGN 32
.loop_1:
    dec ecx
    jge .loop_2
.loop_2:
    dec ecx
    jge .loop_3
.loop_3:
    dec ecx
    jge .loop_4
.loop_4:
.loop_5:
    dec ecx
    jge .loop_6
Run Code Online (Sandbox Code Playgroud)

测试结果表明,单个循环中仅融合一对。( r479 div r1002479 )

 Performance counter stats for process id '22597':

   120,459,876,711      cycles                                                      
    35,514,146,968      instructions     #    0.29  insn per cycle         
    17,792,584,278      r479             # r479: Number of uops delivered                     
                                         # to Instruction Decode Queue (IDQ) from MITE path                                  
        50,968,497      r4002479        
                                         
                                                  
    17,756,894,879      r1002479         # r1002479: Cycles MITE is …
Run Code Online (Sandbox Code Playgroud)

cpu x86 assembly intel cpu-architecture

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

x86_64 - 汇编 - 循环条件和乱序

不是要求基准.

(如果是这样的话,我会自己做的.)


我的问题:

为方便起见,我倾向于避免间接/索引寻址模式.

作为替代,我经常使用立即,绝对或寄存器寻址.

代码:

; %esi has the array address. Say we iterate a doubleword (4bytes) array.
; %ecx is the array elements count
(0x98767) myloop:
    ... ;do whatever with %esi
    add $4, %esi
    dec %ecx
    jnz 0x98767;
Run Code Online (Sandbox Code Playgroud)

在这里,我们有一个序列化的组合(dec和jnz),它可以防止正常的乱序执行(依赖).

有没有办法避免/破坏dep?(我不是装配专家).

assembly loops x86-64 conditional-statements

4
推荐指数
1
解决办法
1070
查看次数

为什么 x86 通常不允许目标寄存器不是第一个源寄存器?

在RISC-V中,可以Regs[x1] <- Regs[x2]+Regs[x3]使用指令执行整数运算

add x1,x2,x3
Run Code Online (Sandbox Code Playgroud)

在 x86 中,相同的操作显然需要两条指令,

mov x1,x2
add x1,x3
Run Code Online (Sandbox Code Playgroud)

该模式对于 x86 中的其他基本指令(例如、和 )src1 <- src1 op src2似乎很常见。然而,x86 确实有浮点数的eg 。andorsubdest <- src1 op src2add

是双指令模式mov x1,x2op x1,x3; 通常将宏融合到单个微操作中?或者,对于这些操作来说,独立目标是如此不常见,以至于 x86 架构不会费心在单个 uop 中允许它?如果是这样,禁止独立目的地会带来什么效率?

x86 assembly cpu-architecture riscv

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

有没有更好的方法来检测 16 字节标志数组中设置的位?

    ALIGNTO(16) uint8_t noise_frame_flags[16] = { 0 };

    // Code detects noise and sets noise_frame_flags omitted

    __m128i xmm0            = _mm_load_si128((__m128i*)noise_frame_flags);
    bool    isNoiseToCancel = _mm_extract_epi64(xmm0, 0) | _mm_extract_epi64(xmm0, 1);

    if (isNoiseToCancel)
        cancelNoises(audiobuffer, nAudioChannels, audio_samples, noise_frame_flags);
Run Code Online (Sandbox Code Playgroud)

这是我在 Linux 上的 AV Capture 工具的代码片段。这里的noise_frame_flags是16通道音频的标志数组。对于每个通道,相应的字节可以是 0 或 1。1 表示该通道有一些噪声需要消除。例如,如果noise_frame_flags[0] == 1,则意味着设置了第一个通道噪声标志(通过省略的代码)。

即使设置了一个“标志”,我也需要调用cancelNoises. 这段代码在这方面似乎工作得很好。正如您所看到的,我曾经_mm_load_si128加载正确对齐的整个标志数组,然后加载两个_mm_extract_epi64来提取“标志”。我的问题是有更好的方法来做到这一点(也许使用流行计数)?

注意:ALIGNTO(16)是一个宏扩展以纠正 GCC 等效项,但外观更好。

c++ sse x86-64 simd micro-optimization

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