当前位置: 首页 > news >正文

C++编程:利用ARM硬件加速CRC32计算

文章目录

    • 引言
    • 软件实现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;elseulCRC >>= 1;}return ulCRC;}// 计算整个数据块的CRC32uint32_t compute(const uint8_t* data, size_t len, uint32_t crc = 0) const {// 初始化CRC32为0xFFFFFFFFcrc = 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 ^ 0xFFFFFFFFcrc = crc32_end(crc);return crc;}// 初始化CRC32inline uint32_t crc32_start(void) const {return 0xFFFFFFFFU; // 标准CRC32初始化值}// 结束CRC32inline uint32_t crc32_end(uint32_t crc) const {return crc ^ 0xFFFFFFFFU; // 标准CRC32最终异或}
};

代码解析

  1. calcCRC32Value:计算单个字节的CRC32值,逐位处理,不使用查找表。
  2. compute:计算整个数据块的CRC32。流程如下:
    • 初始化CRC32为0xFFFFFFFF
    • 对每个字节执行CRC32计算:
      • 计算ulCrcDark:将当前CRC右移8位,并清除高24位。
      • 计算ulCrcWhite:将当前CRC的低8位与字节进行XOR操作,然后调用calcCRC32Value
      • 更新CRC:ulCrcDark ^ ulCrcWhite
    • 最终CRC32值为crc ^ 0xFFFFFFFF

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:// 初始化CRC32inline uint32_t crc32_start(void) const {return 0xFFFFFFFFU; // 标准CRC32初始化值}// 结束CRC32inline 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); // 使用__crc32ddata += 8;}// 处理4字节数据while (data + 4 <= end) {uint32_t val;memcpy(&val, data, sizeof(uint32_t));crc = __crc32w(crc, val); // 使用__crc32wdata += 4;}// 处理2字节数据while (data + 2 <= end) {uint16_t val;memcpy(&val, data, sizeof(uint16_t));crc = __crc32h(crc, val); // 使用__crc32hdata += 2;}// 处理1字节数据while (data < end) {uint8_t val = *data;crc = __crc32b(crc, val); // 使用__crc32bdata += 1;}return crc;}
};

代码解析

  1. crc32_start:初始化CRC32为0xFFFFFFFF
  2. crc32_end:最终CRC32值为crc ^ 0xFFFFFFFF
  3. crc32_do:使用ARM的内置函数进行CRC32计算。按8字节、4字节、2字节和1字节的顺序处理数据,以提高性能。

性能压测

为了比较软件实现和硬件加速实现的CRC32计算性能,我们编写了一个性能压测程序。该程序包括:

  1. 数据生成:通过重复给定的字符串生成大规模测试数据。
  2. 一致性验证:确保软件和硬件实现的CRC32计算结果一致。
  3. 性能测量:使用高精度计时器(std::chrono)测量两种实现的执行时间。
  4. 结果报告:输出两种方法的执行时间和加速比。

完整的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;elseulCRC >>= 1;}return ulCRC;}// 计算整个数据块的 CRC32uint32_t compute(const uint8_t *data, size_t len, uint32_t crc = 0) const {// 初始化 CRC32 为 0xFFFFFFFFcrc = 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 ^ 0xFFFFFFFFcrc = crc32_end(crc);return crc;}// 初始化 CRC32inline uint32_t crc32_start(void) const {return 0xFFFFFFFFU; // 标准 CRC32 初始化值}// 结束 CRC32inline uint32_t crc32_end(uint32_t crc) const {return crc ^ 0xFFFFFFFFU; // 标准 CRC32 最终异或}
};// 硬件加速 CRC32 实现
class HardwareCRC32 {
public:// 初始化 CRC32inline uint32_t crc32_start(void) const {return 0xFFFFFFFFU; // 标准 CRC32 初始化值}// 结束 CRC32inline 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);             // 使用 __crc32ddata += 8;}// 处理4字节数据while (data + 4 <= end) {uint32_t val;memcpy(&val, data, sizeof(uint32_t));crc = __crc32w(crc, val); // 使用 __crc32wdata += 4;}// 处理2字节数据while (data + 2 <= end) {uint16_t val;memcpy(&val, data, sizeof(uint16_t));crc = __crc32h(crc, val); // 使用 __crc32hdata += 2;}// 处理1字节数据while (data < end) {uint8_t val = *data;crc = __crc32b(crc, val); // 使用 __crc32bdata += 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;// 准备软件 CRC32SoftwareCRC32 software_crc32;// 准备硬件加速 CRC32HardwareCRC32 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;
}

代码说明

  1. 软件CRC32实现

    • 采用逐字节、逐位计算的方法,不使用查找表。
    • calcCRC32Value函数计算单个字节的CRC32值。
    • compute函数计算整个数据块的CRC32值,遵循标准CRC32的初始化和最终异或步骤。
  2. 硬件加速CRC32实现

    • 利用ARMv8.1-A提供的内置函数__crc32d__crc32w__crc32h__crc32b分别处理8字节、4字节、2字节和1字节的数据。
    • 确保数据读取的安全性,通过memcpy避免未对齐访问。
  3. 性能压测流程

    • 数据生成:通过重复给定的字符串生成大规模测试数据(示例中为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 bytesVerifying 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 secondsStarting hardware-accelerated CRC32 benchmark...
Hardware CRC32 Result: 0x5c6092a8
Hardware CRC32 Time: 0.184211 secondsSpeedup: 109.612x faster using hardware-accelerated CRC32

结果分析

  1. 一致性验证

    • 软件和硬件加速实现的CRC32结果一致,确保两者的正确性。
  2. 性能压测

    • 软件实现:处理13,600,000字节的数据块1000次,共耗时约20.19秒。
    • 硬件加速实现:处理相同数据块1000次,仅耗时约0.184秒。
    • 加速比:硬件加速实现比软件实现快约110倍。

性能提升原因

ARM的CRC32指令集通过硬件级别的并行处理和优化,大幅提升了CRC32计算的效率。相比于逐位计算的软件实现,硬件加速实现能够利用处理器的专用指令,减少大量的循环和位操作,从而显著缩短计算时间。


http://www.mrgr.cn/news/48087.html

相关文章:

  • Redis篇--常见问题篇7--缓存一致性2(分布式事务框架Seata)
  • OpenAI 12天发布会:AI革命的里程碑@附35页PDF文件下载
  • 5、栈应用-表达式求值
  • (高可用版本)Kubeadm+Containerd+keepalived部署高可用k8s(v1.28.2)集群
  • 内网穿透ubuntu20 docker coplar
  • YOLO11检测物体的Gradio APP
  • vue基础语法的用法(API组合式风格)
  • maven
  • 小白萌新 JSAR 开发者工具之初体验——好用!
  • 《市场营销学》PPT课件.ppt
  • SAP S/4HANA 迁移:IT 高管实用指南
  • 探索未知,惊喜连连 —— 盲盒小程序开发文案
  • 【低代码】前端低代码开发日记2_遇到的问题(2)变量绑定
  • [单master节点k8s部署]34.ingress 反向代理(一)
  • 提升 Python 包下载速度的利器:cnpip
  • Datawhale 组队学习 文生图 Prompt攻防 task01
  • 解释Flask框架中的session和request对象
  • 模型预测控制工具包——ACADO:通过Cmake构建自己的优化问题
  • lnmp - RBAC方案设计与实现
  • 使用阿里云盘将服务器上的文件上传/下载到云盘/服务器
  • MySQL数据库详细学习要点
  • 纯干货!详解Java并发之线程中断机制
  • Docker理念
  • Android Studio 安装和认识
  • SpringBoot集成Redis
  • 牛上脑和各类牛排的叫法,不要土老帽了~