Go отлично справляется с циклами. Компилятор умеет автовекторизировать простые паттерны, и на современных процессорах с 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