highway
highway (maintained fork)
Repository → · upstream: ajroetker/go-highway · Apache-2.0
go-simd/highway is a fork, not one of the go-asmgen packages. It is
not generated by go-asmgen, it does not carry the suite’s 100 % coverage gate,
and its numbers are upstream’s. It exists for one reason: consumers could not
build against upstream’s published releases.
import "github.com/go-simd/highway/hwy"go get github.com/go-simd/highway@latestWhy a fork
go-highway v0.0.12 requires its own hwy/goat tool module at v0.0.0, a
version that was never published, and points it at a local directory with
replace. A replace applies only to the module that declares it, never to
the modules that depend on it. So every consumer that ran go mod tidy or
go list -m all failed. On 2026-10-18, 48 fleet modules required go-highway,
all of them indirectly through go-images/jpeg2000. The fix was
proposed upstream (issue #73,
PR #74) and is not released
there yet.
What differs from upstream
| change | why |
|---|---|
module path github.com/ajroetker/go-highway → github.com/go-simd/highway | a fork usable by consumers needs its own path; a replace in our go.mod would reach nobody either |
hwy/goat published as hwy/goat/v0.0.2 and required at that version | the defect itself |
retract [v0.0.1, v0.0.12] | GitHub forks inherit tags, so the proxy had cached upstream’s broken releases under the new path |
golang.org/x/text v0.39.0 (v0.0.14) | GO-2026-5970, reachable from cmd/hwygen |
NOTICE in the repository records the origin and these changes. Generated
assembly keeps upstream’s file-system paths in its comments, because those
record where the files were generated and are not module paths.
Releases
| version | content |
|---|---|
| v0.0.13 | upstream main under the new path, goat published, old tags retracted |
| v0.0.14 | x/text security update |
Read before you measure: GOEXPERIMENT=simd
On arm64 highway calls hand-written NEON assembly, which needs no
experiment. On amd64 the AVX2 and AVX-512 kernels are built only with
GOEXPERIMENT=simd. Without it, as on every other architecture, highway
dispatches to its generic Vec[T], which emulates a vector with a
heap-allocated slice and allocates in every Load, Add, Mul, Sub and
ShiftRight. The float kernels’ fallback does not allocate; the integer
ones do.
Measured on GitHub ubuntu-latest (Go 1.27.2, n=10), the JPEG 2000 integer RCT
on one 4K frame took 402.8 ms through highway against 61.3 ms as a plain
loop, with 12.45 million allocations.
arm64 is not automatically better off. The NEON kernel itself took 6.3 ms on
that frame. But the caller first copied its planes into highway Images and
then copied them back, and the whole path took 29.9 ms against 14.1 ms for the
loop. go-images/jpeg2000 therefore uses the plain loop for the RCT on every
architecture (#32).
Before attributing a timing to SIMD, check which *_amd64.gen.go or
*_other.gen.go file your build compiles, and time the path the caller
actually runs, conversions included.
Retirement
When upstream releases the goat fix, consumers move back to
github.com/ajroetker/go-highway and this fork is archived.