Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

simd/archsimd 包详解

概述

simd/archsimd 是 Go 1.26 引入的实验性SIMD(单指令多数据)指令集支持包,提供对架构特定的底层硬件向量指令的访问。

核心功能

  • 提供向量类型(如 Int8x16Float32x4 等)
  • 提供向量运算操作(加法、乘法、比较等)
  • 支持 128 位、256 位、512 位向量
  • 直接映射到硬件指令(AVX2、AVX-512 等)

重要说明

  • ⚠️ 实验性特性:不受 Go 1 兼容性承诺保护
  • ⚠️ 需要 GOEXPERIMENT:必须设置 GOEXPERIMENT=simd 启用
  • ⚠️ 架构限制:目前仅支持 AMD64 架构
  • ⚠️ 非公开 API:不建议在公共 API 中暴露 SIMD 类型

包导入

import "simd/archsimd"

启用实验特性

# 编译时启用
GOEXPERIMENT=simd go build

# 运行时启用
GOEXPERIMENT=simd ./your-program

向量类型总览

浮点类型

类型元素类型元素数量总位数
Float32x4float324128
Float32x8float328256
Float32x16float3216512
Float64x2float642128
Float64x4float644256
Float64x8float648512

整数类型(有符号)

类型元素类型元素数量总位数
Int8x16/32/64int816/32/64128/256/512
Int16x8/16/32int168/16/32128/256/512
Int32x4/8/16int324/8/16128/256/512
Int64x2/4/8int642/4/8128/256/512

整数类型(无符号)

类型元素类型元素数量总位数
Uint8x16/32/64uint816/32/64128/256/512
Uint16x8/16/32uint168/16/32128/256/512
Uint32x4/8/16uint324/8/16128/256/512
Uint64x2/4/8uint642/4/8128/256/512

掩码类型

类型用途
Mask8x16/32/64int8/uint8 向量比较结果
Mask16x8/16/32int16/uint16 向量比较结果
Mask32x4/8/16int32/uint32/float32 向量比较结果
Mask64x2/4/8int64/uint64/float64 向量比较结果

基本使用

简单示例

package main

import (
    "fmt"
    "simd/archsimd"
)

func main() {
    // 创建向量
    va := archsimd.BroadcastFloat32x4(1.0)
    vb := archsimd.BroadcastFloat32x4(2.0)
    
    // 向量加法
    vsum := va.Add(vb)
    
    // 存储结果
    var result [4]float32
    vsum.Store(&result)
    
    fmt.Printf("结果:%v\n", result) // [3 3 3 3]
}

常用操作分类

1. 加载/存储操作

Load 系列函数

// 从数组加载
func LoadFloat32x4(y *[4]float32) Float32x4
func LoadInt8x16(y *[16]int8) Int8x16

// 从切片加载
func LoadFloat32x4Slice(s []float32) Float32x4
func LoadInt8x16Slice(s []int8) Int8x16

// 从切片的部分加载
func LoadFloat32x4SlicePart(s []float32) Float32x4

// 掩码加载
func LoadMaskedFloat32x4(y *[4]float32, mask Mask32x4) Float32x4

Store 系列方法

// 存储到数组
func (x Float32x4) Store(y *[4]float32)

// 存储到切片
func (x Float32x4) StoreSlice(s []float32)

// 存储切片的部分
func (x Float32x4) StoreSlicePart(s []float32)

// 掩码存储
func (x Float32x4) StoreMasked(y *[4]float32, mask Mask32x4)

2. 算术运算

// 加法
func (x Float32x4) Add(y Float32x4) Float32x4

// 减法
func (x Float32x4) Sub(y Float32x4) Float32x4

// 乘法
func (x Float32x4) Mul(y Float32x4) Float32x4

// 除法
func (x Float32x4) Div(y Float32x4) Float32x4

// 融合乘加 (FMA)
func (x Float32x4) MulAdd(y Float32x4, z Float32x4) Float32x4
// 计算:x * y + z

// 饱和加法(整数)
func (x Int8x16) AddSaturated(y Int8x16) Int8x16

3. 位运算

// 与
func (x Int8x16) And(y Int8x16) Int8x16

// 或
func (x Int8x16) Or(y Int8x16) Int8x16

// 异或
func (x Int8x16) Xor(y Int8x16) Int8x16

// 与非
func (x Int8x16) AndNot(y Int8x16) Int8x16

// 取反
func (x Int8x16) Not() Int8x16

4. 比较运算

// 等于
func (x Float32x4) Equal(y Float32x4) Mask32x4

// 不等于
func (x Float32x4) NotEqual(y Float32x4) Mask32x4

// 大于
func (x Float32x4) Greater(y Float32x4) Mask32x4

// 大于等于
func (x Float32x4) GreaterEqual(y Float32x4) Mask32x4

// 小于
func (x Float32x4) Less(y Float32x4) Mask32x4

// 小于等于
func (x Float32x4) LessEqual(y Float32x4) Mask32x4

5. 类型转换

// 浮点转整数
func (x Float32x4) ConvertToInt32() Int32x4
func (x Float32x4) ConvertToInt64() Int64x4

// 浮点转无符号整数
func (x Float32x4) ConvertToUint32() Uint32x4

// 整数转浮点
func (x Int32x4) ConvertToFloat32() Float32x4
func (x Int32x4) ConvertToFloat64() Float64x4

// 位模式重新解释
func (x Float32x4) AsInt32x4() Int32x4
func (x Float32x4) AsUint32x4() Uint32x4
func (x Int32x4) AsFloat32x4() Float32x4

6. 元素操作

// 获取元素
func (x Float32x4) GetElem(index uint8) float32

// 设置元素
func (x Float32x4) SetElem(index uint8, y float32) Float32x4

// 广播单个元素
func (x Float32x4) Broadcast1To4() Float32x4
func (x Float32x4) Broadcast1To8() Float32x8
func (x Float32x4) Broadcast1To16() Float32x16

7. 排列和置换

// 置换
func (x Float32x4) Permute(indices Uint32x4) Float32x4

// 连接置换
func (x Float32x4) ConcatPermute(y Float32x4, indices Uint32x4) Float32x4

// 从一对中选择
func (x Float32x4) SelectFromPair(a, b, c, d uint8, y Float32x4) Float32x4

// 交叉排列
func (x Int16x8) InterleaveHi(y Int16x8) Int16x8
func (x Int16x8) InterleaveLo(y Int16x8) Int16x8

8. 掩码操作

// 掩码选择
func (x Float32x4) Masked(mask Mask32x4) Float32x4

// 合并
func (x Float32x4) Merge(y Float32x4, mask Mask32x4) Float32x4

// 压缩
func (x Float32x4) Compress(mask Mask32x4) Float32x4

// 扩展
func (x Float32x4) Expand(mask Mask32x4) Float32x4

// 掩码转整数向量
func (from Mask32x4) ToInt32x4() (to Int32x4)

// 从位创建掩码
func Mask32x4FromBits(y uint8) Mask32x4

// 掩码转位
func (x Mask32x4) ToBits() uint8

典型示例

示例 1:向量加法

package main

import (
    "fmt"
    "simd/archsimd"
)

func vectorAdd(a, b []float32) []float32 {
    if len(a) != len(b) {
        panic("长度不匹配")
    }
    
    result := make([]float32, len(a))
    
    // 每次处理 4 个 float32
    for i := 0; i < len(a); i += 4 {
        if i+4 <= len(a) {
            va := archsimd.LoadFloat32x4Slice(a[i:])
            vb := archsimd.LoadFloat32x4Slice(b[i:])
            vsum := va.Add(vb)
            vsum.StoreSlice(result[i:])
        } else {
            // 处理剩余元素
            result[i] = a[i] + b[i]
        }
    }
    
    return result
}

func main() {
    a := []float32{1, 2, 3, 4, 5, 6, 7, 8}
    b := []float32{10, 20, 30, 40, 50, 60, 70, 80}
    
    sum := vectorAdd(a, b)
    fmt.Println(sum) // [11 22 33 44 55 66 77 88]
}

示例 2:向量乘法(点积)

package main

import (
    "fmt"
    "simd/archsimd"
)

func dotProduct(a, b []float32) float32 {
    if len(a) != len(b) {
        panic("长度不匹配")
    }
    
    var sum archsimd.Float32x4
    zeros := archsimd.BroadcastFloat32x4(0)
    
    // 每次处理 4 个元素
    for i := 0; i+4 <= len(a); i += 4 {
        va := archsimd.LoadFloat32x4Slice(a[i:])
        vb := archsimd.LoadFloat32x4Slice(b[i:])
        product := va.Mul(vb)
        sum = sum.Add(product)
    }
    
    // 水平求和
    result := [4]float32{}
    sum.Store(&result)
    
    var total float32
    for _, v := range result {
        total += v
    }
    
    // 处理剩余元素
    for i := len(a) - (len(a) % 4); i < len(a); i++ {
        total += a[i] * b[i]
    }
    
    return total
}

func main() {
    a := []float32{1, 2, 3, 4}
    b := []float32{5, 6, 7, 8}
    
    dot := dotProduct(a, b)
    fmt.Printf("点积:%v\n", dot) // 70
}

示例 3:数组缩放

package main

import (
    "simd/archsimd"
)

func scaleArray(data []float32, scale float32) {
    vScale := archsimd.BroadcastFloat32x4(scale)
    
    for i := 0; i+4 <= len(data); i += 4 {
        v := archsimd.LoadFloat32x4Slice(data[i:])
        v = v.Mul(vScale)
        v.StoreSlice(data[i:])
    }
    
    // 处理剩余元素
    for i := len(data) - (len(data) % 4); i < len(data); i++ {
        data[i] *= scale
    }
}

示例 4:向量比较和筛选

package main

import (
    "fmt"
    "simd/archsimd"
)

func filterGreaterThan(data []float32, threshold float32) []float32 {
    vThreshold := archsimd.BroadcastFloat32x4(threshold)
    result := make([]float32, 0, len(data))
    
    for i := 0; i+4 <= len(data); i += 4 {
        v := archsimd.LoadFloat32x4Slice(data[i:])
        mask := v.Greater(vThreshold)
        
        // 使用掩码压缩
        compressed := v.Compress(mask)
        
        // 存储符合条件的元素
        var arr [4]float32
        compressed.Store(&arr)
        
        count := mask.ToBits()
        for j := 0; j < 4; j++ {
            if (count >> uint(j)) & 1 != 0 {
                result = append(result, arr[j])
            }
        }
    }
    
    return result
}

func main() {
    data := []float32{1, 5, 3, 8, 2, 9, 4, 7}
    filtered := filterGreaterThan(data, 5)
    fmt.Println(filtered) // [8, 9, 7]
}

示例 5:矩阵乘法(简化版)

package main

import (
    "simd/archsimd"
)

func matrixMultiply4x4(a, b [16]float32) [16]float32 {
    var result [16]float32
    
    // 加载矩阵 A 的行
    row0 := archsimd.LoadFloat32x4(&a[0])
    row1 := archsimd.LoadFloat32x4(&a[4])
    row2 := archsimd.LoadFloat32x4(&a[8])
    row3 := archsimd.LoadFloat32x4(&a[12])
    
    // 计算结果矩阵的每一列
    for j := 0; j < 4; j++ {
        // 加载矩阵 B 的列
        col := archsimd.Float32x4{}
        col = col.SetElem(0, b[j*4+0])
        col = col.SetElem(1, b[j*4+1])
        col = col.SetElem(2, b[j*4+2])
        col = col.SetElem(3, b[j*4+3])
        
        // 计算点积
        r0 := row0.Mul(col)
        r1 := row1.Mul(col)
        r2 := row2.Mul(col)
        r3 := row3.Mul(col)
        
        // 存储结果
        result[j*4+0] = r0.GetElem(0) + r0.GetElem(1) + r0.GetElem(2) + r0.GetElem(3)
        result[j*4+1] = r1.GetElem(0) + r1.GetElem(1) + r1.GetElem(2) + r1.GetElem(3)
        result[j*4+2] = r2.GetElem(0) + r2.GetElem(1) + r2.GetElem(2) + r2.GetElem(3)
        result[j*4+3] = r3.GetElem(0) + r3.GetElem(1) + r3.GetElem(2) + r3.GetElem(3)
    }
    
    return result
}

示例 6:AES 加密辅助

package main

import (
    "simd/archsimd"
)

func aesEncryptRound(state archsimd.Uint8x16, roundKey archsimd.Uint32x4) archsimd.Uint8x16 {
    // AES 一轮加密
    return state.AESEncryptOneRound(roundKey)
}

func aesEncryptLastRound(state archsimd.Uint8x16, roundKey archsimd.Uint32x4) archsimd.Uint8x16 {
    // AES 最后一轮加密
    return state.AESEncryptLastRound(roundKey)
}

示例 7:伽罗瓦域乘法(用于 Reed-Solomon 编码)

package main

import (
    "simd/archsimd"
)

func galoisFieldMul(a, b archsimd.Uint8x16) archsimd.Uint8x16 {
    return a.GaloisFieldMul(b)
}

func galoisFieldAffineTransform(x archsimd.Uint8x16, y archsimd.Uint64x2, b uint8) archsimd.Uint8x16 {
    return x.GaloisFieldAffineTransform(y, b)
}

示例 8:SHA 哈希辅助

package main

import (
    "simd/archsimd"
)

func sha256TwoRounds(x, y, z archsimd.Uint32x4) archsimd.Uint32x4 {
    return x.SHA256TwoRounds(y, z)
}

func sha1FourRounds(x archsimd.Uint32x4, constant uint8, y archsimd.Uint32x4) archsimd.Uint32x4 {
    return x.SHA1FourRounds(constant, y)
}

CPU 特性检测

package main

import (
    "fmt"
    "simd/archsimd"
)

func main() {
    // 检查 CPU 特性(AMD64)
    if archsimd.X86.HasAVX2() {
        fmt.Println("支持 AVX2")
    }
    
    if archsimd.X86.HasAVX512() {
        fmt.Println("支持 AVX-512")
    }
    
    // 根据特性选择合适的向量类型
    if archsimd.X86.HasAVX512() {
        // 使用 512 位向量
        useAVX512()
    } else if archsimd.X86.HasAVX2() {
        // 使用 256 位向量
        useAVX2()
    } else {
        // 使用 128 位向量
        useSSE()
    }
}

func useAVX512() {
    // 使用 Float32x16 等 512 位类型
}

func useAVX2() {
    // 使用 Float32x8 等 256 位类型
}

func useSSE() {
    // 使用 Float32x4 等 128 位类型
}

最佳实践

1. 对齐内存访问

// ✅ 推荐:确保内存对齐
func processAligned(data []float32) {
    // 确保切片起始地址对齐到 32 字节(AVX2)或 64 字节(AVX-512)
    if uintptr(unsafe.Pointer(&data[0]))%32 != 0 {
        // 重新分配对齐的内存
    }
}

2. 批量处理数据

// ✅ 推荐:批量处理,减少循环开销
for i := 0; i+16 <= len(data); i += 16 {
    v := archsimd.LoadFloat32x16Slice(data[i:])
    // 处理 16 个元素
}

3. 避免频繁的类型转换

// ❌ 不推荐:频繁转换
for i := 0; i < n; i++ {
    v := archsimd.Float32x4{}
    v = v.AsInt32x4()
    v = v.AsFloat32x4()
}

// ✅ 推荐:减少转换
v := archsimd.Float32x4{}
// 直接使用 v 操作

4. 使用融合操作

// ✅ 推荐:使用 FMA
result := a.MulAdd(b, c) // a * b + c

// ❌ 不推荐:分开操作
result := a.Mul(b).Add(c)

5. 检查 CPU 特性

// ✅ 推荐:运行时检查
if archsimd.X86.HasAVX2() {
    // 使用 AVX2 指令
}

注意事项

限制

  1. 架构依赖

    • 仅支持 AMD64 架构
    • 不支持 ARM64、386 等其他架构
  2. 实验性质

    • API 可能在未来版本中变化
    • 不受 Go 1 兼容性承诺保护
  3. 性能考虑

    • 需要正确对齐内存才能获得最佳性能
    • 不当使用可能导致性能下降
  4. 可移植性

    • 代码不可移植到其他架构
    • 需要提供回退实现

使用建议

  1. 仅在性能关键路径使用

    • SIMD 编程复杂,仅在实际需要时使用
    • 先分析性能瓶颈
  2. 提供回退实现

    func process(data []float32) {
        if archsimd.X86.HasAVX2() {
            processAVX2(data)
        } else {
            processGeneric(data)
        }
    }
    
  3. 测试不同 CPU

    • 在支持不同指令集的 CPU 上测试
    • 确保回退实现正确
  4. 文档说明

    • 注明使用了 SIMD 优化
    • 说明要求的 CPU 特性

快速参考

常用类型速查

类型加载函数存储方法加法乘法
Float32x4LoadFloat32x4StoreAddMul
Float32x8LoadFloat32x8StoreAddMul
Int32x4LoadInt32x4StoreAddMul
Uint8x16LoadUint8x16StoreAdd-

编译命令

# 启用 SIMD 实验特性
GOEXPERIMENT=simd go build

# 运行测试
GOEXPERIMENT=simd go test

# 禁用 Green Tea GC(可选,用于性能对比)
GOEXPERIMENT=simd,nogreenteagc go build

性能提示

  1. 使用更大的向量类型

    • AVX-512 支持时使用 Float32x16 而非 Float32x4
  2. 减少内存访问

    • 尽可能在寄存器中保持数据
  3. 使用硬件特定指令

    • AES、SHA、伽罗瓦域等专用指令

总结

simd/archsimd 是 Go 1.26 引入的实验性 SIMD 支持包,提供底层硬件向量指令访问。

核心优势

  • ✅ 直接访问硬件 SIMD 指令
  • ✅ 支持 128/256/512 位向量
  • ✅ 丰富的向量运算操作
  • ✅ 专用的加密/哈希指令支持

重要限制

  • ⚠️ 仅支持 AMD64 架构
  • ⚠️ 需要 GOEXPERIMENT=simd
  • ⚠️ 实验性 API,可能变化
  • ⚠️ 代码不可移植

主要用途

  • 数值计算和科学计算
  • 图像/视频处理
  • 加密算法实现
  • 机器学习推理
  • 数据并行处理

使用建议

  1. 仅在性能关键路径使用
  2. 提供通用回退实现
  3. 运行时检查 CPU 特性
  4. 确保内存对齐
  5. 充分测试不同硬件平台