LOOP(英特尔参考手动输入)递减ecx/rcx,然后如果非零则跳转.这很慢,但是英特尔不能廉价地把它变得很快吗? dec/jnz已经将宏观融合成 Sandybridge家族的一个 uop; 唯一的区别是设置标志.
loop关于各种微体系结构,来自Agner Fog的说明表:
Bulldozer-family/Ryzen:1 m-op(与宏观融合测试和分支相同,或者jecxz)
P4:4次(相同jecxz)
loope/ loopne).吞吐量= 4c(loop)或7c(loope/ne).loope/ loopne). 吞吐量=每5个循环一个,这是将循环计数器保留在内存中的瓶颈!jecxz只有2 uops,吞吐量与普通吞吐量相同jcc难道解码器不能像lea rcx, [rcx-1]/ 那样解码jrcxz吗?这将是3 uops.至少那是没有地址大小前缀的情况,否则它必须使用ecx和截断RIP,EIP如果跳转; 也许奇怪的地址大小选择控制减量的宽度解释了许多uops?
或者更好,只需将其解码为不设置标志的融合分支和分支? dec ecx …
我使用英特尔®架构代码分析器(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上的相同示例".那么正确的答案是什么?
来自Ira Baxter回答,为什么INC和DEC指令不会影响进位标志(CF)?
大多数情况下,我远离
INC而DEC现在,因为他们做的部分条件代码更新,这样就可以在管道中引起滑稽的摊位,和ADD/SUB没有.因此,无关紧要(大多数地方),我使用ADD/SUB避免失速.我使用INC/DEC仅在保持代码较小的情况下,例如,适合高速缓存行,其中一个或两个指令的大小产生足够的差异.这可能是毫无意义的纳米[字面意思!] - 优化,但我在编码习惯上相当老派.
我想问一下为什么它会导致管道中的停顿,而添加不会?毕竟,无论是ADD和INC更新标志寄存器.唯一的区别是INC不更新CF.但为什么重要呢?
使用以下代码是否存在任何执行速度差异:
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更快,但我只是想确定.这可能是规模和速度之间的权衡,或双赢(当然代码将更加不透明).
我正在玩这个答案的代码,稍微修改一下:
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序列和d的imul edx, edx一个): …
我一直认为并且知道,由于更好的局部性和节省空间,仅通过乘法完成一次索引的多维数组比通过两个指针取消引用完成索引的数组数组更快。
前一段时间我做了一个小测试,结果很令人惊讶。至少我的 callgrind 分析器报告说使用数组数组的相同函数运行速度稍快一些。
我想知道是否应该更改矩阵类的定义以在内部使用数组数组。这个类几乎在我的模拟引擎中随处使用(?不太确定如何调用..),我确实想找到节省几秒钟的最佳方法。
test_matrix成本为350 200 020,test_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) 我正在为 ARM8 (aarch64) 编写一些汇编代码。我想做一个除法并使用获得的余数进行进一步计算。在 x86 中,当我使用 'div' 时,我知道我的剩余部分保存在RDX 中。我的问题是 - 是否有与 aarch64 指令集中的等价物?我知道 'udiv' 和 'sdiv' 做无符号和有符号的除法,并得到商数。是否有一条指令可以给我余数?(我想要 c 中的 % 模运算符)。我知道我可以使用代数获得它,只是想确认我没有错过更简单的方法。
测试 AL 中的字节是否为零/非零通常哪个更快?
TEST EAX, EAXTEST AL, AL假设前面的"MOVZX EAX, BYTE PTR [ESP+4]"指令加载了一个带有零扩展的字节参数到 EAX 的其余部分,从而防止了我已经知道的组合值惩罚。
因此 AL=EAX 并且读取 EAX 不会受到部分寄存器的影响。
直观上,仅检查 AL 可能会让您认为它更快,但我敢打赌,对于 >32 位寄存器的字节访问,需要考虑更多的惩罚问题。
任何信息/细节表示赞赏,谢谢!
最近的 Intel CPU 的前端包含一个复杂的解码器和许多简单的解码器。复杂解码器可以处理解码为多个微操作的指令,而简单解码器仅支持解码为单个(融合域)微操作的指令。
是否可以通过简单解码器来解码所有 1-μop 指令,或者是否存在只能由复杂解码器处理的 1-μop 指令?
我正在尝试使用我的 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) (如果是这样的话,我会自己做的.)
我的问题:
为方便起见,我倾向于避免间接/索引寻址模式.
作为替代,我经常使用立即,绝对或寄存器寻址.
代码:
; %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?(我不是装配专家).
在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,x2;op x1,x3; 通常将宏融合到单个微操作中?或者,对于这些操作来说,独立目标是如此不常见,以至于 x86 架构不会费心在单个 uop 中允许它?如果是这样,禁止独立目的地会带来什么效率?
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 等效项,但外观更好。