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.

Source code on GitHub


TL;DR


Table of Contents


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}


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}


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.