找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3102

积分

0

好友

424

主题
发表于 2026-2-12 06:54:33 | 查看: 28| 回复: 0

在开发者的普遍印象中,Go 常常被视为在性能和开发效率之间取得平衡的语言,而 Rust 则被奉为性能的终极追求者,尤其在 AES 这类重度依赖 CPU 计算的加密场景下。本文将通过一次严谨的性能测试,挑战这一固有认知。我们将对比 Go 语言(使用标准库、自定义实现及汇编优化)与 Rust 语言在 AES-256-CBC 加密算法上的性能表现,并揭示在不同硬件架构下的结果差异。

一、测试环境与方法

为确保测试的客观性,我们在两个不同的硬件平台上进行了对比。

1.1 硬件与系统环境

AMD64 平台(x86_64):

  • 服务器搭载 Intel(R) Xeon(R) CPU E5-2609 @ 2.40GHz 处理器,支持 AES-NI 指令集。
  • 操作系统为 Linux,内核版本 5.14.0-803.38.1.el9_5.x86_64
  • 内存总计约 16GB。

ARM64 平台(Apple Silicon):

  • 个人电脑为 Apple M3 芯片的 MacBook Pro(14英寸,2023年11月)。
  • 内存为 16 GB。

1.2 语言环境

Go 环境:

  • 在 ARM64 平台上使用 go1.25.1 darwin/arm64
  • 在 AMD64 平台上使用相应版本的 Go 工具链。

Rust 环境:

  • 在 ARM64 平台上,cargo 版本为 1.93.0,活动的 rustc 编译器版本为 1.93.0,工具链为 stable-aarch64-apple-darwin
  • 在 AMD64 平台上,cargo 版本为 1.92.0,活动的 rustc 编译器版本为 1.92.0,工具链为 stable-x86_64-unknown-linux-gnu

1.3 测试数据与方法

测试前,我们生成了两个测试数据文件:

  1. 一个在 macOS 上,名为 aestest100000.data,大小为 103MB。
  2. 另一个在 Linux 服务器上,名为 aetest100w.data,大小为 1.1GB。

文件内容格式为:奇数行是待加密的明文数据(长度在 47 到 2048 字节之间随机),偶数行是固定的 32 字节加密密码。

测试程序的任务是读取文件,循环处理每一对“密码-明文”数据,进行 AES-256-CBC 加密,并统计总耗时与平均每次加密耗时。文件 IO 时间不计入性能统计。每个测试程序在各自平台上连续运行 5 次,取平均成绩作为最终结果。

二、核心代码实现

2.1 Rust 实现

Rust 端使用了 aescbc 等 crate 进行标准库加密。核心加密函数旨在复用缓冲区以减少内存分配。

加密函数 aes256_cbc_encrypt_reuse_buffer

// AES-256-CBC 加密
// 复用 buffer,返回密文长度
fn aes256_cbc_encrypt_reuse_buffer(
    password: &[u8],
    plaintext: &[u8],
    buffer: &mut Vec<u8>,
) -> usize {
    let key: &[u8; 32] = password[..32].try_into().unwrap();
    let iv: &[u8; 16] = password[..16].try_into().unwrap();

    buffer.clear();
    buffer.extend_from_slice(plaintext);

    let block_size: usize = 16;
    let pad_len = block_size - (buffer.len() % block_size);
    buffer.resize(buffer.len() + pad_len, pad_len as u8);

    let cipher: Encryptor<Aes256> = Encryptor::<Aes256>::new(key.into(), iv.into());
    cipher.encrypt_padded_mut::<Pkcs7>(buffer, plaintext.len()).unwrap();
    buffer.len()
}

性能测试与资源统计主函数:

fn main() -> io::Result<()> {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        std::process::exit(1);
    }
    let file_path: &? = &args[1];
    let pid: Pid = sysinfo::get_current_pid().unwrap();
    let mut sys: System = System::new_all();
    sys.refresh_processes();

    encrypt_file_stream_and_profile01(file_path).unwrap();

    sys.refresh_processes();
    if let Some(proc: &Process) = sys.process(pid) {
        println!("=== Process Resource Usage ===");
        println!("Memory : {} KB", proc.memory()/1024);
        println!("CPU : {:} %", proc.cpu_usage());
        println!("==============================");
    }
    Ok(())
}

2.2 Go 实现

Go 语言方面,我们准备了三种实现进行对比:

  1. 标准库实现:使用 crypto/aescrypto/cipher
  2. 自定义实现:优化内存布局,减少拷贝,直接操作指针。
  3. 汇编优化实现:调用手写的汇编函数,一次性处理多个数据块。

Go 标准库实现核心片段:

tmpTime01 = time.Now().UnixNano()
tmpBlock, err = aes.NewCipher(tmpReadData[pwdDataIdx[0]:pwdDataIdx[1]])
if err == nil {
    rawDataLen = rawDataIdx[1] - rawDataIdx[0]
    padding = AesSecBlockSize - rawDataLen%AesSecBlockSize
    padByte = byte(padding)
    tmpResultLen = rawDataLen + padding
    copy(tmpReadData[pwdDataIdx[0]+16:pwdDataIdx[1]], tmpReadData[pwdDataIdx[0]:pwdDataIdx[0]+16])
    for tmpA = rawDataIdx[1]; tmpA < (rawDataIdx[1] + padding); tmpA++ {
        tmpReadData[tmpA] = padByte
    }
    tmpBlockMode = cipher.NewCBCEncrypter(tmpBlock, tmpReadData[pwdDataIdx[0]+16:pwdDataIdx[1]])
    tmpBlockMode.CryptBlocks(resultBuf[:tmpPageSize], tmpReadData[rawDataIdx[0]:rawDataIdx[0]+tmpResultLen])
    totalEncryptedLen += tmpResultLen
}
tmpTime02 = time.Now().UnixNano()
totalTime += tmpTime02 - tmpTime01

Go 自定义实现核心函数 EncryptWithBlocks

// EncryptWithBlocks 数据CBC模式加密直接通过Block 支持原地加密
func EncryptWithBlocks(blockPtr, dstDataPtr, srcDataPtr uintptr, ivDataPtr uintptr, srcLen int) {
    var i int
    var tmpData [2]uint64
    var tmpDataPtr uintptr
    var srcPtr *[2]uint64
    var ivPtr *[2]uint64
    var oldIv [2]uint64
    oldIv = (*[2]uint64)(unsafe.Pointer(ivDataPtr))
    ivPtr = (*[2]uint64)(unsafe.Pointer(ivDataPtr))
    tmpDataPtr = uintptr(unsafe.Pointer(&tmpData[0]))
    for i = 0; i < srcLen; i += 16 {
        srcPtr = (*[2]uint64)(unsafe.Pointer(srcDataPtr))
        tmpData[0] = srcPtr[0] ^ ivPtr[0]
        tmpData[1] = srcPtr[1] ^ ivPtr[1]
        aes.NewEncryptSitk(blockPtr, dstDataPtr, tmpDataPtr)
        ivPtr = (*[2]uint64)(unsafe.Pointer(dstDataPtr))
        dstDataPtr += 16
        srcDataPtr += 16
    }
    (*[2]uint64)(unsafe.Pointer(ivDataPtr)) = oldIv
}

Go 汇编优化接口函数:

package main
import "unsafe"
// 声明汇编函数
//go:noescape
func encryptCBCAsm(xk *uint32, dst, src, iv *byte, blocks int)

/* EncryptWithBlocksOptimized 优化后的 CBC 加密
*/
func EncryptWithBlocksOptimized(xkPtr *uint32, dstDataPtr, srcDataPtr, ivDataPtr uintptr, srcLen int) int {
    if srcLen <= 0 || srcLen%16 != 0 {
        return 0
    }

    blocks := srcLen / 16

    // 直接调用汇编,一次性处理所有 Blocks
    encryptCBCAsm(
        xkPtr,
        (*byte)(unsafe.Pointer(dstDataPtr)),
        (*byte)(unsafe.Pointer(srcDataPtr)),
        (*byte)(unsafe.Pointer(ivDataPtr)),
        blocks,
    )
    return blocks * 16
}

Go 的测试主程序通过命令行参数选择不同的加密实现,并统计进程资源使用情况。

switch os.Args[1] {
case "1":
    fmt.Println("Test AES CBC With Std Crypt method!")
    _ = EncryptFileStream01(os.Args[2])
case "2":
    fmt.Println("Test AES CBC With Custom Crypt method!")
    _ = EncryptFileStream02(os.Args[2])
case "3":
    fmt.Println("Test AES CBC With Custom ASM method!")
    _ = EncryptFileStream03(os.Args[1])
}

三、性能测试结果分析

所有实现均通过结果验证,加密输出一致,确保性能对比的有效性。

3.1 ARM64 (Apple M3) 平台结果

在该平台下,不同实现的平均单次加密耗时对比如下:

  • Rust 标准库:17585.6 纳秒
  • Go 标准库:1246.8 纳秒
  • Go 自定义实现:1185.9 纳秒
  • Go 汇编优化:857.6 纳秒

分析:结果令人震惊。Rust 在此平台上的表现异常缓慢,耗时远超 Go 的任何一种实现。这通常意味着 Rust 所使用的加密库在 ARM64 架构下可能未能有效利用硬件加速指令(如 ARMv8 的加密扩展),或者存在较大的抽象开销。相比之下,Go 的表现则正常得多,其标准库已具备良好的性能,而经过汇编优化后,性能进一步提升,达到全场最佳的 857.6 纳秒,比 Rust 快了超过 20 倍

3.2 AMD64 (Intel Xeon) 平台结果

在 x86_64 架构下,测试结果呈现不同的态势:

  • Rust 标准库:3861.6 纳秒
  • Go 标准库:4415.1 纳秒
  • Go 自定义实现:约 3600 纳秒(根据上下文估算)
  • Go 汇编优化:3456.0 纳秒

分析:在 Rust 的传统优势战场 x86 平台上,其标准库确实展现了强大的性能,以 3861.6 ns 的成绩领先于 Go 标准库的 4415.1 ns,领先幅度约 14%。然而,当 Go 启用自定义的汇编优化后,情况发生逆转。汇编优化版本将耗时压低至 3456.0 ns,成功反超 Rust 标准库实现,性能领先约 10.5%。这证明了在成熟的 x86 平台上,通过精细化的底层优化,Go 完全有能力挑战甚至超越以性能著称的 Rust。

四、结论与启示

本次测试带来了几个颠覆性的发现:

  1. 平台依赖性极强:加密性能严重依赖于硬件架构和底层库的实现。Rust 在 AMD64 上的优秀表现并未延续到 ARM64,而 Go 在两个平台上都表现出了更一致的性能基线。
  2. 汇编优化的威力:无论是 ARM64 还是 AMD64,手写汇编优化都带来了显著的性能提升。在 AMD64 上,它帮助 Go 实现了对 Rust 的 10.5% 反超;在 ARM64 上,则将性能推至极致。这揭示了在追求极限性能的场景下,深入底层的重要性。
  3. 破除“语言决定论”:测试结果有力地表明,“没有慢的语言,只有未充分优化的实现”。Go 通过合理的后端架构与底层优化,在特定的加密任务上可以超越 Rust。性能的差异更多源于编译器优化、标准库实现以及最终生成的机器代码质量,而非语言本身的绝对优劣。

对于高吞吐量、低延迟的加密应用开发者而言,选择技术栈时需紧密结合目标部署平台进行性能评估。如果目标环境是 ARM 服务器,Go 可能是更安全、性能更可预测的选择。如果在 x86 环境且团队具备底层优化能力,Go 同样可以通过优化达到顶尖水平。本次测试也说明,深入的技术探索和社区交流,例如在 云栈社区 这样的平台上分享与学习,是推动技术选型和实践进步的关键。

参考资料

[1] 谁说Golang性能不如Rust?在 AES CBC加密上,Go反超Rust 10%!, 微信公众号:mp.weixin.qq.com/s/I2x-PkRL98PCdEqp_38jhw

版权声明:本文由 云栈社区 整理发布,版权归原作者所有。




上一篇:MiM-StocR框架详解:基于动量线与自适应排名损失的多任务股票推荐模型
下一篇:仅用4000行代码实现OpenClaw核心功能?轻量级AI助手框架nanobot深度解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 15:27 , Processed in 0.487450 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表