⚡️
SIMD-ускорение для слайсов в Go без unsafe и CGoGo отлично справляется с циклами. Компилятор умеет автовекторизировать простые паттерны, и на современных процессорах с AVX-512 стандартный
for range работает быстро. Но не для всех операций. Сравнения: min, max, count; компилятор векторизирует хуже, и на больших массивах это заметно. TurboSlice закрывает именно эту нишу.
turboslice — Go-библиотека, которая заменяет ручные циклы по слайсам на вызовы с SIMD-ускорением через 128-битные SSE-инструкции. Построена на пакете
simd/archsimd из Go 1.26. Работает на любой платформе, но ускорение даёт на AMD64. На ARM64 и остальных архитектурах автоматически откатывается на скалярную реализацию.
Без CGo, без ассемблерных файлов, без
unsafe в публичном API.
Что умеетАгрегации (
Sum,
Min,
Max,
MinMax), поиск (
Find,
Contains,
Count), поэлементная математика (
AddSlices,
MulSlices,
DotProduct), а также набор дженерик-утилит (
Map,
Filter,
Reduce,
Chunk,
Unique,
Flatten и другие).
Когда SIMD помогает, а когда нетАвторы честно описывают границы применимости. Реальный выигрыш от SSE получают
Min,
Max,
MinMax,
Count — от 2x до 2.6x на всех размерах. Для
Sum и
DotProduct[int32/float32] SIMD включается только на слайсах от 4K и 16K элементов соответственно. Ниже этих порогов накладные расходы на настройку SIMD съедают всю выгоду.
А вот
Find,
Contains,
DotProduct[float64] и
AddSlices[float64] остаются скалярными даже в SIMD-сборке. Компилятор Go на этих паттернах генерирует код лучше, чем ручной SSE. Для 64-битных целых умножений (
MulSlices[int64],
DotProduct[int64]) SSE/AVX2 просто не имеют нужной инструкции.
Нужен Go 1.26+. Для SIMD-ускорения собираем с флагом:
GOEXPERIMENT=simd go build ./...
Пример использования:
import "github.com/atul-007/turboslice"
data := []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
turboslice.Sum(data) // 55
turboslice.Find(data, 7) // 6
turboslice.Min(data) // 1
turboslice.Max(data) // 10
turboslice.Contains(data, 5) // true
Вместо типичного цикла:
total := 0
for _, v := range data {
total += v
}
Пишем одну строку:
total := turboslice.Sum(data)
Typed API для горячих путейЕсли тип известен заранее и нужна максимальная производительность, есть типизированные функции (
SumInt32,
MinFloat64,
CountInt32 и т.д.). Они инлайнятся без прохода через
interface{} или type switch. На скалярной сборке работают наравне с ручным циклом, на SIMD-сборке получают ускорение.
turboslice.SumInt32(data) // инлайнится напрямую
turboslice.MinFloat64(vals) // без диспатча через интерфейс
Пример из практикиОбработка сигналов:
signal := loadSensorData()
weights := precomputeWeights(len(signal))
weighted := turboslice.MulSlices(signal, weights)
energy := turboslice.DotProduct(signal, signal)
lo, hi := turboslice.MinMax(signal)
Аналитический пайплайн:
scores := fetchAllScores() // []int32, миллионы записей
total := turboslice.Sum(scores)
lo, hi := turboslice.MinMax(scores)
outliers := turboslice.Filter(scores, func(s int32) bool {
return s > 3*stddev
})
Что стоит учестьMin,
Max,
MinMax паникуют на пустых слайсах, как и
slices.Min/
Max из стандартной библиотеки.
AddSlices,
MulSlices,
DotProduct молча обрезают до минимальной длины. Переполнение целых чисел не проверяется. Обработка NaN различается между SIMD и скалярной реализацией для
Min/
Max.
TurboSlice — нишевый инструмент. Если вы работаете с числовыми слайсами от десятков тысяч элементов на AMD64 и ваш bottleneck — агрегации типа min/max/count, библиотека даёт ощутимый прирост без компромиссов в безопасности кода. Для мелких слайсов и операций, где компилятор уже справляется, она просто не мешает.
➡️
Репозиторий📍 Навигация:
Вакансии •
Задачи •
Собесы🐸
Библиотека Go-разработчика#GoToProduction