相关疑难解决方法(0)

编译器中的布尔值为8位.对他们的操作是否效率低下?

我正在阅读Agner Fog的" 用C++优化软件 "(特定于英特尔,AMD和威盛的x86处理器),它在第34页说明

布尔变量存储为8位整数,值0表示false,1表示true.布尔变量是超定的,因为所有具有布尔变量作为输入的运算符检查输入是否具有除0或1之外的任何其他值,但是具有布尔值作为输出的运算符不能产生除0或1之外的其他值.布尔变量作为输入效率低于必要的效率.

这今天仍然适用于编译器吗?你能举个例子吗?作者说

如果确定操作数没有除0和1之外的其他值,则可以使布尔运算更有效.编译器没有做出这样的假设的原因是变量可能具有其他值,如果它们是未初始化或来自不明来源.

这是否意味着如果我拿一个函数指针bool(*)()作为示例并调用它,那么对它的操作会产生效率低下的代码?或者是通过取消引用指针或从引用读取然后对其进行操作来访问布尔值的情况?

c c++ optimization x86 boolean

46
推荐指数
2
解决办法
3608
查看次数

微融合和寻址模式

我使用英特尔®架构代码分析器(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
查看次数

在x86上做水平浮点矢量和的最快方法

你有一个三(或四)个浮点数的向量.总结它们的最快方法是什么?

SSE(movaps,shuffle,add,movd)总是比x87快吗?SSE4.2中的水平加法说明值得吗?移动到FPU的成本是多少,然后是faddp,faddp?什么是最快的特定指令序列?

"尝试安排事情,这样你可以一次总结四个向量"将不被接受作为答案.:-)

floating-point optimization x86 assembly sse

41
推荐指数
4
解决办法
2万
查看次数

在某个位置或更低位置计算设置位的有效方法是什么?

给定std::bitset<64> bits任意数量的位和位位置X(0-63)

在X位或更低位计数位的最有效方法是什么,如果未设置X位,则返回0

注意:如果设置该位,则返回始终至少为1

蛮力方式很慢:

int countupto(std::bitset<64> bits, int X)
{
  if (!bits[X]) return 0;
  int total=1;
  for (int i=0; i < X; ++i)
  {
    total+=bits[i];
  }
  return total;
}
Run Code Online (Sandbox Code Playgroud)

这个count()方法bitset将为您popcount提供所有位,但bitset不支持范围

注意:这不是如何计算32位整数中的设置位数?因为它询问所有位而不是0到X的范围

c++ algorithm performance bit-manipulation

33
推荐指数
4
解决办法
5006
查看次数

如何安排x86 uops?

现代x86 CPU将传入的指令流分解为微操作(uops 1),然后在输入准备就绪时将这些uop 无序调度.虽然基本思路很清楚,但我想了解准备好指令的具体细节,因为它会影响微优化决策.

例如,采取以下玩具循环2:

top:
lea eax, [ecx + 5]
popcnt eax, eax
add edi, eax
dec ecx
jnz top
Run Code Online (Sandbox Code Playgroud)

这基本上实现了循环(具有以下对应关系:) eax -> total, c -> ecx:

do {
  total += popcnt(c + 5);
} while (--c > 0);
Run Code Online (Sandbox Code Playgroud)

通过查看uop细分,依赖链延迟等,我熟悉优化任何小循环的过程.在上面的循环中,我们只有一个携带的依赖链:dec ecx.环路(前三指令lea,imul,add)是开始新鲜每个环一个依赖关系链的一部分.

决赛decjne融合.因此,我们总共有4个融合域uop,以及一个仅循环携带的依赖链,延迟为1个周期.因此,基于该标准,似乎循环可以在1个周期/迭代时执行.

但是,我们也应该关注港口压力:

  • lea能够在端口1和5执行
  • popcnt可以在端口1上执行
  • add可以在端口0,1,5和6执行
  • 预测采用jnz在端口6上执行

因此,要进行1次循环/迭代,您几乎需要执行以下操作:

  • popcnt 必须在端口1上执行(它可以执行的唯一端口)
  • lea 必须 …

optimization performance x86 intel cpu-architecture

32
推荐指数
2
解决办法
2907
查看次数

为什么JavaScript看起来比C++快4倍?

很长一段时间以来,我一直认为C++比JavaScript更快.然而,今天我制作了一个基准脚本来比较两种语言中浮点计算的速度,结果令人惊叹!

JavaScript似乎比C++快4倍!

我让这两种语言在我的i5-430M笔记本电脑上做同样的工作,执行a = a + b了1亿次.C++大约需要410毫秒,而JavaScript大约需要120毫秒.

我真的不知道为什么JavaScript在这种情况下运行如此之快.有谁能解释一下?

我用于JavaScript的代码是(使用Node.js运行):

(function() {
    var a = 3.1415926, b = 2.718;
    var i, j, d1, d2;
    for(j=0; j<10; j++) {
        d1 = new Date();
        for(i=0; i<100000000; i++) {
            a = a + b;
        }
        d2 = new Date();
        console.log("Time Cost:" + (d2.getTime() - d1.getTime()) + "ms");
    }
    console.log("a = " + a);
})();
Run Code Online (Sandbox Code Playgroud)

C++的代码(由g ++编译)是:

#include <stdio.h>
#include <ctime>

int main() {
    double a = 3.1415926, b = 2.718;
    int i, …
Run Code Online (Sandbox Code Playgroud)

javascript c++ floating-point performance benchmarking

29
推荐指数
4
解决办法
5万
查看次数

哪些 GCC 优化标志对二进制大小影响最大?

我正在使用 GCC 为 ARM 开发 C++。我遇到了一个问题,我没有启用优化,我无法为我的代码创建二进制文件(ELF),因为它不适合可用空间。但是,如果我只是启用调试优化(-Og)(据我所知这是可用的最低优化),代码就很容易适应。

在这两种情况下,都会启用-ffunction-sections-fdata-sections-fno-exceptions-Wl,--gc-sections 。

  • 闪存大小:512 kB
  • 没有优化:.text 溢出约 200 kB
  • 使用-Og优化:.text 约为 290 kB

即使进行了最小的优化,二进制大小也存在巨大差异。

我查看了3.11 控制优化的选项,详细了解使用 -Og 标志执行哪些优化,看看这是否会给我任何见解。

哪些优化标志对二进制大小影响最大?我应该寻找什么来解释这种巨大的差异吗?

c++ gcc arm code-size compiler-optimization

29
推荐指数
2
解决办法
9484
查看次数

在软件中实现SSE 4.2的CRC32C

所以我的设计结合了CRC32C校验和,以确保数据没有被损坏.我决定使用CRC32C,因为如果运行软件的计算机支持SSE 4.2,我可以同时拥有软件版本和硬件加速版本.

我将使用英特尔的开发人员手册(第2A卷),它似乎提供了该crc32指令背后的算法.但是,我运气不好.英特尔的开发人员指南说明如下:

BIT_REFLECT32: DEST[31-0] = SRC[0-31]
MOD2: Remainder from Polynomial division modulus 2

TEMP1[31-0] <- BIT_REFLECT(SRC[31-0])
TEMP2[31-0] <- BIT_REFLECT(DEST[31-0])
TEMP3[63-0] <- TEMP1[31-0] << 32
TEMP4[63-0] <- TEMP2[31-0] << 32
TEMP5[63-0] <- TEMP3[63-0] XOR TEMP4[63-0]
TEMP6[31-0] <- TEMP5[63-0] MOD2 0x11EDC6F41
DEST[31-0]  <- BIT_REFLECT(TEMP6[31-0])
Run Code Online (Sandbox Code Playgroud)

现在,据我所知,我已经完成了TEMP6正确开始的所有事情,但我想我可能要么误解多项式除法,要么错误地实现它.如果我的理解是正确的,那么1 / 1 mod 2 = 1,0 / 1 mod 2 = 0两个除零都是未定义的.

我不明白的是64位和33位操作数的二进制除法是如何工作的.如果SRC0x00000000,并且DEST0xFFFFFFFF,TEMP5[63-32]将所有设置位,而TEMP5[31-0]将全部取消设置位.

如果我要使用来自TEMP5分子的位,那么将有30个除以零,因为多项式 …

c++ crc32 polynomial-math

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

为什么循环总是被编译成"do ... while"样式(尾部跳转)?

当试图理解汇编(启用编译器优化)时,我看到这种行为:

这样一个非常基本的循环

outside_loop;
while (condition) {
     statements;
}
Run Code Online (Sandbox Code Playgroud)

经常被编译成(伪代码)

    ; outside_loop
    jmp loop_condition    ; unconditional
loop_start:
    loop_statements
loop_condition:
    condition_check
    jmp_if_true loop_start
    ; outside_loop
Run Code Online (Sandbox Code Playgroud)

但是,如果未打开优化,则会编译为通常可理解的代码:

loop_condition:
    condition_check
    jmp_if_false loop_end
    loop_statements
    jmp loop_condition  ; unconditional
loop_end:
Run Code Online (Sandbox Code Playgroud)

根据我的理解,编译后的代码更像是这样的:

goto condition;
do {
    statements;
    condition:
}
while (condition_check);
Run Code Online (Sandbox Code Playgroud)

我看不到巨大的性能提升或代码可读性提升,为什么经常出现这种情况呢?是否有此循环样式的名称,例如"尾随条件检查"?

optimization performance assembly loops micro-optimization

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

C中的浮点运算是关联的吗?

添加在数学上保持关联属性:

(a + b) + c = a + (b + c)
Run Code Online (Sandbox Code Playgroud)

在一般情况下,此属性不适用于浮点数,因为它们表示有限精度的值.

作为优化的一部分,是否允许编译器在从C程序生成机器代码时进行上述替换?它在C标准中的确切位置在哪里?

c math floating-point compiler-optimization

22
推荐指数
2
解决办法
3712
查看次数