免责声明:请不要试图在 正式比赛 中使用这里提到的 任何技巧。笔者为此不负任何责任。

1. 在标准环境下使用 c++17 库函数

标准环境指 gcc9.3, -std=c++14

注意到 c++ 有个 experimental 库。

#include <experimental/numeric>

#define gcd std::experimental::fundamentals_v2::gcd

然后我们就有 c++ 自带的 binary gcd 了!

同理:

#include <experimental/memory_resource>
#include <experimental/list>

template <typename T>
using pmr_list = std::experimental::fundamentals_v2::list<T>;

这样我们有了一个可以外接内存池的 STL 链表。

2. 编译器内置指令集

using vec = __attribute__((vector_size(32))) int;

这行代码定义了一个由 32 个 byte 打包成的 int 类型向量,其加、减等运算与 __m256i 完全等价,只不过不需要背冗长的指令集函数了!

如果真的需要,可以采用一下技巧将其零成本转为 __m256i:

union my_m256 {
  vec i32;
  __m256i mm;
};

调用 __m256i 相关函数时,传入 foo.mm 即可。

3. 内存对齐

struct __attribute__((packed)) Foo {
  char a;
  int b;
  char c;
};
// sizeof(Foo) == 6

#pragma pack(1) 也能起到同样的效果。

4. 强制内联

编译器在大多数时候都能准确地判断出一个函数是否需要内联;但偶尔也会有犯错的时候,这时我们可以强制其内联某个函数。

我们当然知道可以采用宏定义,但是这太危险了。

__attribute__((always_inline)) inline int add(int a, int b) { return a + b; }

[[gnu::always_inline]] inline int minus(int a, int b) { return a - b; }

注意不是所有函数都能够强制内联。

5. 新鲜出炉的 __builtins

__builtin_{popcount,clz,ctz}g

用法:同原函数,但是参数传一个无符号整形,编译器自动选择应该生成多少位的指令,不用写 ll 等后缀了。

此外,clzg, ctzg 还可以传第二个参数,表示第一个参数为 $0$ 的时候应该返回多少。

gcc 14 开始支持。

__builtin_bitreverse

正在实现,详见 bugzilla

标签: none

添加新评论