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

为何C++ SSE SIMD与Go汇编实现浮点数组求和结果不一致?

问题分析:C++ SSE与Go汇编求和结果不一致

问题背景

用C++结合SSE指令实现两个float数组的逐元素求和后累加,输出结果为33.32;但相同逻辑的Go汇编实现输出异常值1.1076422e+09,需排查差异原因。

通用伪代码逻辑:

a := [N]float{}
b := [N]float{}
sum := 0
for i < N do
    sum += a[i] + b[i]
endfor

Go汇编代码的核心错误

1. 数组指针步长计算错误

Go汇编中使用ADDQ $10, AX和ADDQ $10, CX移动数组指针,这是完全错误的:

  • 每个float32占4字节,一次处理4个元素需要移动4*4=16字节,而非10字节。
  • 错误步长会导致指针越界访问无关内存,读取垃圾数据,最终计算出异常大的数值。

2. 未处理非4倍数的剩余元素

当数组长度N=7时,循环每次处理4个元素,仅能覆盖前4个元素,剩余3个元素完全被忽略,直接导致求和结果缺失部分数据。

修正后的Go汇编代码

#include "textflag.h"

//func _sum(a []float32, b[]float32) float32
TEXT _sum(SB), NOSPLIT, $0-52
    MOVQ a_base+0(FP), AX
    MOVQ b_base+24(FP), CX
    MOVQ a_len+8(FP), DX

    VXORPS X0, X0, X0  // 初始化累加寄存器为0

loop:
    CMPQ DX, $4
    JB   handle_remainder  // 剩余元素不足4个,跳转到处理剩余逻辑
    VMOVUPS (AX), X1       // 加载a的4个元素
    VMOVUPS (CX), X2       // 加载b的4个元素
    VADDPS X2, X1, X1      // 计算a[i]+b[i]
    VADDPS X1, X0, X0      // 累加到总和寄存器

    ADDQ $16, AX           // 指针移动16字节(4个float32)
    ADDQ $16, CX
    SUBQ $4, DX
    JMP loop

handle_remainder:
    CMPQ DX, $0
    JE   back
    // 逐个处理剩余元素
    VMOVSS (AX), X1
    VMOVSS (CX), X2
    VADDSS X2, X1, X1
    VADDSS X1, X0, X0

    ADDQ $4, AX
    ADDQ $4, CX
    SUBQ $1, DX
    JMP handle_remainder

back:
    // 将XMM0中的低32位(总和)存入返回值
    MOVSS X0, ret+48(FP)
    RET

额外注意点

  • Go主文件中b数组的第二个元素值为1,而C++中是2,这也会导致结果差异,需保持测试数据一致。
  • C++原代码也存在越界加载问题(当长度非4倍数时,_mm_loadu_ps会读取数组外的内存),建议同样添加剩余元素处理逻辑,避免未定义行为。

内容的提问来源于stack exchange,提问作者ANTON SAVELIEV

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 00:44:58