标签: micro-optimization

Rust 是否会针对计算范围内的循环进行优化?

作为练习,我尝试对 Rust 1.3.0 中的代码进行微优化。我在数组上有一个循环。像这样的东西:

loop {
    for i in 0..arr.len() {
        // something happens here
    }
}
Run Code Online (Sandbox Code Playgroud)

由于 Rust 中的数组大小是固定的,编译器是否会通过arr.len()仅计算一次并重用该值来优化代码,还是会在顶层循环的每次传递中计算表达式?这个问题可以扩展到除了arr.len().

换句话说,上面的代码是否等同于:

let arr_len = arr.len();

loop {
    for i in 0..arr_len {
        // something happens here
    }
}
Run Code Online (Sandbox Code Playgroud)

optimization micro-optimization rust

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

每个浮点运算花费的时间相同吗?

我相信无论操作数有多大,整数加法或减法总是花费相同的时间。ALU 输出稳定所需的时间可能因输入操作数而异,但利用 ALU 输出的 CPU 组件将等待足够长的时间,以便任何整数运算都将在相同的周期中处理。(ADD、SUB、MUL 和 DIV 所需的周期会有所不同,但我认为,无论输入操作数如何,ADD 都将采用相同的周期。)

对于浮点运算也是如此吗?

我正在尝试实现一个包含大量浮点运算的程序。我想知道缩放我正在处理的数字是否有助于快速运行。

floating-point performance cpu-architecture micro-optimization alu

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

使一个寄存器依赖于另一个寄存器而不改变其值

考虑以下 x86 程序集:

; something that sets rax
mov rcx, [rdi]
xor rax, rcx
xor rax, rcx
Run Code Online (Sandbox Code Playgroud)

在序列末尾,rax的值与进入时的值相同,但从 CPU 的角度来看,它的值取决于从内存加载到 的值rcx。特别是,rax在该加载和两条指令完成之前,后续的使用不会开始xor

有没有什么方法可以比双xor序列更有效地实现这种效果,例如,使用单个单微指令单周期延迟指令?如果某个常量值需要在序列之前设置一次(例如,有一个归零的寄存器),这是可以的。

performance x86 assembly micro-optimization microbenchmark

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

性能:Mod 和赋值与条件和赋值

我在 ISR 中有一个计数器(由 50us 的外部 IRQ 触发)。计数器递增并环绕 MAX_VAL (240)。

我有以下代码:

if(condition){
  counter++;
  counter %= MAX_VAL;
  doStuff(table[counter]);
}
Run Code Online (Sandbox Code Playgroud)

我正在考虑另一种实现:

if(condition){
  //counter++;//probably I would increment before the comparison in production code
  if(++counter >= MAX_VAL){
    counter=0;
  }
  doStuff(table[counter]);
}
Run Code Online (Sandbox Code Playgroud)

我知道人们建议不要尝试这样优化,但这让我想知道。在 x86 上什么更快?MAX_VAL 的什么值可以证明第二个实现是合理的?

大约每 50us 调用一次,因此减少指令集并不是一个坏主意。if(++counter >= MAX_VAL) 将被预测为 false,因此在绝大多数情况下它将删除对 0 的赋值。出于我的目的,我更喜欢 %= 实现的一致性。

c x86 assembly micro-optimization

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

如何实现浮点值的 totalOrder 谓词?

IEEE 754 规范在 §5.10 中定义了一个总顺序,我想在汇编中实现它。

维基百科的描述来看,这听起来很像可以实现无分支,或者几乎无分支,但我一直没能想出一个像样的方法;我在主要编程语言中找不到任何现有的符合规范的实现

比较两个浮点数时,它充当 ? 操作,除了 totalOrder(?0, +0) ? ¬ totalOrder(+0, ?0) 和同一个浮点数的不同表示按它们的指数乘以符号位排序。然后通过排序 ?qNaN < ?sNaN < numbers < +sNaN < +qNaN 将排序扩展到 NaN,同一类中的两个 NaN 之间的排序基于整数有效负载乘以这些数据的符号位。

首先检查 NaN 然后跳转到浮点比较或处理 NaN 情况是否有意义,或者将浮点值移动到整数寄存器并在那里执行所有操作是否更有意义?

(至少从阅读描述来看,感觉规范作者努力允许使用整数指令进行直接实现。)

在 x86-64 处理器上实现浮点总顺序的“最佳”方法是什么?

floating-point assembly x86-64 ieee-754 micro-optimization

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

X86:如何将xmm0的下半部分设置为0,而不影响上半部分?

我使用 xmm0 有 128 位的系统。我想将 [63...0] 设置为零,而不影响 [127...64]。我用:

MOV RAX, 0xFFFFFFFFFFFFFFFF
MOVQ xmm2, RAX
PSHUFD xmm2, xmm2, 0b00001111
PAND xmm1, xmm2
Run Code Online (Sandbox Code Playgroud)

有没有更快的方法?

x86 assembly sse simd micro-optimization

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

班级成员的顺序会影响访问速度吗?

我正在编写一个应该绝对没有开销的委托库。因此,尽可能快地访问函数指针很重要。

所以我的问题是:访问速度是否取决于班级中的成员位置?我听说最重要的成员应该是成员声明中的第一个成员,这对我来说很有意义,因为这意味着this类的指针指向与重要成员相同的地址(假设是非虚拟类)。而如果重要成员将在任何其他位置,CPU 将不得不通过添加this和类布局中的偏移量来计算它的位置。

另一方面,我知道编译器将该地址表示为 a qword-ptr,其中包含偏移量的信息。

所以我的问题归结为:解决 a 是否qword-ptr需要一个恒定的时间,或者如果偏移量不是,它会增加0吗?行为在不同平台上是否保持相同?

c++ performance micro-optimization class-members addressing-mode

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

将 SSE 与 AVX128 混合使用以获得更短的指令?

从我能收集到的所有信息来看,混合 SSE 和 128 位 (E)VEX 编码指令没有性能损失。这表明将两者混合应该没问题。当 SSE 指令通常比 VEX 等效指令短 1 个字节时,这可能是有益的。

但是,我从未见过任何人或任何编译器这样做。作为一个例子,在Intel的AVX(128位)MD5实现方式中,各种vmovdqa可替换为movaps(或者vshufps可以用较短的替换shufps,由于DEST和SRC1寄存器是相同的)。
是否有任何特殊原因可以避免 SSE,或者我遗漏了什么?

x86 assembly sse avx micro-optimization

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

如果距开始位置 &lt;128 字节,则更快地访问结构成员?

Anger Fog 的 C++ optimization manual,我读到:

如果成员相对于结构或类的开头的偏移量小于 128,则访问数据成员的代码会更加紧凑,因为偏移量可以表示为 8 位有符号数。如果相对于结构或类的开头的偏移量是 128 字节或更多,则偏移量必须表示为 32 位数字(指令集在 8 位和 32 位偏移量之间没有任何内容)。例子:

// Example 7.40
class S2 {
public:
int a[100]; // 400 bytes. first byte at 0, last byte at 399
int b; // 4 bytes. first byte at 400, last byte at 403
int ReadB() {return b;}
};
Run Code Online (Sandbox Code Playgroud)

这里 b 的偏移量为 400。任何通过指针或成员函数(如 ReadB)访问 b 的代码都需要将偏移量编码为 32 位数字。如果 a 和 b 交换,则可以使用编码为 8 位有符号数的偏移量访问两者,或者根本没有偏移量。这使得代码更紧凑,从而更有效地使用代码缓存。因此,建议将大数组和其他大对象放在结构或类声明的最后,最常用的数据成员放在最前面。如果不可能在前 128 个字节中包含所有数据成员,则将最常用的成员放在前 128 个字节中。

我曾尝试这样做,我看到在这个测试程序的组件输出没有什么区别,如图所示在这里: …

x86 assembly micro-optimization

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

为什么在 SETcc 之前进行 XOR?

这段代码

int foo(int a, int b)
{ 
    return (a == b);
}
Run Code Online (Sandbox Code Playgroud)

生成以下程序集(https://godbolt.org/z/fWsM1zo6q

foo(int, int):
        xorl    %eax, %eax
        cmpl    %esi, %edi
        sete    %al
        ret
Run Code Online (Sandbox Code Playgroud)

根据https://www.felixcloutier.com/x86/setcc

[SETcc] 根据状态标志的设置将目标操作数设置为 0 或 1

那么,如果根据无论如何结果为零/一,那么首先%eax用零初始化有什么意义呢?是不是gcc和clang出于某种原因都无法避免浪费CPU时钟?xorl %eax, %eaxa == b

c++ x86 assembly micro-optimization

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