From 255ba83c4b347a2d071bcb178f6db66e8ca32ea1 Mon Sep 17 00:00:00 2001 From: greatroar <@> Date: Sun, 5 Jul 2020 08:37:34 +0200 Subject: [PATCH] Parallel index benchmarks + benchmark optimizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createRandomIndex was using the global RNG, which locks on every call It was also using twice as many random numbers as necessary and doing a float division in every iteration of the inner loop. BenchmarkDecodeIndex was using too short an input, especially for a parallel version. (It may now be using one that is a bit large.) Results on linux/amd64, -benchtime=3s -count=20: name old time/op new time/op delta PackerManager-8 178ms ± 0% 178ms ± 0% ~ (p=0.165 n=20+20) DecodeIndex-8 13.6µs ± 2% 4539886.8µs ± 0% +33293901.38% (p=0.000 n=20+18) IndexHasUnknown-8 44.4ns ± 7% 44.4ns ± 5% ~ (p=0.873 n=20+19) IndexHasKnown-8 49.2ns ± 3% 48.3ns ± 0% -1.86% (p=0.000 n=20+16) IndexAlloc-8 802ms ± 1% 758ms ± 1% -5.51% (p=0.000 n=20+19) MasterIndexLookupSingleIndex-8 124ns ± 1% 122ns ± 0% -1.41% (p=0.000 n=20+14) MasterIndexLookupMultipleIndex-8 373ns ± 2% 369ns ± 2% -1.13% (p=0.001 n=20+20) MasterIndexLookupSingleIndexUnknown-8 67.8ns ± 3% 68.4ns ± 5% ~ (p=0.753 n=20+20) MasterIndexLookupMultipleIndexUnknown-8 316ns ± 3% 315ns ± 3% ~ (p=0.846 n=20+20) SaveAndEncrypt-8 30.5ms ± 1% 30.2ms ± 1% -1.09% (p=0.000 n=19+19) LoadTree-8 527µs ± 1% 540µs ± 1% +2.37% (p=0.000 n=19+20) LoadBlob-8 5.65ms ± 0% 5.64ms ± 0% -0.21% (p=0.000 n=19+18) LoadAndDecrypt-8 7.07ms ± 2% 5.93ms ± 0% -16.15% (p=0.000 n=19+20) LoadIndex-8 32.1ms ± 2% 25.1ms ± 0% -21.64% (p=0.000 n=20+18) name old speed new speed delta PackerManager-8 296MB/s ± 0% 296MB/s ± 0% ~ (p=0.159 n=20+20) SaveAndEncrypt-8 138MB/s ± 1% 139MB/s ± 1% +1.10% (p=0.000 n=19+19) LoadBlob-8 177MB/s ± 0% 177MB/s ± 0% +0.21% (p=0.000 n=19+18) LoadAndDecrypt-8 141MB/s ± 2% 169MB/s ± 0% +19.24% (p=0.000 n=19+20) name old alloc/op new alloc/op delta PackerManager-8 91.8kB ± 0% 91.8kB ± 0% ~ (p=0.826 n=19+12) IndexAlloc-8 786MB ± 0% 786MB ± 0% +0.01% (p=0.000 n=20+20) SaveAndEncrypt-8 21.0MB ± 0% 21.0MB ± 0% -0.00% (p=0.012 n=20+19) name old allocs/op new allocs/op delta PackerManager-8 1.41k ± 0% 1.41k ± 0% ~ (all equal) IndexAlloc-8 977k ± 0% 977k ± 0% +0.01% (p=0.022 n=20+20) SaveAndEncrypt-8 73.0 ± 0% 73.0 ± 0% ~ (all equal) --- internal/repository/index_test.go | 53 ++++++++++++++++++++---- internal/repository/master_index_test.go | 49 ++++++++++++++++++---- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/internal/repository/index_test.go b/internal/repository/index_test.go index c96101906..513f4c5d1 100644 --- a/internal/repository/index_test.go +++ b/internal/repository/index_test.go @@ -3,6 +3,7 @@ package repository_test import ( "bytes" "math/rand" + "sync" "testing" "github.com/restic/restic/internal/repository" @@ -329,15 +330,40 @@ func TestIndexUnserialize(t *testing.T) { } } +var ( + benchmarkIndexJSON []byte + benchmarkIndexJSONOnce sync.Once +) + +func initBenchmarkIndexJSON() { + idx, _ := createRandomIndex(rand.New(rand.NewSource(0))) + var buf bytes.Buffer + idx.Encode(&buf) + benchmarkIndexJSON = buf.Bytes() +} + func BenchmarkDecodeIndex(b *testing.B) { + benchmarkIndexJSONOnce.Do(initBenchmarkIndexJSON) b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := repository.DecodeIndex(docExample) + _, err := repository.DecodeIndex(benchmarkIndexJSON) rtest.OK(b, err) } } +func BenchmarkDecodeIndexParallel(b *testing.B) { + benchmarkIndexJSONOnce.Do(initBenchmarkIndexJSON) + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, err := repository.DecodeIndex(benchmarkIndexJSON) + rtest.OK(b, err) + } + }) +} + func TestIndexUnserializeOld(t *testing.T) { idx, err := repository.DecodeOldIndex(docOldExample) rtest.OK(t, err) @@ -401,7 +427,7 @@ func createRandomIndex(rng *rand.Rand) (idx *repository.Index, lookupID restic.I var blobs []restic.Blob offset := 0 for offset < maxPackSize { - size := 2000 + rand.Intn(4*1024*1024) + size := 2000 + rng.Intn(4*1024*1024) id := NewRandomTestID(rng) blobs = append(blobs, restic.Blob{ Type: restic.DataBlob, @@ -411,12 +437,12 @@ func createRandomIndex(rng *rand.Rand) (idx *repository.Index, lookupID restic.I }) offset += size - - if rand.Float32() < 0.001 && lookupID.IsNull() { - lookupID = id - } } idx.StorePack(packID, blobs) + + if i == 0 { + lookupID = blobs[rng.Intn(len(blobs))].ID + } } return idx, lookupID @@ -444,12 +470,25 @@ func BenchmarkIndexHasKnown(b *testing.B) { } func BenchmarkIndexAlloc(b *testing.B) { + rng := rand.New(rand.NewSource(0)) b.ReportAllocs() + for i := 0; i < b.N; i++ { - createRandomIndex(rand.New(rand.NewSource(0))) + createRandomIndex(rng) } } +func BenchmarkIndexAllocParallel(b *testing.B) { + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + rng := rand.New(rand.NewSource(0)) + for pb.Next() { + createRandomIndex(rng) + } + }) +} + func TestIndexHas(t *testing.T) { type testEntry struct { id restic.ID diff --git a/internal/repository/master_index_test.go b/internal/repository/master_index_test.go index 7dfcdda5f..3b858253a 100644 --- a/internal/repository/master_index_test.go +++ b/internal/repository/master_index_test.go @@ -1,6 +1,7 @@ package repository_test import ( + "fmt" "math/rand" "testing" @@ -74,11 +75,11 @@ func BenchmarkMasterIndexLookupMultipleIndex(b *testing.B) { mIdx := repository.NewMasterIndex() for i := 0; i < 5; i++ { - idx, _ := createRandomIndex(rand.New(rng)) + idx, _ := createRandomIndex(rng) mIdx.Insert(idx) } - idx1, lookupID := createRandomIndex(rand.New(rng)) + idx1, lookupID := createRandomIndex(rng) mIdx.Insert(idx1) b.ResetTimer() @@ -107,17 +108,51 @@ func BenchmarkMasterIndexLookupMultipleIndexUnknown(b *testing.B) { lookupID := restic.NewRandomID() mIdx := repository.NewMasterIndex() - for i := 0; i < 5; i++ { - idx, _ := createRandomIndex(rand.New(rng)) + for i := 0; i < 6; i++ { + idx, _ := createRandomIndex(rng) mIdx.Insert(idx) } - idx1, _ := createRandomIndex(rand.New(rng)) - mIdx.Insert(idx1) - b.ResetTimer() for i := 0; i < b.N; i++ { mIdx.Lookup(lookupID, restic.DataBlob) } } + +func BenchmarkMasterIndexLookupParallel(b *testing.B) { + mIdx := repository.NewMasterIndex() + + for _, numindices := range []int{5, 10, 20} { + var lookupID restic.ID + + b.StopTimer() + rng := rand.New(rand.NewSource(0)) + for i := 0; i < numindices; i++ { + var idx *repository.Index + idx, lookupID = createRandomIndex(rng) + mIdx.Insert(idx) + } + b.StartTimer() + + name := fmt.Sprintf("known,indices=%d", numindices) + b.Run(name, func(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + mIdx.Lookup(lookupID, restic.DataBlob) + } + }) + }) + + lookupID = restic.NewRandomID() + name = fmt.Sprintf("unknown,indices=%d", numindices) + b.Run(name, func(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + mIdx.Lookup(lookupID, restic.DataBlob) + } + }) + }) + + } +}