From 35cae1099e773ad1d34f18ce33f0fd58f5312545 Mon Sep 17 00:00:00 2001 From: Wei Meng Date: Mon, 11 Jul 2022 16:38:35 +0800 Subject: [PATCH] Realloc slice exponentially in mfs `registry/storage/driver/inmemory/driver_test.go` times out after ~10min. The slow test is `testsuites.go:TestWriteReadLargeStreams()` which writes a 5GB file. Root cause is inefficient slice reallocation algorithm. The slice holding file bytes grows only 32K on each allocation. To fix it, this PR grows slice exponentially. Signed-off-by: Wei Meng --- registry/storage/driver/inmemory/mfs.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/registry/storage/driver/inmemory/mfs.go b/registry/storage/driver/inmemory/mfs.go index 9ac8d434..028052ed 100644 --- a/registry/storage/driver/inmemory/mfs.go +++ b/registry/storage/driver/inmemory/mfs.go @@ -290,18 +290,28 @@ func (f *file) ReadAt(p []byte, offset int64) (n int, err error) { return copy(p, f.data[offset:]), nil } +// reallocExponent is the exponent used to realloc a slice. The value roughly +// follows the behavior of Go built-in append function. +const reallocExponent = 1.25 + func (f *file) WriteAt(p []byte, offset int64) (n int, err error) { - off := int(offset) - if cap(f.data) < off+len(p) { - data := make([]byte, len(f.data), off+len(p)) + newLen := offset + int64(len(p)) + if int64(cap(f.data)) < newLen { + // Grow slice exponentially to ensure amortized linear time complexity + // of reallocation + newCap := int64(float64(cap(f.data)) * reallocExponent) + if newCap < newLen { + newCap = newLen + } + data := make([]byte, len(f.data), newCap) copy(data, f.data) f.data = data } f.mod = time.Now() - f.data = f.data[:off+len(p)] + f.data = f.data[:newLen] - return copy(f.data[off:off+len(p)], p), nil + return copy(f.data[offset:newLen], p), nil } func (f *file) String() string {