Skip to content

Commit

Permalink
update benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
cornelk committed Aug 28, 2022
1 parent 152772b commit e90d44f
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 25 deletions.
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,26 @@ Reading from the hash map in a thread-safe way is nearly as fast as reading from
in an unsafe way and twice as fast as Go's `sync.Map`:

```
BenchmarkReadHashMapUint-8 2047108 547.2 ns/op
BenchmarkReadHaxMapUint-8 2295067 532.3 ns/op (not safe to use, can lose writes during concurrent deletes)
BenchmarkReadGoMapUintUnsafe-8 3303577 360.6 ns/op
BenchmarkReadGoMapUintMutex-8 83017 14266 ns/op
BenchmarkReadGoSyncMapUint-8 943773 1208 ns/op
BenchmarkReadHashMapUint-8 1314156 955.6 ns/op
BenchmarkReadHaxMapUint-8 872134 1316 ns/op (can not handle hash 0 collisions)
BenchmarkReadGoMapUintUnsafe-8 1560886 762.8 ns/op
BenchmarkReadGoMapUintMutex-8 42284 28232 ns/op
BenchmarkReadGoSyncMapUint-8 468338 2672 ns/op
```

Reading from the map while writes are happening:
```
BenchmarkReadHashMapWithWritesUint-8 1669750 718.1 ns/op
BenchmarkReadGoMapWithWritesUintMutex-8 24919 54270 ns/op
BenchmarkReadGoSyncMapWithWritesUint-8 823063 1439 ns/op
BenchmarkReadHashMapWithWritesUint-8 890938 1288 ns/op
BenchmarkReadGoMapWithWritesUintMutex-8 14290 86758 ns/op
BenchmarkReadGoSyncMapWithWritesUint-8 374464 3149 ns/op
```

Write performance without any concurrent reads:

```
BenchmarkWriteHashMapUint-8 30104 49176 ns/op
BenchmarkWriteGoMapMutexUint-8 177536 6571 ns/op
BenchmarkWriteGoSyncMapUint-8 19857 61428 ns/op
BenchmarkWriteHashMapUint-8 15384 79032 ns/op
BenchmarkWriteGoMapMutexUint-8 74569 14874 ns/op
BenchmarkWriteGoSyncMapUint-8 10000 107094 ns/op
```

The benchmarks were run with Golang 1.19.0 on Linux using `make benchmark`.
Expand All @@ -73,7 +73,7 @@ The benchmarks were run with Golang 1.19.0 on Linux using `make benchmark`.

* Faster

* thread-safe access without need of a(n extra) mutex
* thread-safe access without need of a mutex

### Benefits over [Golang's sync.Map](https://golang.org/pkg/sync/#Map)

Expand All @@ -92,3 +92,5 @@ The benchmarks were run with Golang 1.19.0 on Linux using `make benchmark`.
Once a slice is allocated, the size of it does not change.
The library limits the index into the slice, therefore the Golang size check is obsolete.
When the slice reaches a defined fill rate, a bigger slice is allocated and all keys are recalculated and transferred into the new slice.

* For hashing, specialized xxhash implementations are used that match the size of the key type where available
24 changes: 17 additions & 7 deletions benchmarks/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/cornelk/hashmap"
)

const benchmarkItemCount = 512
const benchmarkItemCount = 1024

func setupHashMap(b *testing.B) *hashmap.HashMap[uintptr, uintptr] {
b.Helper()
Expand All @@ -19,8 +19,6 @@ func setupHashMap(b *testing.B) *hashmap.HashMap[uintptr, uintptr] {
for i := uintptr(0); i < benchmarkItemCount; i++ {
m.Set(i, i)
}

b.ResetTimer()
return m
}

Expand All @@ -45,7 +43,6 @@ func setupHashMapString(b *testing.B) (*hashmap.HashMap[string, string], []strin
keys[i] = s
}

b.ResetTimer()
return m, keys
}

Expand All @@ -57,7 +54,6 @@ func setupGoMap(b *testing.B) map[uintptr]uintptr {
m[i] = i
}

b.ResetTimer()
return m
}

Expand All @@ -69,7 +65,6 @@ func setupGoSyncMap(b *testing.B) *sync.Map {
m.Store(i, i)
}

b.ResetTimer()
return m
}

Expand All @@ -83,12 +78,12 @@ func setupGoMapString(b *testing.B) (map[string]string, []string) {
m[s] = s
keys[i] = s
}
b.ResetTimer()
return m, keys
}

func BenchmarkReadHashMapUint(b *testing.B) {
m := setupHashMap(b)
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Expand All @@ -105,6 +100,7 @@ func BenchmarkReadHashMapUint(b *testing.B) {
func BenchmarkReadHashMapWithWritesUint(b *testing.B) {
m := setupHashMap(b)
var writer uintptr
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
// use 1 thread as writer
Expand All @@ -129,6 +125,7 @@ func BenchmarkReadHashMapWithWritesUint(b *testing.B) {

func BenchmarkReadHashMapString(b *testing.B) {
m, keys := setupHashMapString(b)
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Expand All @@ -145,6 +142,7 @@ func BenchmarkReadHashMapString(b *testing.B) {

func BenchmarkReadHashMapInterface(b *testing.B) {
m := setupHashMap(b)
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Expand All @@ -160,6 +158,7 @@ func BenchmarkReadHashMapInterface(b *testing.B) {

func BenchmarkReadHaxMapUint(b *testing.B) {
m := setupHaxMap(b)
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Expand All @@ -176,6 +175,7 @@ func BenchmarkReadHaxMapUint(b *testing.B) {
func BenchmarkReadHaxMapWithWritesUint(b *testing.B) {
m := setupHaxMap(b)
var writer uintptr
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
// use 1 thread as writer
Expand All @@ -200,6 +200,7 @@ func BenchmarkReadHaxMapWithWritesUint(b *testing.B) {

func BenchmarkReadGoMapUintUnsafe(b *testing.B) {
m := setupGoMap(b)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
for i := uintptr(0); i < benchmarkItemCount; i++ {
Expand All @@ -215,6 +216,7 @@ func BenchmarkReadGoMapUintUnsafe(b *testing.B) {
func BenchmarkReadGoMapUintMutex(b *testing.B) {
m := setupGoMap(b)
l := &sync.RWMutex{}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
for i := uintptr(0); i < benchmarkItemCount; i++ {
Expand All @@ -233,6 +235,7 @@ func BenchmarkReadGoMapWithWritesUintMutex(b *testing.B) {
m := setupGoMap(b)
l := &sync.RWMutex{}
var writer uintptr
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
// use 1 thread as writer
Expand Down Expand Up @@ -261,6 +264,7 @@ func BenchmarkReadGoMapWithWritesUintMutex(b *testing.B) {

func BenchmarkReadGoSyncMapUint(b *testing.B) {
m := setupGoSyncMap(b)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
for i := uintptr(0); i < benchmarkItemCount; i++ {
Expand All @@ -276,6 +280,7 @@ func BenchmarkReadGoSyncMapUint(b *testing.B) {
func BenchmarkReadGoSyncMapWithWritesUint(b *testing.B) {
m := setupGoSyncMap(b)
var writer uintptr
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
// use 1 thread as writer
Expand All @@ -300,6 +305,7 @@ func BenchmarkReadGoSyncMapWithWritesUint(b *testing.B) {

func BenchmarkReadGoMapStringUnsafe(b *testing.B) {
m, keys := setupGoMapString(b)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
for i := 0; i < benchmarkItemCount; i++ {
Expand All @@ -316,6 +322,7 @@ func BenchmarkReadGoMapStringUnsafe(b *testing.B) {
func BenchmarkReadGoMapStringMutex(b *testing.B) {
m, keys := setupGoMapString(b)
l := &sync.RWMutex{}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
for i := 0; i < benchmarkItemCount; i++ {
Expand All @@ -333,6 +340,7 @@ func BenchmarkReadGoMapStringMutex(b *testing.B) {

func BenchmarkWriteHashMapUint(b *testing.B) {
m := hashmap.New[uintptr, uintptr]()
b.ResetTimer()

for n := 0; n < b.N; n++ {
for i := uintptr(0); i < benchmarkItemCount; i++ {
Expand All @@ -344,6 +352,7 @@ func BenchmarkWriteHashMapUint(b *testing.B) {
func BenchmarkWriteGoMapMutexUint(b *testing.B) {
m := make(map[uintptr]uintptr)
l := &sync.RWMutex{}
b.ResetTimer()

for n := 0; n < b.N; n++ {
for i := uintptr(0); i < benchmarkItemCount; i++ {
Expand All @@ -356,6 +365,7 @@ func BenchmarkWriteGoMapMutexUint(b *testing.B) {

func BenchmarkWriteGoSyncMapUint(b *testing.B) {
m := &sync.Map{}
b.ResetTimer()

for n := 0; n < b.N; n++ {
for i := uintptr(0); i < benchmarkItemCount; i++ {
Expand Down
6 changes: 4 additions & 2 deletions benchmarks/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ module github.com/cornelk/hashmap/benchmarks

go 1.19

replace github.com/cornelk/hashmap => ../

require (
github.com/alphadose/haxmap v0.1.1-0.20220808155550-bc3b9a6adfc4
github.com/cornelk/hashmap v1.0.4-0.20220810053739-f969cadbb3f0
github.com/alphadose/haxmap v0.2.1-0.20220828165710-add810974d4f
github.com/cornelk/hashmap v1.0.5-0.20220828215932-152772b42884
)

require github.com/cespare/xxhash v1.1.0 // indirect
6 changes: 2 additions & 4 deletions benchmarks/go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alphadose/haxmap v0.1.1-0.20220808155550-bc3b9a6adfc4 h1:E78W2LuJFjDKpr0a0xFK9w0rawO4wtmTdcbRX5H11mg=
github.com/alphadose/haxmap v0.1.1-0.20220808155550-bc3b9a6adfc4/go.mod h1:68nFwlFwh/HEilKSdlNOKnd1PwOy8EUnKUtZH2TdVb8=
github.com/alphadose/haxmap v0.2.1-0.20220828165710-add810974d4f h1:up6qKu3lIXQ4H3AyJovBy0Tp6Ug++aveneLrUv7TyNo=
github.com/alphadose/haxmap v0.2.1-0.20220828165710-add810974d4f/go.mod h1:Fu37Wlmj7cR++vSLgRTu3fGy8wpjHGmMypM2aclkc1A=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cornelk/hashmap v1.0.4-0.20220810053739-f969cadbb3f0 h1:jed6FFAMwRgg57mgmqH/kfSsjtXL8ocT3f2zE/8vumg=
github.com/cornelk/hashmap v1.0.4-0.20220810053739-f969cadbb3f0/go.mod h1:T9KPzj/SjvCwiF51NdfFMmqHhRm6cl2slzzgmmgDnUw=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
Expand Down

0 comments on commit e90d44f

Please sign in to comment.