PG 19 支持 CPU 硬件加速 checksum 计算
PG 19 支持 CPU 硬件加速 checksum 计算
本文档解读 John Naylor 提交的校验和硬件加速改进,包括 x86 平台的 AVX2 优化和 ARM 平台的 CRC32C 硬件加速支持。
相关 Commit
5e13b0f24039 | |
fbc57f2bc2ee | |
effaa464afd3 | __cpuidex and __get_cpuid_count separately |
f6bd9f0fe25a |
核心价值
数倍性能提升: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. 性能提升对比
使用实践
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 性能优化的重要改进。
关键要点:
AVX2:x86 平台使用 256 位寄存器,vs SSE2 的 128 位 ARM CRC32C:利用 ARM 的 CRC32 和 PMULL 扩展 自动选择:运行时检测最佳实现 性能提升:数倍于纯软件实现 对 DBA 透明:无需额外配置,自动生效
对 DBA 的实际影响:
启用校验和的 CPU 开销更小 与在线校验和启用功能配合更好 不影响现有的监控和管理方式