标签: compiler-optimization

为什么编译器不能用0优化浮点加法?

我有四个身份函数,它们基本上什么都不做。只有乘法1才能通过 clang 优化为单个ret语句。

float id0(float x) {
    return x + 1 - 1;
}

float id1(float x) {
    return x + 0;
}

float id2(float x) {
    return x * 2 / 2;
}

float id3(float x) {
    return x * 1;
}
Run Code Online (Sandbox Code Playgroud)

以下编译器输出是:(clang 10, at -O3)

.LCPI0_0:
        .long   1065353216              # float 1
.LCPI0_1:
        .long   3212836864              # float -1
id0(float):                                # @id0(float)
        addss   xmm0, dword ptr [rip + .LCPI0_0]
        addss   xmm0, dword ptr [rip + .LCPI0_1] …
Run Code Online (Sandbox Code Playgroud)

c c++ optimization compilation compiler-optimization

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

如何在大型C++项目中检测编译时的瓶颈?

我想减少大型C++项目的编译时间.我尝试使用预编译的头文件,接口等.但在我继续之前,我想知道是否有任何工具可以帮助检测为什么编译时间如此之长.有人建议使用pc-lint,我会试一试. 如何在大型C++项目中检测不必要的#include文件? 但是,如果有其他工具可以分析编译时间并讨论任何提高编译速度的提示,请告诉我.提前致谢.

环境:Microsoft Visual Studio C++ 2008或2010.

c++ compiler-optimization visual-studio-2008 visual-studio

21
推荐指数
2
解决办法
3330
查看次数

为什么-march = native很少使用?

对于大多数C/C++编译器,有一个可传递给编译器的标志-march=native,它告诉编译器调整生成的代码以用于主机CPU的微架构和ISA扩展.即使它没有使用相同的名称,通常也有基于LLVM的编译器的等效选项,例如rustcswiftc.

根据我自己的经验,这个标志可以为数字密集型代码提供大量的加速,而且听起来它可以免于为你自己的机器编译的代码的妥协.也就是说,我认为我没有看到任何默认启用它的构建系统或静态编译器:

  • 显然,任何要求您传递它的命令行编译器可执行文件都不会默认使用它.

  • 我想不出任何默认启用它的IDE.

  • 我想不出任何共同构建系统我已经与(工作的cmake,automake,cargo,spm,等),使其能够在默认情况下,即使是最优化的基础之上.

我可以想到一些原因,但没有一个真的令人满意:

  • 使用-march=native不适合将分发给其他计算机的二进制文件.也就是说,我发现自己为自己的机器编译源代码比其他机器更频繁,这并不能解释它在调试版本中的缺乏用途,因为它没有分发的意图.

  • 至少在Intel x86 CPU上,我的理解是不经常使用AVX指令会降低性能或功率效率,因为AVX单元在不使用时断电,要求它上电以供使用,以及许多Intel CPU下行运行AVX指令.尽管如此,它只解释了为什么不启用AVX,而不是为什么代码不会针对特定微架构处理常规指令而调整.

  • 由于大多数x86 CPU使用具有寄存器重命名的花哨的无序超标量流水线,因此针对特定微架构的调优代码可能并不是特别重要.不过,如果它可以帮助,为什么不使用它呢?

gcc clang compiler-optimization

21
推荐指数
3
解决办法
3691
查看次数

分支似然提示是否通过函数调用进行?

我遇到过一些场景,我想说函数的返回值可能在函数体内,而不是调用它的 if 语句。

例如,假设我想将代码从使用LIKELY宏移植到使用新[[likely]]注释。但这些在句法上不同的地方:

#define LIKELY(...) __builtin_expect(!!(__VA_ARGS__),0)
if(LIKELY(x)) { ... } 
Run Code Online (Sandbox Code Playgroud)

对比

if(x) [[likely]] { ... }
Run Code Online (Sandbox Code Playgroud)

没有简单的方法可以重新定义LIKELY宏以使用注释。会定义一个函数

inline bool likely(bool x) { 
  if(x) [[likely]] return true;
  else return false;
}
Run Code Online (Sandbox Code Playgroud)

将提示传播到if?像

if(likely(x)) { ... }
Run Code Online (Sandbox Code Playgroud)

类似地,在通用代码中,很难在实际if语句中直接表达算法似然信息,即使该信息在其他地方是已知的。例如,a copy_ifwhere 谓词几乎总是假的。据我所知,没有办法用属性来表达,但如果分支权重信息可以通过函数传播,这是一个已解决的问题。

到目前为止,我还没有找到关于这个的文档,我不知道通过查看输出的程序集来测试这个的好设置。

c++ compiler-optimization branch-prediction c++20

21
推荐指数
2
解决办法
770
查看次数

如何阻止 Clang 通过模板过度扩展嵌套循环?

考虑这段代码:

#include <iostream>
typedef long xint;
template<int N>
struct foz {
    template<int i=0>
    static void foo(xint t) {
        for (int j=0; j<10; ++j) {
            foo<i+1> (t+j);
        }
    }
    template<>
    static void foo<N>(xint t) {
        std::cout << t;
    }

};

int main() {
    foz<8>::foo<0>(0);
}
Run Code Online (Sandbox Code Playgroud)

在编译时clang++ -O0,它会在几秒钟内编译然后运行 ​​4 秒。

然而,使用clang++ -O2,编译需要很长的时间和大量的内存。在Compiler Explorer上可以看到,更改8为较小的值后,它完全展开了循环。

我并不是让它完全没有优化,而是让它不递归,就像嵌套循环应该表现的那样。有什么我应该做的吗?

c++ clang compiler-optimization template-meta-programming loop-unrolling

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

goto对C++编译器优化的影响

使用goto现代C++编译器有哪些性能优势或惩罚?

我正在编写一个C++代码生成器,使用goto它会使编写更容易.没有人会触及生成的C++文件,所以不要让我全部"goto is bad".作为一个好处,他们可以节省临时变量的使用.

从纯粹的编译器优化角度来看,我想知道goto对编译器优化器的结果是什么?与使用临时/标志相比,它是否使代码更快,更慢或通常没有性能变化.

c++ optimization goto compiler-optimization

20
推荐指数
3
解决办法
3492
查看次数

用Java编写的编译器:Peephole优化器实现

我正在为Pascal的子集编写一个编译器.编译器为成型机器生成机器指令.我想为这种机器语言编写一个窥视孔优化器,但是我无法替换一些更复杂的模式.

窥孔优化器规范

我研究了几种不同的编写窥视孔优化器的方法,并且我已经确定了一种后端方法:

  • emit()每次生成机器指令时,编码器都会调用一个函数.
  • emit(Instruction currentInstr) 检查窥视孔优化表:
    • 如果当前指令与模式的尾部匹配:
      1. 检查先前发出的匹配说明
      2. 如果所有指令都与模式匹配,则应用优化,修改代码存储的尾端
    • 如果未找到优化,则照常发出指令

目前的设计方法

这个方法很简单,这是我遇到麻烦的实现.在我的编译器中,机器指令存储在一个Instruction类中.我写了一个InstructionMatch类存储正则表达式,用于匹配机器指令的每个组件.如果模式匹配某些机器指令,则其equals(Instruction instr)方法返回.trueinstr

但是,我无法完全应用我的规则.首先,我觉得根据我目前的做法,我最终会得到一堆不必要的物品.鉴于窥视孔优化数字的完整列表可以编号大约400个模式,这将快速失控.此外,我实际上无法使用这种方法进行更难的替换(参见"我的问题").

替代方法

我读过的一篇论文将先前的指令折叠成一个长字符串,使用正则表达式匹配和替换,并将字符串转换回机器指令.这对我来说似乎是一个糟糕的方法,如果我错了,请纠正我.

示例模式,模式语法

x: JUMP x+1; x+1: JUMP y  -->  x: JUMP y
LOADL x; LOADL y; add     -->  LOADL x+y
LOADA d[r]; STOREI (n)    -->  STORE (n) d[r]
Run Code Online (Sandbox Code Playgroud)

请注意,这些示例模式中的每一个都只是以下机器指令模板的人类可读表示:

op_code register n d
Run Code Online (Sandbox Code Playgroud)

(n通常表示字数,d表示地址位移).语法x: <instr>指示该指令存储在x代码存储区中的地址处.

因此,当操作码为5(并且在该指令中未使用)时,该指令LOADL 17等效于整机指令5 0 0 17LOADLnr

我的问题

所以,考虑到这个背景,我的问题是:当我需要将先前指令的部分包含在替换中的变量时,如何有效地匹配和替换模式?例如,我可以简单地用LOADL …

java compiler-construction compiler-optimization

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

优化C代码

对于高性能计算课程的分配,我需要优化以下代码片段:

int foobar(int a, int b, int N)
{
    int i, j, k, x, y;
    x = 0;
    y = 0;
    k = 256;
    for (i = 0; i <= N; i++) {
        for (j = i + 1; j <= N; j++) {
            x = x + 4*(2*i+j)*(i+2*k);
            if (i > j){
               y = y + 8*(i-j);
            }else{
               y = y + 8*(j-i);
            }
        }
    }
    return x;
}
Run Code Online (Sandbox Code Playgroud)

使用一些建议,我设法优化代码(或至少我认为如此),例如:

  1. 不断传播
  2. 代数简化
  3. 复制传播
  4. 常见的Subexpression消除
  5. 死代码消除
  6. 循环不变量删除
  7. 按位移位而不是乘法,因为它们更便宜.

这是我的代码:

int …
Run Code Online (Sandbox Code Playgroud)

c optimization performance bit-shift compiler-optimization

20
推荐指数
4
解决办法
3010
查看次数

确定预处理器中的优化级别?

-Og是一个相对较新的优化选项,旨在改善应用优化时的调试体验.如果用户选择-Og,那么我希望我的源文件激活备用代码路径以增强调试体验.GCC提供__OPTIMIZE__预处理器宏,但只有在优化生效时才设置为1.

有没有办法学习优化级别,比如-O1,-O3或者-Og与预处理器一起使用?

c gcc gdb compiler-optimization c-preprocessor

20
推荐指数
2
解决办法
1890
查看次数

GCC为数组元素的重复XOR生成冗余代码

GCC让我很难为以下源代码生成最佳程序集:

memset(X, 0, 16);
for (int i= 0; i < 16; ++i) {
    X[0] ^= table[i][Y[i]].asQWord;
}
Run Code Online (Sandbox Code Playgroud)

X作为一个uint64_t[2]数组,并且
Y是一个unsigned char[16]数组,并且
table是一个双维数组union qword_t:

union qword_t {
    uint8_t asBytes[8];
    uint64_t asQWord;
};

const union qword_t table[16][256] = /* ... */;
Run Code Online (Sandbox Code Playgroud)

使用选项时,-m64 -Ofast -mno-sse它会展开循环,每个xor和赋值会产生3条指令(因此发出的指令总数为3*16 = 48):

movzx  r9d, byte ptr [Y + i]                   ; extracting byte
xor    rax, qword ptr [table + r9*8 + SHIFT]   ; xoring, SHIFT = i …
Run Code Online (Sandbox Code Playgroud)

c assembly gcc compiler-optimization

20
推荐指数
2
解决办法
2118
查看次数