Early Exploration of Zero‑Heap‑Allocation Techniques in Go
This document describes the initial work on Tellstone, a prototype in‑memory key‑value store written in Go. The project is in its very early stages – only a handful of internal packages have been drafted, and none of the code is production‑ready. The purpose of this write‑up is to share the experimental techniques I am investigating, not to make performance guarantees.
TL;DR
- I am experimenting with zero‑heap‑allocation patterns on the hot path of a storage engine.
- Early benchmarks (run on a developer notebook) show ~70 M operations per second with 0 B/op and 0 allocs/op in isolated micro‑benchmarks. These numbers are pre‑liminary and not representative of a real workload.
- The current codebase consists of a few internal packages that demonstrate:
- Static hashing without heap allocation.
- In‑place encryption using pre‑allocated buffers.
- Zero‑allocation parsing of a tiny SQL‑like syntax.
- All of the above are experimental and have not been hardened, tested under load, or audited for safety.
Table of Contents
- Motivation
- Current Status
- Experimental – Sharding & In‑Place Crypto
- Zero‑Allocation Parsing
- Next Steps
Motivation {#motivation}
In high‑throughput Go services the Garbage Collector (GC) can become a hidden source of latency. Even a small number of heap allocations on a hot path can cause stop‑the‑world pauses that degrade tail latency. My goal with Tellstone is to explore how far a Go‑based engine can be pushed when the hot path is deliberately kept allocation‑free.
Note: The ideas presented here are research‑oriented. They are not meant as a production recommendation, nor have they been subjected to rigorous correctness or security reviews.
Current Status {#current-status}
- The repository contains a prototype engine split into a few packages under
internal/engine,internal/crypto, andinternal/parser. - No public API or client/server transport layer exists yet.
- Benchmarks are micro‑benchmarks that run the core functions in isolation; they do not include network I/O, concurrency control, or persistence.
- The code compiles and passes basic unit tests, but many edge cases remain unimplemented (e.g., error handling, key expiration, multi‑node sharding coordination).
Experimental – Sharding & In‑Place Crypto {#sharding-and-crypto}
Sharding
I currently use a 256‑shard layout. Keys are mapped to shards using a hand‑rolled FNV‑1a 32‑bit hash that operates directly on the string bytes without allocating intermediate slices.
func shardIdx(key string) uint32 {
const (
offset32 = 2166136261
prime32 = 16777619
shardCnt = 256
)
h := uint32(offset32)
for i := 0; i < len(key); i++ {
h ^= uint32(key[i])
h *= prime32
}
return h & (shardCnt - 1)
}
This function stays entirely on the stack; the compiler can inline it and it does not cause any heap allocation.
In‑Place Encryption
The prototype encrypts values with ChaCha20‑Poly1305. Rather than allocating a new slice for ciphertext, I reuse a buffer obtained from a sync.Pool (or a pre‑allocated slice when the pool is warm).
func encryptInPlace(dst, pt []byte, aead cipher.AEAD) ([]byte, error) {
// dst is expected to have enough capacity; if not we fall back to allocation (rare in tests).
needed := aead.NonceSize() + len(pt) + aead.Overhead()
if cap(dst) < needed {
dst = make([]byte, needed) // allocation path – only for benchmark warm‑up
}
dst = dst[:needed]
nonce := dst[:aead.NonceSize()]
if _, err := rand.Read(nonce); err != nil { return nil, err }
// Seal writes ciphertext directly into the tail of dst.
out := aead.Seal(dst[aead.NonceSize():aead.NonceSize()], nonce, pt, nil)
return dst[:aead.NonceSize()+len(out)], nil
}
In practice the pool‑backed path runs without any heap traffic, but the fallback allocation is kept for safety during early testing.
Zero‑Allocation Parsing {#zero-allocation-parsing}
I am prototyping a tiny SQL‑like grammar for SET/GET commands. The parser works directly on the incoming byte buffer and returns sub‑slices that point into the original data, avoiding any string allocations.
// fast case‑insensitive prefix check without allocating strings
func hasPrefixCase(b, prefix []byte) bool {
if len(b) < len(prefix) { return false }
for i := range prefix {
c := b[i]
if c >= 'A' && c <= 'Z' { c += 32 }
if c != prefix[i] { return false }
}
return true
}
The current implementation only recognises SET key value and GET key. Error handling is minimal and the parser is not yet tolerant of malformed input.
Benchmarks {#benchmarks}
Below are a selection of micro‑benchmarks from the current prototype (run on an AMD Ryzen 9 9950X, 16‑core, Go 1.26.2). All benchmarks report 0 B/op and 0 allocs/op for the hot‑path functions, confirming the allocation‑free design.
SQL Parser Benchmarks
Benchmark | ns/op | B/op | allocs/op |
ParseSQL_Select_Parallel | 11.10 | 0 | 0 |
ParseSQL_Insert_Parallel | 11.21 | 0 | 0 |
ParseSQL_Select_Sequential | 27.05 | 0 | 0 |
ParseSQL_Insert_Sequential | 84.07 | 0 | 0 |
ParseSQL_Select_MixedCase_Sequential | 28.35 | 0 | 0 |
ParseSQL_Insert_LongValue_Sequential | 851.3 | 0 | 0 |
ParseSQL_Insert_TTLOverflow_Sequential | 52.32 | 0 | 0 |
ParseSQL_Select_UTF8Key_Sequential | 26.54 | 0 | 0 |
ParseSQL_MixedParallel_Mix_Parallel | 11.49 | 0 | 0 |
Storage Engine Benchmarks
Benchmark | ns/op | B/op | allocs/op |
EngineGetNoAlloc | 15.10 | 0 | 0 |
EngineGetWithEncryptionNoAlloc | 160.5 | 0 | 0 |
ChronometerEvictionPipeline | 168.8 | 1 | 0 |
ChronometerEvictionPipelineSequential | 218.1 | 1 | 0 |
These numbers represent isolated function calls; real‑world latency will include network, synchronization, and I/O overhead.
Next Steps {#next-steps}
- Expose a network layer (e.g., a simple TCP/HTTP gateway) and measure how much latency the encryption and parsing stages add when wrapped in real I/O.
- Add concurrency controls (e.g., lock‑free data structures or per‑shard mutexes) and benchmark under realistic multi‑goroutine workloads.
- Improve robustness – comprehensive error handling, testing for edge cases, and security review of the in‑place crypto path.
- Publish a public API and package the internal modules for external consumption once the design stabilises.
Disclaimer: All code, benchmarks, and performance claims in this document are experimental. Tellstone is not ready for production use, and the presented numbers should be taken as early‑stage curiosities rather than guarantees.