errgroup/syncmap/map_test.go
Bryan C. Mills 54b13b0b03 syncmap: add a synchronized map implementation.
This is a draft for the sync.Map API proposed in golang/go#18177.

It supports fast-path loads via an atomic variable, falling back to a
Mutex for stores. In order to keep stores amortized to O(1), loads
following a store follow the Mutex path until enough loads have
occurred to offset the cost of a deep copy.

For mostly-read loads, such as the maps in the reflect package in the
standard library, this significantly reduces cache-line contention vs.
a plain RWMutex with a map.

goos: linux
goarch: amd64
pkg: golang.org/x/sync/syncmap
BenchmarkLoadMostlyHits/*syncmap_test.DeepCopyMap               20000000                73.1 ns/op
BenchmarkLoadMostlyHits/*syncmap_test.DeepCopyMap-48            100000000               13.8 ns/op
BenchmarkLoadMostlyHits/*syncmap_test.RWMutexMap                20000000                87.7 ns/op
BenchmarkLoadMostlyHits/*syncmap_test.RWMutexMap-48             10000000               154 ns/op
BenchmarkLoadMostlyHits/*syncmap.Map                            20000000                72.1 ns/op
BenchmarkLoadMostlyHits/*syncmap.Map-48                         100000000               11.2 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.DeepCopyMap             20000000                63.2 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.DeepCopyMap-48          200000000               14.2 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.RWMutexMap              20000000                72.7 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.RWMutexMap-48           10000000               150 ns/op
BenchmarkLoadMostlyMisses/*syncmap.Map                          30000000                56.4 ns/op
BenchmarkLoadMostlyMisses/*syncmap.Map-48                       200000000                9.77 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap_test.RWMutexMap            2000000               683 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap_test.RWMutexMap-48         1000000              1394 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap.Map                        2000000               645 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap.Map-48                     1000000              1253 ns/op
BenchmarkLoadOrStoreUnique/*syncmap_test.RWMutexMap              1000000              1015 ns/op
BenchmarkLoadOrStoreUnique/*syncmap_test.RWMutexMap-48           1000000              1911 ns/op
BenchmarkLoadOrStoreUnique/*syncmap.Map                          1000000              1018 ns/op
BenchmarkLoadOrStoreUnique/*syncmap.Map-48                       1000000              1776 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.DeepCopyMap         50000000                30.2 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.DeepCopyMap-48      2000000000               1.24 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.RWMutexMap          30000000                50.1 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.RWMutexMap-48        5000000               451 ns/op
BenchmarkLoadOrStoreCollision/*syncmap.Map                      30000000                36.8 ns/op
BenchmarkLoadOrStoreCollision/*syncmap.Map-48                   2000000000               1.24 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.DeepCopyMap             10000000               213 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.DeepCopyMap-48           1000000              5012 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.RWMutexMap              20000000                68.8 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.RWMutexMap-48            5000000               429 ns/op
BenchmarkAdversarialAlloc/*syncmap.Map                           5000000               229 ns/op
BenchmarkAdversarialAlloc/*syncmap.Map-48                        2000000               600 ns/op
BenchmarkAdversarialDelete/*syncmap_test.DeepCopyMap             5000000               314 ns/op
BenchmarkAdversarialDelete/*syncmap_test.DeepCopyMap-48          2000000               726 ns/op
BenchmarkAdversarialDelete/*syncmap_test.RWMutexMap             20000000                63.2 ns/op
BenchmarkAdversarialDelete/*syncmap_test.RWMutexMap-48           5000000               469 ns/op
BenchmarkAdversarialDelete/*syncmap.Map                         10000000               203 ns/op
BenchmarkAdversarialDelete/*syncmap.Map-48                      10000000               253 ns/op

goos: linux
goarch: ppc64le
pkg: golang.org/x/sync/syncmap
BenchmarkLoadMostlyHits/*syncmap_test.DeepCopyMap                5000000               253 ns/op
BenchmarkLoadMostlyHits/*syncmap_test.DeepCopyMap-48            50000000                26.2 ns/op
BenchmarkLoadMostlyHits/*syncmap_test.RWMutexMap                 5000000               505 ns/op
BenchmarkLoadMostlyHits/*syncmap_test.RWMutexMap-48              3000000               443 ns/op
BenchmarkLoadMostlyHits/*syncmap.Map                            10000000               200 ns/op
BenchmarkLoadMostlyHits/*syncmap.Map-48                         100000000               18.1 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.DeepCopyMap             10000000               162 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.DeepCopyMap-48          100000000               23.8 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.RWMutexMap              10000000               195 ns/op
BenchmarkLoadMostlyMisses/*syncmap_test.RWMutexMap-48            3000000               531 ns/op
BenchmarkLoadMostlyMisses/*syncmap.Map                          10000000               182 ns/op
BenchmarkLoadMostlyMisses/*syncmap.Map-48                       100000000               15.8 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap_test.RWMutexMap            1000000              1664 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap_test.RWMutexMap-48         1000000              1768 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap.Map                        1000000              2128 ns/op
BenchmarkLoadOrStoreBalanced/*syncmap.Map-48                     1000000              1903 ns/op
BenchmarkLoadOrStoreUnique/*syncmap_test.RWMutexMap              1000000              2657 ns/op
BenchmarkLoadOrStoreUnique/*syncmap_test.RWMutexMap-48           1000000              2577 ns/op
BenchmarkLoadOrStoreUnique/*syncmap.Map                          1000000              1714 ns/op
BenchmarkLoadOrStoreUnique/*syncmap.Map-48                       1000000              2484 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.DeepCopyMap         10000000               130 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.DeepCopyMap-48      100000000               11.3 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.RWMutexMap           3000000               426 ns/op
BenchmarkLoadOrStoreCollision/*syncmap_test.RWMutexMap-48        2000000               930 ns/op
BenchmarkLoadOrStoreCollision/*syncmap.Map                      10000000               131 ns/op
BenchmarkLoadOrStoreCollision/*syncmap.Map-48                   300000000                4.07 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.DeepCopyMap              3000000               447 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.DeepCopyMap-48            300000              4159 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.RWMutexMap              10000000               191 ns/op
BenchmarkAdversarialAlloc/*syncmap_test.RWMutexMap-48            3000000               535 ns/op
BenchmarkAdversarialAlloc/*syncmap.Map                           2000000               525 ns/op
BenchmarkAdversarialAlloc/*syncmap.Map-48                        1000000              1000 ns/op
BenchmarkAdversarialDelete/*syncmap_test.DeepCopyMap             2000000               711 ns/op
BenchmarkAdversarialDelete/*syncmap_test.DeepCopyMap-48          2000000               900 ns/op
BenchmarkAdversarialDelete/*syncmap_test.RWMutexMap              3000000               354 ns/op
BenchmarkAdversarialDelete/*syncmap_test.RWMutexMap-48           3000000               473 ns/op
BenchmarkAdversarialDelete/*syncmap.Map                          2000000              1357 ns/op
BenchmarkAdversarialDelete/*syncmap.Map-48                       5000000               334 ns/op

Updates golang/go#18177

Change-Id: I8d561b617b1cd2ca03a8e68a5d5a28a519a0ce38
Reviewed-on: https://go-review.googlesource.com/33912
Reviewed-by: Russ Cox <rsc@golang.org>
2017-02-15 17:32:38 +00:00

116 lines
2.5 KiB
Go

// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package syncmap_test
import (
"fmt"
"math/rand"
"reflect"
"testing"
"testing/quick"
"golang.org/x/sync/syncmap"
)
// mapCall is a quick.Generator for calls on mapInterface.
type mapCall struct {
key interface{}
apply func(mapInterface) (interface{}, bool)
desc string
}
type mapResult struct {
value interface{}
ok bool
}
var stringType = reflect.TypeOf("")
func randValue(r *rand.Rand) interface{} {
k, ok := quick.Value(stringType, r)
if !ok {
panic(fmt.Sprintf("quick.Value(%v, _) failed", stringType))
}
return k.Interface()
}
func (mapCall) Generate(r *rand.Rand, size int) reflect.Value {
k := randValue(r)
var (
app func(mapInterface) (interface{}, bool)
desc string
)
switch rand.Intn(4) {
case 0:
app = func(m mapInterface) (interface{}, bool) {
return m.Load(k)
}
desc = fmt.Sprintf("Load(%q)", k)
case 1:
v := randValue(r)
app = func(m mapInterface) (interface{}, bool) {
m.Store(k, v)
return nil, false
}
desc = fmt.Sprintf("Store(%q, %q)", k, v)
case 2:
v := randValue(r)
app = func(m mapInterface) (interface{}, bool) {
return m.LoadOrStore(k, v)
}
desc = fmt.Sprintf("LoadOrStore(%q, %q)", k, v)
case 3:
app = func(m mapInterface) (interface{}, bool) {
m.Delete(k)
return nil, false
}
desc = fmt.Sprintf("Delete(%q)", k)
}
return reflect.ValueOf(mapCall{k, app, desc})
}
func applyCalls(m mapInterface, calls []mapCall) (results []mapResult, final map[interface{}]interface{}) {
for _, c := range calls {
v, ok := c.apply(m)
results = append(results, mapResult{v, ok})
}
final = make(map[interface{}]interface{})
m.Range(func(k, v interface{}) bool {
final[k] = v
return true
})
return results, final
}
func applyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) {
return applyCalls(new(syncmap.Map), calls)
}
func applyRWMutexMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) {
return applyCalls(new(RWMutexMap), calls)
}
func applyDeepCopyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) {
return applyCalls(new(DeepCopyMap), calls)
}
func TestMapMatchesRWMutex(t *testing.T) {
if err := quick.CheckEqual(applyMap, applyRWMutexMap, nil); err != nil {
t.Error(err)
}
}
func TestMapMatchesDeepCopy(t *testing.T) {
if err := quick.CheckEqual(applyMap, applyRWMutexMap, nil); err != nil {
t.Error(err)
}
}