#go #parallel-processing #vectorization #gccgo
# #Вперед #параллельная обработка #векторизация #gccgo
Вопрос:
Я опытный программист на C , привыкший к низкоуровневой оптимизации, и я пытаюсь добиться производительности от Go.
Пока меня интересуют GFlop / s.
Я написал следующий код go:
package main
import (
"fmt"
"time"
"runtime"
"sync"
)
func expm1(x float64) float64 {
return ((((((((((((((15.0 x) * x 210.0) * x 2730.0) * x 32760.0) * x 360360.0) * x 3603600.0) * x 32432400.0) * x 259459200.0) * x 1816214400.0) * x 10897286400.0) * x 54486432000.0) * x 217945728000.0) *
x 653837184000.0) * x 1307674368000.0) * x * 7.6471637318198164759011319857881e-13;
}
func twelve(x float64) float64 {
return expm1( expm1( expm1( expm1( expm1( expm1( expm1( expm1( expm1( expm1( expm1( expm1(x))))))))))));
}
func populate(data []float64, N int) {
CPUCOUNT := runtime.NumCPU();
var wg sync.WaitGroup
var slice = N / CPUCOUNT;
wg.Add(CPUCOUNT)
defer wg.Wait()
for i := 0; i < CPUCOUNT; i {
go func(ii int) {
for j := ii * slice; j < ii * slice slice; j = 1 {
data[j] = 0.1;
}
defer wg.Done();
}(i);
}
}
func apply(data []float64, N int) {
CPUCOUNT := runtime.NumCPU();
var wg sync.WaitGroup
var slice = N / CPUCOUNT;
wg.Add(CPUCOUNT)
defer wg.Wait()
for i := 0; i < CPUCOUNT; i {
go func(ii int) {
for j := ii * slice; j < ii * slice slice; j = 8 {
data[j] = twelve(data[j]);
data[j 1] = twelve(data[j 1]);
data[j 2] = twelve(data[j 2]);
data[j 3] = twelve(data[j 3]);
data[j 4] = twelve(data[j 4]);
data[j 5] = twelve(data[j 5]);
data[j 6] = twelve(data[j 6]);
data[j 7] = twelve(data[j 7]);
}
defer wg.Done();
}(i);
}
}
func Run(data []float64, N int) {
populate(data, N);
start:= time.Now();
apply(data, N);
stop:= time.Now();
elapsed:=stop.Sub(start);
seconds := float64(elapsed.Milliseconds()) / 1000.0;
Gflop := float64(N) * 12.0 * 15.0E-9;
fmt.Printf("%fn", Gflop / seconds);
}
func main() {
CPUCOUNT := runtime.NumCPU();
fmt.Printf("num procs : %dn", CPUCOUNT);
N := 1024*1024*32 * CPUCOUNT;
data:= make([]float64, N);
for i := 0; i < 100; i {
Run(data, N);
}
}
это попытка перевода из моего бенчмарка c , который дает 80% пиковых флопов.
Версия C выдает 95 GFlop / s, в то время как версия go выдает 6 GFlops / s (счетчик FMA равен 1).
Вот фрагмент сборки go (gccgo -O3 -mfma -mavx2):
vfmadd132sd %xmm1, %xmm15, %xmm0
.loc 1 12 50
vfmadd132sd %xmm1, %xmm14, %xmm0
.loc 1 12 64
vfmadd132sd %xmm1, %xmm13, %xmm0
.loc 1 12 79
vfmadd132sd %xmm1, %xmm12, %xmm0
.loc 1 12 95
vfmadd132sd %xmm1, %xmm11, %xmm0
.loc 1 12 112
vfmadd132sd %xmm1, %xmm10, %xmm0
И что я получаю из своего кода на c (g -fopenmp -mfma -mavx2 -O3):
vfmadd213pd .LC3(%rip), %ymm12, %ymm5
vfmadd213pd .LC3(%rip), %ymm11, %ymm4
vfmadd213pd .LC3(%rip), %ymm10, %ymm3
vfmadd213pd .LC3(%rip), %ymm9, %ymm2
vfmadd213pd .LC3(%rip), %ymm8, %ymm1
vfmadd213pd .LC3(%rip), %ymm15, %ymm0
vfmadd213pd .LC4(%rip), %ymm15, %ymm0
vfmadd213pd .LC4(%rip), %ymm14, %ymm7
vfmadd213pd .LC4(%rip), %ymm13, %ymm6
vfmadd213pd .LC4(%rip), %ymm12, %ymm5
vfmadd213pd .LC4(%rip), %ymm11, %ymm4
Поэтому у меня есть несколько вопросов, наиболее важным из которых является :
- Правильно ли я выражаю параллелизм?
а если нет, то как мне это сделать?
Для дополнительного повышения производительности мне нужно знать, что не так со следующими элементами :
- Почему я вижу только инструкции vfmadd132sd в сборке вместо vfmadd132pd?
- Как я могу правильно выровнять распределение памяти?
- Как я могу удалить отладочную информацию из сгенерированного исполняемого файла?
- Передаю ли я правильные параметры в gccgo?
- Использую ли я правильный компилятор?
Комментарии:
1. Обратите внимание, что программы обеспечивают параллелизм, а не параллелизм
2. Вы задали слишком много вопросов сразу. Можете ли вы сузить его до одного за раз?
3. @flimzy сузился до первого.
Ответ №1:
Правильно ли я выражаю параллелизм?
Нет. Возможно, вы уничтожаете кэш процессора. (Но это трудно сказать, не зная подробностей о вашей системе. Думаю, это не NUMA?). В любом случае, технически ваш код параллельный, а не параллельный.
Почему я вижу только инструкции vfmadd132sd в сборке вместо vfmadd132pd?
Потому что компилятор поместил его туда. Это вопрос компилятора или вопрос программирования?
Как я могу правильно выровнять распределение памяти?
Это зависит от вашего определения «правильно». Выравнивание полей структуры и среза не контролируется ad hoc, но вы можете изменить порядок полей структуры (которые вы вообще не использовали, поэтому я не знаю, о чем вы здесь спрашиваете).
Как я могу удалить отладочную информацию из сгенерированного исполняемого файла?
Обратитесь к документации gcc.
Передаю ли я правильные параметры в gccgo?
Я не знаю.
Использую ли я правильный компилятор?
Что делает компилятор «правильным»?