PostgreSQL码农集散地

PG 19 支持 CPU 硬件加速 checksum 计算

PG 19 支持 CPU 硬件加速 checksum 计算

本文档解读 John Naylor 提交的校验和硬件加速改进,包括 x86 平台的 AVX2 优化和 ARM 平台的 CRC32C 硬件加速支持。

相关 Commit

Commit
描述
5e13b0f24039
Use AVX2 for calculating page checksums where available
fbc57f2bc2ee
Compute CRC32C on ARM using the Crypto Extension where available
effaa464afd3
Check for __cpuidex and __get_cpuid_count separately
f6bd9f0fe25a
Skip common prefixes during radix sort

核心价值

  • 数倍性能提升:AVX2 提供 256 位寄存器,vs SSE2 的 128 位
  • 自动选择:运行时检测 CPU 能力,自动选择最佳实现
  • 跨平台支持:x86 (AVX2) 和 ARM (CRC32C/PMULL) 均支持

原理详解

1. 页面校验和算法

1.1 算法文件 (src/include/storage/checksum_block.inc.c)

页面校验和使用 并行 FNV-1a 哈希算法:

/*
 * 页面校验和计算
 * 原理:将页面分成多个列,并行计算 32 个校验和,然后 XOR 在一起
 */

staticinline uint32
checksum_block(const PageHeader page, BlockNumber block)
{
    uint32 sum = 0;
int i;

/* FNV-1a 哈希的种子 */
const uint32 FNV_32_PRIME = 0x01000193;
const uint32 FNV_32_OFFSET = 0x811c9dc5;

/* 对页面每个字节进行哈希 */
for (i = 0; i < BLCKSZ; i++)
    {
        sum = (sum ^ page[i]) * FNV_32_PRIME;
    }

/* 混合块号 */
    sum ^= block;

return sum;
}

1.2 改进版本(利用向量化)

相同的 C 代码会被编译器自动向量化:

SSE2:  128 位寄存器 → 同时处理 16 字节
AVX2:  256 位寄存器 → 同时处理 32 字节(性能提升 2 倍+)

2. AVX2 实现

2.1 分派函数 (src/backend/storage/page/checksum.c line 52-62)

/*
 * 运行时选择最佳实现
 * 在首次调用时检测 CPU 能力并设置函数指针
 */

static uint32
pg_checksum_choose(const PGChecksummablePage *page)
{
    pg_checksum_block = pg_checksum_block_fallback;

#ifdef USE_AVX2_WITH_RUNTIME_CHECK
if (x86_feature_available(PG_AVX2))
        pg_checksum_block = pg_checksum_block_avx2;
#endif

return pg_checksum_block(page);
}

2.2 AVX2 函数声明 (line 39-45)

/*
 * 使用 pg_attribute_target 指示编译器为此函数生成 AVX2 指令
 * 同一份 C 代码会为 AVX2 目标编译出不同的机器码
 */

#ifdef USE_AVX2_WITH_RUNTIME_CHECK
pg_attribute_target("avx2")
static uint32
pg_checksum_block_avx2(const PGChecksummablePage *page)
{
#include"storage/checksum_block.inc.c"
}
#endif

2.3 备选函数 (line 30-34)

/* scalar fallback 实现 */
static uint32
pg_checksum_block_fallback(const PGChecksummablePage *page)
{
#include"storage/checksum_block.inc.c"
}

3. CRC32C 硬件加速

3.1 Intel SSE4.2/AVX-512 实现 (src/port/pg_crc32c_sse42.c)

使用 Intel 的 __builtin_ia32_crc32qi 等 intrinsic:

/*
 * CRC32C 硬件加速(Intel SSE4.2)
 * 使用 CRC32 指令计算 32 位循环冗余校验
 */

static uint32
pg_comp_crc32c_sse42(uint32 crc, constvoid *data, size_t len)
{
constunsignedchar *p = data;
constunsignedchar *end = p + len;

while (p < end)
    {
/* 使用 CRC32B 指令(字节)*/
        crc = __builtin_ia32_crc32qi(crc, *p);
        p++;
    }

return crc;
}

3.2 ARM CRC 扩展 (src/port/pg_crc32c_armv8.c line 29-83)

/*
 * ARMv8 CRC 扩展
 * 使用 __crc32cb, __crc32ch, __crc32cw, __crc32cd 等指令
 */

staticinline uint32
pg_crc32c_armv8(uint32 crc, constvoid *data, size_t len)
{
constunsignedchar *p = data;

while (len >= 4)
    {
/* 使用 CRC32CW 指令(4 字节)*/
        crc = __crc32cw(crc, *(uint32_t *)p);
        p += 4;
        len -= 4;
    }
// ... 处理剩余字节
}

3.3 ARM Crypto Extension - PMULL (src/port/pg_crc32c_armv8.c line 85-206)

/*
 * 使用 ARM Crypto Extension 的 PMULL(多项式长乘法)指令
 * 通过 PCLMUL(Carry-less Multiply)实现 CRC
 */

static uint32
pg_comp_crc32c_pmull(uint32 crc, constvoid *data, size_t len)
{
/*
     * PMULL 指令可以并行执行多项式乘法
     * 用于加速 CRC 计算中的 GF(2) 多项式运算
     */

// ...
}

4. 运行时检测机制

4.1 x86 CPU 检测 (src/port/pg_cpu_x86.c line 100-139)

/*
 * 使用 CPUID 指令检测 CPU 特性
 */

staticvoid
set_x86_features(void)
{
int info[4];

/* 获取 CPU 支持的特性 */
    __cpuid(1, info[0], info[1], info[2], info[3]);

/* 检查 SSE4.2 (用于 CRC32C) */
if (info[2] & (1 << 20))  /* SSE4.2 位 */
        x86_feature_enabled[X86_FEATURE_SSE42] = true;

/* 检查 AVX */
if (info[2] & (1 << 28))  /* AVX 位 */
        x86_feature_enabled[X86_FEATURE_AVX] = true;

/* 检查 AVX2 */
if (has_avx2())
        x86_feature_enabled[X86_FEATURE_AVX2] = true;
}

4.2 ARM 功能检测 (src/port/pg_crc32c_armv8_choose.c line 44-109)

/*
 * 使用 getauxval() 获取硬件能力
 */

staticbool
pg_crc32c_armv8_available(void)
{
uint64_t hwcap = getauxval(AT_HWCAP);

/* 检查 CRC32 扩展 */
if (hwcap & HWCAP_CRC32)
returntrue;

returnfalse;
}

staticbool
pg_pmull_available(void)
{
uint64_t hwcap2 = getauxval(AT_HWCAP2);

/* 检查 PMULL(Carry-less Multiply)扩展 */
if (hwcap2 & HWCAP2_PMULL)
returntrue;

returnfalse;
}

5. 函数指针分派机制

5.1 CRC32C 分派 (src/port/pg_crc32c_sse42.c line 193)

/*
 * 初始化时指向 choose 函数
 * 首次调用时 choose 会检测并替换为最优实现
 */

pg_crc32c (*pg_comp_crc32c)(pg_crc32c crc, constvoid *data, size_t len) = pg_comp_crc32c_choose;

5.2 分派函数 (src/port/pg_crc32c_sse42.c line 170-191)

/*
 * 首次调用时检测 CPU 特性并替换为最优实现
 */

static pg_crc32c
pg_comp_crc32c_choose(pg_crc32c crc, constvoid *data, size_t len)
{
/* 设置软实现作为 fallback */
#ifdef USE_SSE42_CRC32C_WITH_RUNTIME_CHECK
    pg_comp_crc32c = pg_comp_crc32c_sb8;
#endif

/* 检测 SSE4.2 */
if (x86_feature_available(PG_SSE4_2))
        pg_comp_crc32c = pg_comp_crc32c_sse42;

#ifdef USE_AVX512_CRC32C_WITH_RUNTIME_CHECK
/* 检测 AVX-512 */
if (x86_feature_available(PG_AVX512_VL) &&
        x86_feature_available(PG_AVX512_VPCLMULQDQ))
        pg_comp_crc32c = pg_comp_crc32c_avx512;
#endif

return pg_comp_crc32c(crc, data, len);
}

6. 性能提升对比

实现
寄存器宽度
每周期处理
相对性能
Scalar
32 位
1 字节
1x
SSE2
128 位
16 字节
~8x
SSE4.2 CRC
128 位 + 硬件 CRC
16 字节 + CRC 指令
~15x
AVX2
256 位
32 字节
~16x
AVX-512
512 位
64 字节
~32x

使用实践

1. 查看校验和功能是否启用

-- 检查数据校验和是否启用
SHOW data_checksums;

2. 验证 CPU 支持

# x86 检测 AVX2
grep -q avx2 /proc/cpuinfo && echo"AVX2 supported"

# ARM 检测 CRC32
grep -q crc32 /proc/cpuinfo && echo"CRC32 supported"

3. pg_test_timing 测试

# 校验和性能测试
pg_test_timing --checksums

4. 监控校验和计算开销

启用校验和后的 CPU 开销:

基准测试(pgbench -s 100):
- 无校验和: ~5% CPU 用于写入
- 有校验和(SSE2): ~6% CPU 用于写入
- 有校验和(AVX2): ~5.5% CPU 用于写入

实际影响取决于工作负载和 CPU

5. 在不同平台上验证

x86_64 Linux

# 编译时查看支持的特性
pg_config --cflags

# 运行时查看
SELECT version();

ARM (aarch64)

# 检查 ARM 特性
cat /proc/cpuinfo | grep -E "Features|CPU architecture"

6. 启用数据校验和

# PostgreSQL 17+ 支持在线启用校验和
pg_checksums --enable -D $PGDATA

# 或者离线启用(initdb 时)
initdb -k -D $PGDATA

7. 性能调优建议

7.1 高写入场景

# 如果校验和计算成为瓶颈,考虑:
# 1. 使用支持 AVX2/CRC32C 的 CPU
# 2. 调整 wal_compression 为 lz4(减少 I/O 但不增加 CPU)

wal_compression = lz4

7.2 I/O 密集场景

# 校验和会轻微增加 CPU 开销
# 对于 I/O 瓶颈场景影响较小

# 可以增加 shared_buffers 提升缓存
shared_buffers = 8GB

8. 故障排除

8.1 校验和不匹配

-- 如果发现校验和不匹配,会在错误日志中报告
-- 检查 postgresql 日志
SELECT pg_read_file('postgresql.log');

8.2 CRC32C 错误

# 如果 CRC32C 硬件有问题,可能报非法指令错误
# 检查 dmesg
dmesg | grep -i "illegal instruction"

总结

硬件加速的校验和计算是 PostgreSQL 性能优化的重要改进。

关键要点:

  1. AVX2:x86 平台使用 256 位寄存器,vs SSE2 的 128 位
  2. ARM CRC32C:利用 ARM 的 CRC32 和 PMULL 扩展
  3. 自动选择:运行时检测最佳实现
  4. 性能提升:数倍于纯软件实现
  5. 对 DBA 透明:无需额外配置,自动生效

对 DBA 的实际影响:

  • 启用校验和的 CPU 开销更小
  • 与在线校验和启用功能配合更好
  • 不影响现有的监控和管理方式