文章目录
- 引言
- 软件实现CRC32
- 代码解析
- ARM硬件加速CRC32
- 代码解析
- 性能压测
- 完整的`crc32_benchmark.cpp`实现
- 代码说明
- 编译与运行
- 运行结果示例
- 结果分析
- 性能提升原因
引言
本文将介绍如何在ARM架构上通过硬件加速实现高性能的CRC32计算,并与传统的软件实现进行性能对比。
软件实现CRC32
传统的软件实现通常采用逐字节、逐位计算的方法,不使用查找表。这种方法虽然实现简单,但在处理大规模数据时效率较低。以下是基于逐位计算的CRC32实现示例:
#include <cstdint>
// 软件CRC32实现(逐字节、逐位计算,不使用查找表)
class SoftwareCRC32 {
public:
static constexpr uint32_t CRC32_POLYNOMIAL = 0xEDB88320L;
// 计算单个字节的CRC32值
uint32_t calcCRC32Value(int32_t f_data_r) const {
uint32_t ulCRC = f_data_r & 0xFF;
for (int i = 0; i < 8; ++i) {
if (ulCRC & 1)
ulCRC = (ulCRC >> 1) ^ CRC32_POLYNOMIAL;
else
ulCRC >>= 1;
}
return ulCRC;
}
// 计算整个数据块的CRC32
uint32_t compute(const uint8_t* data, size_t len, uint32_t crc = 0) const {
// 初始化CRC32为0xFFFFFFFF
crc = crc32_start();
for (size_t i = 0; i < len; ++i) {
uint8_t byte = data[i];
uint32_t ulCrcDark = (crc >> 8) & 0x00FFFFFFL;
uint32_t ulCrcWhite = calcCRC32Value(static_cast<int32_t>((crc ^ byte) & 0xFF));
crc = ulCrcDark ^ ulCrcWhite;
}
// 最终CRC32为crc ^ 0xFFFFFFFF
crc = crc32_end(crc);
return crc;
}
// 初始化CRC32
inline uint32_t crc32_start(void) const {
return 0xFFFFFFFFU; // 标准CRC32初始化值
}
// 结束CRC32
inline uint32_t crc32_end(uint32_t crc) const {
return crc ^ 0xFFFFFFFFU; // 标准CRC32最终异或
}
};
代码解析
calcCRC32Value
:计算单个字节的CRC32值,逐位处理,不使用查找表。compute
:计算整个数据块的CRC32。流程如下:- 初始化CRC32为
0xFFFFFFFF
。 - 对每个字节执行CRC32计算:
- 计算
ulCrcDark
:将当前CRC右移8位,并清除高24位。 - 计算
ulCrcWhite
:将当前CRC的低8位与字节进行XOR操作,然后调用calcCRC32Value
。 - 更新CRC:
ulCrcDark ^ ulCrcWhite
。
- 计算
- 最终CRC32值为
crc ^ 0xFFFFFFFF
。
- 初始化CRC32为
ARM硬件加速CRC32
现代ARM处理器(特别是ARMv8.1-A及以上版本)提供了专用的CRC32指令,可以显著提升CRC32计算的性能。这些指令包括:
__crc32b
:处理单字节数据。__crc32h
:处理双字节数据(16位)。__crc32w
:处理四字节数据(32位)。__crc32d
:处理八字节数据(64位)。
利用这些指令,我们可以实现一个高效的硬件加速CRC32计算类。
#include <cstdint>
#include <cstring>
#include <arm_acle.h> // ARM内置函数头文件
// 硬件加速CRC32实现
class HardwareCRC32 {
public:
// 初始化CRC32
inline uint32_t crc32_start(void) const {
return 0xFFFFFFFFU; // 标准CRC32初始化值
}
// 结束CRC32
inline uint32_t crc32_end(uint32_t crc) const {
return crc ^ 0xFFFFFFFFU; // 标准CRC32最终异或
}
// 使用ARM内置函数进行硬件加速的CRC32计算
inline uint32_t crc32_do(const void *const in_buf, uint32_t crc, const uint64_t in_buf_len) const {
const uint8_t* data = static_cast<const uint8_t*>(in_buf);
const uint8_t* end = data + in_buf_len;
// 处理8字节数据
while (data + 8 <= end) {
uint64_t val;
memcpy(&val, data, sizeof(uint64_t)); // 确保安全读取
crc = __crc32d(crc, val); // 使用__crc32d
data += 8;
}
// 处理4字节数据
while (data + 4 <= end) {
uint32_t val;
memcpy(&val, data, sizeof(uint32_t));
crc = __crc32w(crc, val); // 使用__crc32w
data += 4;
}
// 处理2字节数据
while (data + 2 <= end) {
uint16_t val;
memcpy(&val, data, sizeof(uint16_t));
crc = __crc32h(crc, val); // 使用__crc32h
data += 2;
}
// 处理1字节数据
while (data < end) {
uint8_t val = *data;
crc = __crc32b(crc, val); // 使用__crc32b
data += 1;
}
return crc;
}
};
代码解析
crc32_start
:初始化CRC32为0xFFFFFFFF
。crc32_end
:最终CRC32值为crc ^ 0xFFFFFFFF
。crc32_do
:使用ARM的内置函数进行CRC32计算。按8字节、4字节、2字节和1字节的顺序处理数据,以提高性能。
性能压测
为了比较软件实现和硬件加速实现的CRC32计算性能,我们编写了一个性能压测程序。该程序包括:
- 数据生成:通过重复给定的字符串生成大规模测试数据。
- 一致性验证:确保软件和硬件实现的CRC32计算结果一致。
- 性能测量:使用高精度计时器(
std::chrono
)测量两种实现的执行时间。 - 结果报告:输出两种方法的执行时间和加速比。
完整的crc32_benchmark.cpp
实现
// crc32_benchmark.cpp
// crc32_benchmark.cpp
#include <algorithm>
#include <arm_acle.h> // ARM 内置函数头文件
#include <chrono>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
// 软件 CRC32 实现(逐字节、逐位计算,不使用查找表)
class SoftwareCRC32 {
public:
static constexpr uint32_t CRC32_POLYNOMIAL = 0xEDB88320L;
// 计算单个字节的 CRC32 值
uint32_t calcCRC32Value(int32_t f_data_r) const {
uint32_t ulCRC = f_data_r & 0xFF;
for (int i = 0; i < 8; ++i) {
if (ulCRC & 1)
ulCRC = (ulCRC >> 1) ^ CRC32_POLYNOMIAL;
else
ulCRC >>= 1;
}
return ulCRC;
}
// 计算整个数据块的 CRC32
uint32_t compute(const uint8_t *data, size_t len, uint32_t crc = 0) const {
// 初始化 CRC32 为 0xFFFFFFFF
crc = crc32_start();
for (size_t i = 0; i < len; ++i) {
uint8_t byte = data[i];
uint32_t ulCrcDark = (crc >> 8) & 0x00FFFFFFL;
uint32_t ulCrcWhite =
calcCRC32Value(static_cast<int32_t>((crc ^ byte) & 0xFF));
crc = ulCrcDark ^ ulCrcWhite;
}
// 最终 CRC32 为 crc ^ 0xFFFFFFFF
crc = crc32_end(crc);
return crc;
}
// 初始化 CRC32
inline uint32_t crc32_start(void) const {
return 0xFFFFFFFFU; // 标准 CRC32 初始化值
}
// 结束 CRC32
inline uint32_t crc32_end(uint32_t crc) const {
return crc ^ 0xFFFFFFFFU; // 标准 CRC32 最终异或
}
};
// 硬件加速 CRC32 实现
class HardwareCRC32 {
public:
// 初始化 CRC32
inline uint32_t crc32_start(void) const {
return 0xFFFFFFFFU; // 标准 CRC32 初始化值
}
// 结束 CRC32
inline uint32_t crc32_end(uint32_t crc) const {
return crc ^ 0xFFFFFFFFU; // 标准 CRC32 最终异或
}
// 使用 ARM 内置函数进行硬件加速的 CRC32 计算
inline uint32_t crc32_do(const void *const in_buf, uint32_t crc,
const uint64_t in_buf_len) const {
const uint8_t *data = static_cast<const uint8_t *>(in_buf);
const uint8_t *end = data + in_buf_len;
// 处理8字节数据
while (data + 8 <= end) {
uint64_t val;
memcpy(&val, data, sizeof(uint64_t)); // 确保安全读取
crc = __crc32d(crc, val); // 使用 __crc32d
data += 8;
}
// 处理4字节数据
while (data + 4 <= end) {
uint32_t val;
memcpy(&val, data, sizeof(uint32_t));
crc = __crc32w(crc, val); // 使用 __crc32w
data += 4;
}
// 处理2字节数据
while (data + 2 <= end) {
uint16_t val;
memcpy(&val, data, sizeof(uint16_t));
crc = __crc32h(crc, val); // 使用 __crc32h
data += 2;
}
// 处理1字节数据
while (data < end) {
uint8_t val = *data;
crc = __crc32b(crc, val); // 使用 __crc32b
data += 1;
}
return crc;
}
};
// 生成测试数据
std::vector<uint8_t> generate_test_data(const std::string &base_str,
size_t repeat_times) {
std::vector<uint8_t> data;
data.reserve(base_str.size() * repeat_times);
for (size_t i = 0; i < repeat_times; ++i) {
data.insert(data.end(), base_str.begin(), base_str.end());
}
return data;
}
int main() {
using namespace std::chrono;
// 定义测试字符串
std::string test_str =
"#RAWIMUA,COM4,0,100.0,UNKNOWN,1356,828.466,00000000,0000,782;"
"1356,828.465612000,12800000,1309784,-55720,32705,-1006,-10274,6618*"
"b290f1e0";
// 定义重复次数以生成较大的数据块
size_t repeat_times = 10; // 根据需要调整
// 生成测试数据
std::vector<uint8_t> test_data = generate_test_data(test_str, repeat_times);
size_t data_size = test_data.size();
std::cout << "Generated test data size: " << data_size << " bytes"
<< std::endl;
// 准备软件 CRC32
SoftwareCRC32 software_crc32;
// 准备硬件加速 CRC32
HardwareCRC32 hardware_crc32;
// 定义迭代次数
size_t iterations = 1000000; // 根据需要调整
// 验证 CRC32 计算的一致性(小数据块)
std::cout << "\nVerifying CRC32 consistency with small data block..."
<< std::endl;
std::vector<uint8_t> small_test_data = generate_test_data(test_str, 1);
uint32_t sw_crc_small =
software_crc32.compute(small_test_data.data(), small_test_data.size());
uint32_t hw_crc_small = hardware_crc32.crc32_do(small_test_data.data(),
hardware_crc32.crc32_start(),
small_test_data.size());
hw_crc_small = hardware_crc32.crc32_end(hw_crc_small);
std::cout << "Software CRC32 (small data): 0x" << std::hex << sw_crc_small
<< std::dec << std::endl;
std::cout << "Hardware CRC32 (small data): 0x" << std::hex << hw_crc_small
<< std::dec << std::endl;
if (sw_crc_small == hw_crc_small) {
std::cout << "Verification Passed: CRC32 results match." << std::endl;
} else {
std::cout << "Verification Failed: CRC32 results do not match."
<< std::endl;
return 1; // 终止程序
}
// 测试软件 CRC32 性能
std::cout << "\nStarting software CRC32 benchmark..." << std::endl;
auto start_sw = high_resolution_clock::now();
uint32_t sw_crc_result = 0;
for (size_t i = 0; i < iterations; ++i) {
sw_crc_result = software_crc32.compute(test_data.data(), test_data.size(),
sw_crc_result);
}
// 结束时间
auto end_sw = high_resolution_clock::now();
// 计算持续时间
duration<double> duration_sw = end_sw - start_sw;
// 输出结果
std::cout << "Software CRC32 Result: 0x" << std::hex << sw_crc_result
<< std::dec << std::endl;
std::cout << "Software CRC32 Time: " << duration_sw.count() << " seconds"
<< std::endl;
// 测试硬件加速 CRC32 性能
std::cout << "\nStarting hardware-accelerated CRC32 benchmark..."
<< std::endl;
auto start_hw = high_resolution_clock::now();
uint32_t hw_crc_result = 0;
for (size_t i = 0; i < iterations; ++i) {
hw_crc_result = hardware_crc32.crc32_do(
test_data.data(), hardware_crc32.crc32_start(), test_data.size());
hw_crc_result = hardware_crc32.crc32_end(hw_crc_result);
}
// 结束时间
auto end_hw = high_resolution_clock::now();
// 计算持续时间
duration<double> duration_hw = end_hw - start_hw;
// 输出结果
std::cout << "Hardware CRC32 Result: 0x" << std::hex << hw_crc_result
<< std::dec << std::endl;
std::cout << "Hardware CRC32 Time: " << duration_hw.count() << " seconds"
<< std::endl;
// 计算速度提升
double speedup = duration_sw.count() / duration_hw.count();
std::cout << "\nSpeedup: " << speedup
<< "x faster using hardware-accelerated CRC32" << std::endl;
return 0;
}
代码说明
-
软件CRC32实现:
- 采用逐字节、逐位计算的方法,不使用查找表。
calcCRC32Value
函数计算单个字节的CRC32值。compute
函数计算整个数据块的CRC32值,遵循标准CRC32的初始化和最终异或步骤。
-
硬件加速CRC32实现:
- 利用ARMv8.1-A提供的内置函数
__crc32d
、__crc32w
、__crc32h
和__crc32b
分别处理8字节、4字节、2字节和1字节的数据。 - 确保数据读取的安全性,通过
memcpy
避免未对齐访问。
- 利用ARMv8.1-A提供的内置函数
-
性能压测流程:
- 数据生成:通过重复给定的字符串生成大规模测试数据(示例中为13,600,000字节)。
- 一致性验证:使用较小的数据块(1360字节)验证软件和硬件实现的CRC32结果是否一致。
- 性能测量:
- 对软件和硬件实现分别进行1000次迭代的CRC32计算,记录总执行时间。
- 计算并输出两种方法的执行时间及加速比。
编译与运行
确保您的编译器支持ARMv8.1-A及其CRC32扩展,并启用相应的编译选项。例如,使用GCC或Clang时,可以使用以下编译命令:
aarch64-linux-gnu-g++ -march=armv8.1-a+crc -O3 -o crc32_benchmark crc32_benchmark.cpp
运行结果示例
以下是一个示例运行结果:
Generated test data size: 13600000 bytes
Verifying CRC32 consistency with small data block...
Software CRC32 (small data): 0xb290f1e0
Hardware CRC32 (small data): 0xb290f1e0
Verification Passed: CRC32 results match.
Starting software CRC32 benchmark...
Software CRC32 Result: 0x5c6092a8
Software CRC32 Time: 20.1918 seconds
Starting hardware-accelerated CRC32 benchmark...
Hardware CRC32 Result: 0x5c6092a8
Hardware CRC32 Time: 0.184211 seconds
Speedup: 109.612x faster using hardware-accelerated CRC32
结果分析
-
一致性验证:
- 软件和硬件加速实现的CRC32结果一致,确保两者的正确性。
-
性能压测:
- 软件实现:处理13,600,000字节的数据块1000次,共耗时约20.19秒。
- 硬件加速实现:处理相同数据块1000次,仅耗时约0.184秒。
- 加速比:硬件加速实现比软件实现快约110倍。
性能提升原因
ARM的CRC32指令集通过硬件级别的并行处理和优化,大幅提升了CRC32计算的效率。相比于逐位计算的软件实现,硬件加速实现能够利用处理器的专用指令,减少大量的循环和位操作,从而显著缩短计算时间。