经过十多年的C/C++编码,我注意到了以下模式 - 非常优秀的程序员倾向于详细了解编译器的内部结构.
我是一个相当优秀的程序员,我有一个特殊的编译器"迷信"集合,所以我想重新启动我的知识并从基础开始.
有人可以推荐在线资源或喜欢的书籍链接吗?我对C/C++编译,优化,GCC和LLVM特别感兴趣.
我经常使用const未修改的局部变量,如下所示:
const float height = person.getHeight();
Run Code Online (Sandbox Code Playgroud)
我认为它可以使编译后的代码更快,允许编译器进行更多优化.或者我错了,编译器可以自己弄清楚局部变量永远不会被修改?
在编译时gcc -O3,为什么以下循环没有矢量化(自动):
#define SIZE (65536)
int a[SIZE], b[SIZE], c[SIZE];
int foo () {
int i, j;
for (i=0; i<SIZE; i++){
for (j=i; j<SIZE; j++) {
a[i] = b[i] > c[j] ? b[i] : c[j];
}
}
return a[0];
}
Run Code Online (Sandbox Code Playgroud)
什么时候呢?
#define SIZE (65536)
int a[SIZE], b[SIZE], c[SIZE];
int foov () {
int i, j;
for (i=0; i<SIZE; i++){
for (j=i; j<SIZE; j++) {
a[i] += b[i] > c[j] ? b[i] : c[j];
}
}
return a[0];
}
Run Code Online (Sandbox Code Playgroud)
唯一的区别在于内部循环中的表达式的结果是 …
一般概念似乎与正确使用的拥有原始指针相比没有时间开销,只要std::unique_ptr有足够的优化.
但是std::unique_ptr,特别是在复合数据结构中使用std::vector<std::unique_ptr<T>>呢?例如,调整矢量的基础数据的大小,这可能发生在push_back.各地要隔离性能,我环路pop_back,shrink_to_fit,emplace_back:
#include <chrono>
#include <vector>
#include <memory>
#include <iostream>
constexpr size_t size = 1000000;
constexpr size_t repeat = 1000;
using my_clock = std::chrono::high_resolution_clock;
template<class T>
auto test(std::vector<T>& v) {
v.reserve(size);
for (size_t i = 0; i < size; i++) {
v.emplace_back(new int());
}
auto t0 = my_clock::now();
for (int i = 0; i < repeat; i++) {
auto back = std::move(v.back()); …Run Code Online (Sandbox Code Playgroud) 在编写项目时,我遇到了一个奇怪的问题.
这是我设法编写的用于重新创建问题的最小代码.我故意将一个实际的字符串存储在其他地方,并分配了足够的空间.
// #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h> // For offsetof()
typedef struct _pack{
// The type of `c` doesn't matter as long as it's inside of a struct.
int64_t c;
} pack;
int main(){
pack *p;
char str[9] = "aaaaaaaa"; // Input
size_t len = offsetof(pack, c) + (strlen(str) + 1);
p = malloc(len);
// Version 1: crash
strcpy((char*)&(p->c), str);
// Version 2: crash
strncpy((char*)&(p->c), str, strlen(str)+1);
// Version 3: works!
memcpy((char*)&(p->c), str, strlen(str)+1); …Run Code Online (Sandbox Code Playgroud) 我有以下 C/C++ 代码片段:
#define ARRAY_LENGTH 666
int g_sum = 0;
extern int *g_ptrArray[ ARRAY_LENGTH ];
void test()
{
unsigned int idx = 0;
// either enable or disable the check "idx < ARRAY_LENGTH" in the while loop
while( g_ptrArray[ idx ] != nullptr /* && idx < ARRAY_LENGTH */ )
{
g_sum += *g_ptrArray[ idx ];
++idx;
}
return;
}
Run Code Online (Sandbox Code Playgroud)
当我使用版本 12.2.0 中的 GCC 编译器编译上述代码时,并选择-Os两种情况:
g_ptrArray[ idx ] != nullptrg_ptrArray[ idx ] != …以C++编写的一个数值软件的以下关键循环基本上将两个对象的成员比较:
for(int j=n;--j>0;)
asd[j%16]=a.e<b.e;
Run Code Online (Sandbox Code Playgroud)
a和b是类ASD:
struct ASD {
float e;
...
};
Run Code Online (Sandbox Code Playgroud)
我正在调查将此比较放在轻量级成员函数中的效果:
bool test(const ASD& y)const {
return e<y.e;
}
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
for(int j=n;--j>0;)
asd[j%16]=a.test(b);
Run Code Online (Sandbox Code Playgroud)
编译器正在内联这个函数,但问题是,汇编代码会有所不同,导致运行时开销超过10%.我要问:
为什么编译器会推出不同的汇编代码?
为什么生产的组件更慢?
编辑: 第二个问题已通过实施@ KamyarSouri的建议(j%16)得到了回答.汇编代码现在看起来几乎相同(请参阅http://pastebin.com/diff.php?i=yqXedtPm).唯一的区别是第18,33,48行:
000646F9 movzx edx,dl
Run Code Online (Sandbox Code Playgroud)
此图表显示了我的代码的50个测试的FLOP/s(最多为缩放因子).

用于生成绘图的gnuplot脚本:http://pastebin.com/8amNqya7
编译器选项:
/ Zi/W3/WX-/MP/Ox/Ob2/Oi/Ot/Oy/GL/D"WIN32"/ D"NDEBUG"/ D"_CONSOLE"/ D"_UNICODE"/ D"UNICODE"/ Gm-/EHsc/MT/GS-/Gy/arch:SSE2/fp:exact/Zc:wchar_t/Zc:forScope/Gd/analyze-
链接器选项:/ INCREMENTAL:NO"kernel32.lib""user32.lib""gdi32.lib""winspool.lib""comdlg32.lib""advapi32.lib""shell32.lib""ole32.lib""oleaut32. lib""uuid.lib""odbc32.lib""odbccp32.lib"/ ALLOWISOLATION/MANIFESTUAC:"level ='asInvoker'uiAccess ='false'"/ SUBSYSTEM:CONSOLE/OPT:REF/OPT:ICF/LTCG/TLBID …
#include <cstdio>
#include <cstdlib>
struct Interface {
virtual void f() = 0;
};
struct Impl1: Interface {
void f() override {
std::puts("foo");
}
};
// or __attribute__ ((visibility ("hidden")))/anonymous namespace
static Interface* const ptr = new Impl1 ;
int main() {
ptr->f();
}
Run Code Online (Sandbox Code Playgroud)
使用g ++ - 7编译时-O3 -flto -fdevirtualize-at-ltrans -fipa-pta -fuse-linker-plugin,上述ptr->f()调用不能被虚拟化.
似乎没有外部库可以修改ptr.这是GCC优化器的缺陷,还是因为其他一些来源使得虚拟化在这种情况下不可用?
更新:似乎clang-7 with -flto -O3 -fwhole-program-vtables -fvisibility=hidden是唯一可以虚拟化此程序的编译器+标志(如2018/03).
考虑使用以下 float 循环,使用 -O3 -mavx2 -mfma 编译
for (auto i = 0; i < a.size(); ++i) {
a[i] = (b[i] > c[i]) ? (b[i] * c[i]) : 0;
}
Run Code Online (Sandbox Code Playgroud)
Clang 在矢量化方面做得非常出色。它使用 256 位 ymm 寄存器,并了解 vblendps/vandps 之间的差异,以获得尽可能最佳的性能。
.LBB0_7:
vcmpltps ymm2, ymm1, ymm0
vmulps ymm0, ymm0, ymm1
vandps ymm0, ymm2, ymm0
Run Code Online (Sandbox Code Playgroud)
然而,海湾合作委员会的情况要糟糕得多。由于某种原因,它并没有比 SSE 128 位向量更好(-mprefer-vector-width=256 不会改变任何东西)。
.L6:
vcomiss xmm0, xmm1
vmulss xmm0, xmm0, xmm1
vmovss DWORD PTR [rcx+rax*4], xmm0
Run Code Online (Sandbox Code Playgroud)
如果将其替换为普通数组(如指南中所示),gcc 会将其矢量化为 AVX ymm。
int a[256], b[256], c[256];
auto foo …Run Code Online (Sandbox Code Playgroud)