我是Golang的新手,我正在对Golang和Cgo进行一些实验。我想看看Golang是否可以通过在简单测试上使用
SIMD
操作的C程序中受益,但是我看到的是,正常的通道比使用CGO从Golang调用的SIMD优化的C代码更快。我听说使用Golang的C处罚,但找不到任何详细信息。这是我的样本,所以任何建议都会有所帮助。
-Golang- CGO测试:包装cutils
/*
#cgo CFLAGS: -mavx2 -Wall -O0
#include <stdio.h>
#include <immintrin.h>
const char* foo(void) {
return __FILE__;
}
#define FLOATS_IN_AVX_REG 8
void simdAdd(float* out, float* a, float* b, unsigned long len) {
const unsigned long vectSize = (len / FLOATS_IN_AVX_REG) * FLOATS_IN_AVX_REG ;
unsigned long i=0;
for(i=0; i < vectSize; i += FLOATS_IN_AVX_REG) {
__m256 regA = _mm256_loadu_ps(a+i);
__m256 regB = _mm256_loadu_ps(b+i);
__m256 res = _mm256_add_ps(regA, regB);
_mm256_storeu_ps(out+i, res);
}
for(; i < len; i++) {
out[i] = a[i] + b[i];
}
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func NoSimdAdd(a, b []float32, l uint32) []float32 {
res := make([]float32, l)
for i := 0; i < len(a); i++ {
res[i] = a[i] + b[i]
}
return res
}
func SIMDadd(a, b []float32, l uint32) []float32 {
res := make([]float32, l)
C.simdAdd(
(*C.float)(unsafe.Pointer(&res[0])),
(*C.float)(unsafe.Pointer(&a[0])),
(*C.float)(unsafe.Pointer(&b[0])),
C.ulong(l))
return res
}
主要测试:
package main
import (
"ex1/files/cutils"
"fmt"
"math/rand/v2"
"time"
)
func genFArray(s uint32) []float32 {
r := make([]float32, s)
for i := range s {
r[i] = 1.0 * rand.Float32() * (10.0 - 1.0)
}
return r
}
const (
TEST_SIZE = 16
LOOP_CNT = 1000000
)
func main() {
a := genFArray(1 << TEST_SIZE)
start := time.Now()
for i := 0; i < LOOP_CNT; i++ {
cutils.SIMDadd(a, a, 1<<TEST_SIZE)
}
fmt.Printf("SIMD %.2fs elapsed\n", time.Since(start).Seconds())
}
在这里证明SIMD优化的原始C程序也在这里:
#include <stdio.h>
#include <immintrin.h>
#include <math.h>
#include <sys/time.h> // for gettimeofday()
#define FLOATS_IN_AVX_REG 8
#define ARR_SIZE (1 << 16)
#define LOOP_CNT 1000000
void plain_add(float* out, float* a, float* b, unsigned long len);
void simd_add(float* out, float* a, float* b, unsigned long len) ;
float func_Uniform(float) ;
int main(void) {
unsigned long i,j;
float arr1[ARR_SIZE];
float result[ARR_SIZE];
for(i=0; i < ARR_SIZE; i++) arr1[i] = func_Uniform(1.0);
printf("Start test\r\n");
struct timeval t1, t2;
double elapsedTime;
gettimeofday(&t1, NULL);
for(j=0; j < LOOP_CNT; j++)
simd_add(result, arr1, arr1, ARR_SIZE);
gettimeofday(&t2, NULL);
elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms
elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms
printf("Time elapsed %f ms.\n", elapsedTime);
return 0;
}
float func_Uniform(float a) {
return ((float)rand()/(float)(RAND_MAX)) * a ;
}
void plain_add(float* out, float* a, float* b, unsigned long len)
{
unsigned long i;
for(i=0; i < len; i++) out[i] = a[i] + b[i];
}
void simd_add(float* out, float* a, float* b, unsigned long len) {
const unsigned long vectSize = (len / FLOATS_IN_AVX_REG) * FLOATS_IN_AVX_REG ;
unsigned long i=0;
for(i=0; i < vectSize; i += FLOATS_IN_AVX_REG) {
__m256 regA = _mm256_loadu_ps(a+i);
__m256 regB = _mm256_loadu_ps(b+i);
__m256 res = _mm256_add_ps(regA, regB);
_mm256_storeu_ps(out+i, res);
}
for(; i < len; i++) {
out[i] = a[i] + b[i];
}
}
因此,使用SIMD对NO SIMD测试的C程序具有明显的差异:
no simd
Start test
Time elapsed 206796.292000 ms.
simd
Start test
Time elapsed 84095.521000 ms.
然而,Golang实验的差异各不相同,但SIMD较慢。
thanks.[编辑]:我已经更改了测试,看到了一些改进,而且还没有使用
go run .
void simdAddTest(float* out, float* a, float* b, unsigned long len, unsigned long loopcnt) {
unsigned long l ;
for(l=0; l < loopcnt; l++) {
simdAdd(out, a, b, len);
}
}
仅一次从Golang打电话:func SIMDAdd2(a, b []float32, l uint32, cnt uint32) []float32 {
res := make([]float32, l)
C.simdAddTest(
(*C.float)(unsafe.Pointer(&res[0])),
(*C.float)(unsafe.Pointer(&a[0])),
(*C.float)(unsafe.Pointer(&b[0])),
C.ulong(l),
C.ulong(cnt))
return res
}
现在结果更合理:
NO SIMD 59.71s elapsed
SIMD 41.10s elapsed
IT在SIMD和NON SIMD矢量添加之间的差异约为10-15秒,这是某种程度上的,但是我也不确定这是我可以从中挤出的最好的。
我在使用CGO从GO调用C函数时会看到额外的开销,这会产生性能问题。即使您在C.中使用SIMD,也可能会减慢速度。 trory通过CGO与GO的本机性能比较普通C函数(无SIMD)的性能,以查看高架CGO增加了多少。另外,请确保您使用的是-O3之类的编译器优化,并且对于Simd。
此外,您也可以尝试并行化工作或寻找直接使用SIMD的GO库,避免CGO,以防CGO开销仍然是问题。