You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用intrinsics优化SSH AES-CTR中XOR运算性能的技术咨询

问题描述

我有一段代码,会遍历两个缓冲区,将二者做XOR运算后的结果存入第三个缓冲区,具体来说,两个输入分别是数据缓冲区和密钥流缓冲区,第三个为目标缓冲区。
涉事的完整函数如下:

static int
ssh_aes_ctr(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src,
    LIBCRYPTO_EVP_INL_TYPE len)
{
        typedef union {
#ifdef CIPHER_INT128_OK
                __uint128_t *u128;
#endif
                uint64_t *u64;
                uint32_t *u32;
                uint8_t *u8;
                const uint8_t *cu8;
                uintptr_t u;
        } ptrs_t;
        ptrs_t destp, srcp, bufp;
        uintptr_t align;
        struct ssh_aes_ctr_ctx_mt *c;
        struct kq *q, *oldq;
        int ridx;
        u_char *buf;

        if (len == 0)
                return 1;
        if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL)
                return 0;

        q = &c->q[c->qidx];
        ridx = c->ridx;

        /* src already padded to block multiple */
        srcp.cu8 = src;
        destp.u8 = dest;
        do { /* do until len is 0 */
                buf = q->keys[ridx];
                bufp.u8 = buf;

                /* figure out the alignment on the fly */
#ifdef CIPHER_UNALIGNED_OK
                align = 0;
#else
                align = destp.u | srcp.u | bufp.u;
#endif

                /* xor the src against the key (buf)
                 * different systems can do all 16 bytes at once or
                 * may need to do it in 8 or 4 bytes chunks
                 * worst case is doing it as a loop */
#ifdef CIPHER_INT128_OK
                if ((align & 0xf) == 0) {
                        destp.u128[0] = srcp.u128[0] ^ bufp.u128[0];
                } else
#endif
                /* 64 bits */
                if ((align & 0x7) == 0) {
                        destp.u64[0] = srcp.u64[0] ^ bufp.u64[0];
                        destp.u64[1] = srcp.u64[1] ^ bufp.u64[1];
                /* 32 bits */
                } else if ((align & 0x3) == 0) {
                        destp.u32[0] = srcp.u32[0] ^ bufp.u32[0];
                        destp.u32[1] = srcp.u32[1] ^ bufp.u32[1];
                        destp.u32[2] = srcp.u32[2] ^ bufp.u32[2];
                        destp.u32[3] = srcp.u32[3] ^ bufp.u32[3];
                } else {
                        /*1 byte at a time*/
                        size_t i;
                        for (i = 0; i < AES_BLOCK_SIZE; ++i)
                                dest[i] = src[i] ^ buf[i];
                }

                /* inc/decrement the pointers by the block size (16)*/
                destp.u += AES_BLOCK_SIZE;
                srcp.u += AES_BLOCK_SIZE;

                /* Increment read index, switch queues on rollover */
                if ((ridx = (ridx + 1) % KQLEN) == 0) {
                        oldq = q;
                        /* Mark next queue draining, may need to wait */
                        c->qidx = (c->qidx + 1) % numkq;
                        q = &c->q[c->qidx];
                        pthread_mutex_lock(&q->lock);
                        while (q->qstate != KQFULL) {
                                pthread_cond_wait(&q->cond, &q->lock);
                        }
                        q->qstate = KQDRAINING;
                        pthread_cond_broadcast(&q->cond);
                        pthread_mutex_unlock(&q->lock);

                        /* Mark consumed queue empty and signal producers */
                        pthread_mutex_lock(&oldq->lock);
                        oldq->qstate = KQEMPTY;
                        pthread_cond_broadcast(&oldq->cond);
                        pthread_mutex_unlock(&oldq->lock);
                }
        } while (len -= AES_BLOCK_SIZE);
        c->ridx = ridx;
        return 1;
}

我使用vtune对代码进行性能分析后发现,destp.u128[0] = srcp.u128[0] ^ bufp.u128[0]; 这行代码占用了4%的CPU运行时间。
该行对应的汇编代码如下:

shl $0x4, %rax
addq (%rsp), %rax
movq (%rax), %rdx
xorq (%rbx), %rdx
movq 0x8(%rax), %rax
xorq 0x8(%rax), %rax
movq %rax, 0x8(%r12)

其中xorq (%rbx), %rdx指令就消耗了3.5%的CPU时间。我想知道能否通过对需要做XOR运算的数据做向量化处理,再使用intrinsics执行XOR操作来提升性能。我几乎没有使用intrinsics的实际经验,但愿意学习相关技术。我不确定现有代码是否已经接近预期的最优水平,希望能获得相关指导建议,谢谢。

性能优化指导建议

现有代码的性能瓶颈原因

你当前的__uint128_t写法理论上是128位运算,但编译器默认会将其拆分为两条64位的加载、异或、存储指令,没有利用CPU内置的SIMD向量运算单元,这是你看到两条xorq指令、且占比较高的核心原因。

可以通过SIMD intrinsics获得明确性能提升

XOR是非常适合向量化的运算,对于x86架构,你可以用基线支持的SSE2指令集完成128位向量异或,不需要依赖高端指令集扩展,优化步骤如下:

  1. 引入SSE2头文件:
#include <emmintrin.h>
  1. 修改ptrs_t联合体,增加SIMD指针类型:
typedef union {
#ifdef CIPHER_INT128_OK
    __uint128_t *u128;
#endif
    __m128i *u128_simd; // 新增SSE2 128位向量指针
    uint64_t *u64;
    uint32_t *u32;
    uint8_t *u8;
    const uint8_t *cu8;
    uintptr_t u;
} ptrs_t;
  1. 替换原__uint128_t分支的运算逻辑:
#ifdef CIPHER_INT128_OK
if ((align & 0xf) == 0) {
    // 对齐加载128位数据,单指令异或,对齐存储结果
    __m128i src_vec = _mm_load_si128(srcp.u128_simd);
    __m128i key_vec = _mm_load_si128(bufp.u128_simd);
    __m128i res_vec = _mm_xor_si128(src_vec, key_vec);
    _mm_store_si128(destp.u128_simd, res_vec);
} else
#endif

优化后,原来的6条核心运算指令会缩减为3条向量指令,指令吞吐量提升一倍。

更高阶的优化方向

如果你的运行环境支持AVX2、AVX512指令集,可以进一步扩展到256位、512位向量运算,一次处理2/4个16字节AES块,性能提升会更明显:

  • AVX2对应_mm256_load_si256、_mm256_xor_si256等API,一次处理32字节数据
  • AVX512对应_mm512_load_si512、_mm512_xor_si512等API,一次处理64字节数据
    如果需要兼容ARM架构,可以用NEON指令集的等价API实现同样的向量化逻辑,用编译宏做跨平台适配即可。

额外优化提示

你当前的循环一次仅处理1个16字节块,可以修改为一次处理4/8个块,减少循环分支的开销,同时提升CPU缓存的利用效率,进一步降低内存访问延迟带来的性能损耗。你观测到的XOR指令占比高,本质上大概率是内存访问瓶颈导致的,批量处理可以很好地缓解这个问题。


内容的提问来源于stack exchange,提问作者Chris Rapier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 03:15:03