我有四个身份函数,它们基本上什么都不做。只有乘法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++项目的编译时间.我尝试使用预编译的头文件,接口等.但在我继续之前,我想知道是否有任何工具可以帮助检测为什么编译时间如此之长.有人建议使用pc-lint,我会试一试. 如何在大型C++项目中检测不必要的#include文件? 但是,如果有其他工具可以分析编译时间并讨论任何提高编译速度的提示,请告诉我.提前致谢.
环境:Microsoft Visual Studio C++ 2008或2010.
对于大多数C/C++编译器,有一个可传递给编译器的标志-march=native,它告诉编译器调整生成的代码以用于主机CPU的微架构和ISA扩展.即使它没有使用相同的名称,通常也有基于LLVM的编译器的等效选项,例如rustc或swiftc.
根据我自己的经验,这个标志可以为数字密集型代码提供大量的加速,而且听起来它可以免于为你自己的机器编译的代码的妥协.也就是说,我认为我没有看到任何默认启用它的构建系统或静态编译器:
显然,任何要求您传递它的命令行编译器可执行文件都不会默认使用它.
我想不出任何默认启用它的IDE.
我想不出任何共同构建系统我已经与(工作的cmake,automake,cargo,spm,等),使其能够在默认情况下,即使是最优化的基础之上.
我可以想到一些原因,但没有一个真的令人满意:
使用-march=native不适合将分发给其他计算机的二进制文件.也就是说,我发现自己为自己的机器编译源代码比其他机器更频繁,这并不能解释它在调试版本中的缺乏用途,因为它没有分发的意图.
至少在Intel x86 CPU上,我的理解是不经常使用AVX指令会降低性能或功率效率,因为AVX单元在不使用时断电,要求它上电以供使用,以及许多Intel CPU下行运行AVX指令.尽管如此,它只解释了为什么不启用AVX,而不是为什么代码不会针对特定微架构处理常规指令而调整.
由于大多数x86 CPU使用具有寄存器重命名的花哨的无序超标量流水线,因此针对特定微架构的调优代码可能并不是特别重要.不过,如果它可以帮助,为什么不使用它呢?
我遇到过一些场景,我想说函数的返回值可能在函数体内,而不是调用它的 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 谓词几乎总是假的。据我所知,没有办法用属性来表达,但如果分支权重信息可以通过函数传播,这是一个已解决的问题。
到目前为止,我还没有找到关于这个的文档,我不知道通过查看输出的程序集来测试这个的好设置。
考虑这段代码:
#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
使用goto现代C++编译器有哪些性能优势或惩罚?
我正在编写一个C++代码生成器,使用goto它会使编写更容易.没有人会触及生成的C++文件,所以不要让我全部"goto is bad".作为一个好处,他们可以节省临时变量的使用.
从纯粹的编译器优化角度来看,我想知道goto对编译器优化器的结果是什么?与使用临时/标志相比,它是否使代码更快,更慢或通常没有性能变化.
我正在为Pascal的子集编写一个编译器.编译器为成型机器生成机器指令.我想为这种机器语言编写一个窥视孔优化器,但是我无法替换一些更复杂的模式.
我研究了几种不同的编写窥视孔优化器的方法,并且我已经确定了一种后端方法:
emit()每次生成机器指令时,编码器都会调用一个函数.emit(Instruction currentInstr) 检查窥视孔优化表:
这个方法很简单,这是我遇到麻烦的实现.在我的编译器中,机器指令存储在一个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 …
对于高性能计算课程的分配,我需要优化以下代码片段:
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)
使用一些建议,我设法优化代码(或至少我认为如此),例如:
这是我的代码:
int …Run Code Online (Sandbox Code Playgroud) -Og是一个相对较新的优化选项,旨在改善应用优化时的调试体验.如果用户选择-Og,那么我希望我的源文件激活备用代码路径以增强调试体验.GCC提供__OPTIMIZE__预处理器宏,但只有在优化生效时才设置为1.
有没有办法学习优化级别,比如-O1,-O3或者-Og与预处理器一起使用?
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++ ×5
c ×4
gcc ×3
optimization ×3
clang ×2
assembly ×1
bit-shift ×1
c++20 ×1
compilation ×1
gdb ×1
goto ×1
java ×1
performance ×1