3.7.3 AVX指令
从Go1.11开始,Go汇编语言引入了AVX512指令的支持。AVX指令集是属于Intel家的SIMD指令集中的一部分。AVX512的最大特点是数据有512位宽度,可以一次计算8个64位数或者是等大小的数据。因此AVX指令可以用于优化矩阵或图像等并行度很高的算法。不过并不是每个X86体系的CPU都支持了AVX指令,因此首要的任务是如何判断CPU支持了哪些高级指令。
在Go语言标准库的internal/cpu
包提供了CPU是否支持某些高级指令的基本信息,但是只有标准库才能引用这个包(因为internal路径的限制)。该包底层是通过X86提供的CPUID指令来识别处理器的详细信息。最简便的方法是直接将internal/cpu
包克隆一份。不过这个包为了避免复杂的依赖没有使用init函数自动初始化,因此需要根据情况手工调整代码执行doinit函数初始化。
internal/cpu
包针对X86处理器提供了以下特性检测:
package cpu
var X86 x86
// The booleans in x86 contain the correspondingly named cpuid feature bit.
// HasAVX and HasAVX2 are only set if the OS does support XMM and YMM registers
// in addition to the cpuid feature bit being set.
// The struct is padded to avoid false sharing.
type x86 struct {
HasAES bool
HasADX bool
HasAVX bool
HasAVX2 bool
HasBMI1 bool
HasBMI2 bool
HasERMS bool
HasFMA bool
HasOSXSAVE bool
HasPCLMULQDQ bool
HasPOPCNT bool
HasSSE2 bool
HasSSE3 bool
HasSSSE3 bool
HasSSE41 bool
HasSSE42 bool
}
因此我们可以用以下的代码测试运行时的CPU是否支持AVX2指令集:
import (
cpu "path/to/cpu"
)
func main() {
if cpu.X86.HasAVX2 {
// support AVX2
}
}
AVX512是比较新的指令集,只有高端的CPU才会提供支持。为了主流的CPU也能运行代码测试,我们选择AVX2指令来构造例子。AVX2指令每次可以处理32字节的数据,可以用来提升数据复制的工作的效率。
下面的例子是用AVX2指令复制数据,每次复制数据32字节倍数大小的数据:
// func CopySlice_AVX2(dst, src []byte, len int)
TEXT ·CopySlice_AVX2(SB), NOSPLIT, $0
MOVQ dst_data+0(FP), DI
MOVQ src_data+24(FP), SI
MOVQ len+32(FP), BX
MOVQ $0, AX
LOOP:
VMOVDQU 0(SI)(AX*1), Y0
VMOVDQU Y0, 0(DI)(AX*1)
ADDQ $32, AX
CMPQ AX, BX
JL LOOP
RET
其中VMOVDQU指令先将0(SI)(AX*1)
地址开始的32字节数据复制到Y0寄存器中,然后再复制到0(DI)(AX*1)
对应的目标内存中。VMOVDQU指令操作的数据地址可以不用对齐。
AVX2共有16个Y寄存器,每个寄存器有256bit位。如果要复制的数据很多,可以多个寄存器同时复制,这样可以利用更高效的流水特性优化性能。
学员评价