Skip to content
JohanLindvallPublic

About

Three fast non-cryptographic hashes for Go, one package each: XXH3 (64/128-bit), XXH64 and rapidhash. Bit-identical to their reference C implementations. Generated SSE2/AVX2/AVX-512/NEON/SVE2 kernels, verified by simulation where the host cannot run them. No dependencies outside the standard library.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

haste

CI Go Reference Version License: MIT

Fast XXH3, XXH64 and rapidhash for Go. Three packages with reference-compatible results, automatic CPU dispatch, and no dependencies outside the standard library. No cgo or assembly toolchain is needed to use them.

The assembly kernels are generated and checked by simulating their instruction streams, including backends the test machine cannot execute. Published XXH3 measurements at 64 KiB reach 97 GB/s on Zen 4 and 28 GB/s on Neoverse N2, against zeebo/xxh3's 83 and 12.5 GB/s; see Speed for the context.

Quick start · Choose a package · API reference · Benchmarks · Contributing · Help

Quick start

Requires Go 1.21 or newer. In your Go module:

go get github.com/JohanLindvall/haste/xxh3

Save this as main.go, then run go run .:

package main

import (
    "fmt"

    "github.com/JohanLindvall/haste/xxh3"
)

func main() {
    data := []byte("haste")
    fmt.Printf("%016x\n", xxh3.Sum64(data))
    fmt.Printf("%x\n", xxh3.Sum128(data).Bytes())
}

Output:

29a60a4033936c78
26b6ef3229e32bf9476854cadb11bc32

These are non-cryptographic hashes for checksums, fingerprints, and fast lookup keys. Seeds and custom secrets do not make them suitable for passwords, authentication, or collision resistance against hostile input.

Choose a package

Package Use it when Output Streaming
xxh3 You want a general starting point, or need XXH3 compatibility 64 or 128 bits Yes; both widths from one digest
xxh64 You need existing XXH64 checksums, including cespare/xxhash output 64 bits Yes
rapidhash You need rapidhash V3, or want to compare a scalar hash on your workload 64 bits No

All three accept byte slices, strings, and seeds. They compute different hashes; choose the algorithm your data format expects. XXH3 and XXH64 match xxHash v0.8.3, and rapidhash matches V3's default, unprotected form. Older rapidhash versions can produce different results.

Install another package with go get github.com/JohanLindvall/haste/xxh64 or go get github.com/JohanLindvall/haste/rapidhash. Import a subpackage: the module root exports no hash functions.

Use

Byte slices, strings, and seeds

With fmt and xxh3 imported, a function body can start with:

data := []byte("haste")
h := xxh3.Sum64(data)
textHash := xxh3.Sum64String("haste") // same result, without copying the string
seeded := xxh3.Sum64Seed(data, 42)
wide := xxh3.Sum128(data)            // wide.Hi, wide.Lo; wide.Bytes() for encoding

fmt.Printf("%016x %016x %016x %x\n", h, textHash, seeded, wide.Bytes())

Sum64String, Sum64Seed, and Sum64SeedString are available in all three packages. XXH3 also has 128-bit forms and custom-secret entry points. A custom secret must contain at least xxh3.MinSecretSize bytes; NewSecret retains it, so keep it unchanged while the digest is in use.

Streaming and io.Reader

XXH3 and XXH64 digests implement hash.Hash64 and work with io.Copy:

package main

import (
    "fmt"
    "io"
    "log"
    "strings"

    "github.com/JohanLindvall/haste/xxh3"
)

func main() {
    d := xxh3.New()
    if _, err := io.Copy(d, strings.NewReader("haste")); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%016x\n", d.Sum64())
    fmt.Printf("%x\n", d.Sum128().Bytes())
}

This prints the same two hashes as the quick start. Replace the reader with a file or another io.Reader to hash incrementally. Sum64 and Sum128 preserve the digest state, and Reset lets you reuse it. NewSeed(seed) retains the seed across resets. Use a separate digest per concurrent stream; a digest is not safe for concurrent mutation. Construct XXH3 digests with New, NewSeed, or NewSecret; their zero value is not usable.

Fixed-size keys and byte order

All three packages provide Sum64Uint32, Sum64Uint64, and Sum64Uint128(lo, hi) for integer keys. They hash the same bytes as Sum64 with the integers encoded in little-endian order. XXH3 and XXH64 also have seeded forms. See the runnable integer-key example.

XXH3's Uint128.Bytes() returns the canonical big-endian 16-byte result, high half first. Digest.Sum(nil) in XXH3 and XXH64 returns the big-endian 64-bit hash; use Sum128().Bytes() when you need 128 bits.

XXH64

The common Sum64, Sum64String, New, Digest.Write, and Digest.Sum64 calls match cespare/xxhash's API. To keep existing xxhash call sites for those operations:

import xxhash "github.com/JohanLindvall/haste/xxh64"

The seeded constructor is named NewSeed. With the alias above, replace xxhash.NewWithSeed(seed) with xxhash.NewSeed(seed). Check the API reference for the rest of your call sites; the two packages' exported APIs are not identical.

rapidhash

Use rapidhash.Sum64(data) or rapidhash.Sum64Seed(data, seed) for a whole input. rapidhash needs the full input and its length, so it has no streaming API. Compare it with XXH3 on your own workload; the measurement notes describe its different use of the CPU and the algorithm versions compared.

Speed

A sample from the published 64 KiB XXH3 measurements, using Go 1.26. Throughput in GB/s, higher is better:

CPU haste/xxh3 zeebo/xxh3
Ryzen 7 8840HS (Zen 4, AVX-512) 97 83
Azure Cobalt 100 (Neoverse N2) 28 12.5
Core Ultra 9 185H (Redwood Cove, AVX2) 63 59

These are measurements on specific hardware, not a promise for every CPU or input length. For short inputs, call overhead and code alignment can dominate. The full tables and analysis retain input sizes, nanoseconds per hash, streaming results, and XXH64 and rapidhash comparisons. Automated runner results cover additional platforms.

To reproduce a comparison, follow the benchmark procedure. The comparison suite lives in a separate bench/ module, keeping its Go and C reference dependencies out of the library.

Backends

CPU detection happens automatically at package initialization:

Build XXH3 XXH64 and rapidhash
amd64 AVX-512, AVX2, or SSE2 Scalar assembly
arm64 NEON, a core-specific hybrid, or supported SVE2 widths Scalar assembly
Other architectures, or -tags purego Portable Go Portable Go

Each package exposes Backend() to report the active implementation. To select the portable paths for your application, use go build -tags purego ./.... See backend selection and verification for details.

Correctness

All three packages are checked against C-derived reference vectors, portable Go implementations, and simulated instruction streams. Streaming tests compare chunked input with one-shot hashing. CI runs native amd64 and arm64 tests, race and purego configurations, fuzz targets, cross-compilation, and generated file checks. The separate comparison module also checks independent Go ports and, when cgo is available, the reference C implementations.

Read how the kernels are verified or run go test ./... from a checkout. The Go examples run as part of the test suite and appear in the package documentation.

Releases

The module is v0.x while the API evolves; a minor version may break source compatibility. Pin a tagged version in your application's go.mod and review changes when upgrading. See available versions and tags.

A green CI run on main tags the next patch when Go, assembly, or module files have changed since the last tag. Markdown-only changes do not earn a version. Earlier versions exposed XXH3 at the module root; update that import to github.com/JohanLindvall/haste/xxh3 to use the current layout.

Contributing

Documentation fixes, small reproductions, test cases, and benchmark reports on new CPUs are welcome. You do not need assembly experience to help. CONTRIBUTING.md covers setup, the repository layout, checks, and performance measurements. The issue forms help you report a bug, suggest a feature, or share a performance result.

Building the assembly

The generated kernels are checked in. For generator work, edit internal/asmgen, then run go generate ./... with the target binutils installed. See the setup and verification steps and how generation works.

Help and community

See SUPPORT.md for questions and troubleshooting, and the code of conduct for participation expectations. Johan Lindvall maintains haste, with help from contributors.

If haste is useful to you, a star helps other Go developers discover it. Sharing a reproducible benchmark or a use case helps people judge whether it fits their workload.

Security

Report vulnerabilities through the private reporting form. See SECURITY.md for scope and supported versions.

License

MIT. Source files carry SPDX license identifiers. The vendored reference C implementations live only in the separate bench/ module and are not downloaded with the library. For academic use, see CITATION.cff.

About

Three fast non-cryptographic hashes for Go, one package each: XXH3 (64/128-bit), XXH64 and rapidhash. Bit-identical to their reference C implementations. Generated SSE2/AVX2/AVX-512/NEON/SVE2 kernels, verified by simulation where the host cannot run them. No dependencies outside the standard library.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages