From caa1bc0866a9c67625779eeec20364a1e5837f82 Mon Sep 17 00:00:00 2001 From: Philip Ko Date: Sun, 5 Jul 2026 02:56:49 -0700 Subject: [PATCH] Preallocate entries slice in DeserializeEntries DeserializeEntries reads the entry count from the directory stream and then builds the result with make([]EntryV3, 0) plus append. Because the slice starts at zero capacity, append reallocates and copies the backing array as it grows - 27 times for a full-planet leaf directory (~62k entries) - and each discarded intermediate array is transient garbage. The exact entry count is already known before the append loops, so size the slice capacity to it up front. The decode logic is otherwise unchanged and the returned entries are identical. Decoding a 62,746-entry leaf (gzip), allocations measured with -benchmem: before: 8,849,368 B/op 216 allocs/op after: 1,579,005 B/op 190 allocs/op (-82% bytes, -26 allocs) --- pmtiles/directory.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pmtiles/directory.go b/pmtiles/directory.go index befda59..ce73317 100644 --- a/pmtiles/directory.go +++ b/pmtiles/directory.go @@ -323,8 +323,6 @@ func SerializeEntries(entries []EntryV3, compression Compression) []byte { } func DeserializeEntries(data *bytes.Buffer, compression Compression) []EntryV3 { - entries := make([]EntryV3, 0) - var reader io.Reader if compression == NoCompression { @@ -338,6 +336,10 @@ func DeserializeEntries(data *bytes.Buffer, compression Compression) []EntryV3 { numEntries, _ := binary.ReadUvarint(byteReader) + // Preallocate the entries slice to the known count so the append loops below + // do not repeatedly reallocate and copy the backing array as it grows. + entries := make([]EntryV3, 0, numEntries) + lastID := uint64(0) for i := uint64(0); i < numEntries; i++ { tmp, _ := binary.ReadUvarint(byteReader)