Skip to content

Instantly share code, notes, and snippets.

@davecheney
Last active May 10, 2024 18:30
Show Gist options
  • Save davecheney/3be245c92b61e5045f75 to your computer and use it in GitHub Desktop.
Save davecheney/3be245c92b61e5045f75 to your computer and use it in GitHub Desktop.
Which is faster ? map[string]bool or map[string]struct{} ?
package bm
import (
"testing"
)
var mb = map[string]bool{
"alpha": true,
"beta": true,
"gamma": true,
"delta": true,
"epsilon": true,
"zeta": true,
"eta": true,
"theta": true,
"iota": true,
"kappa": true,
"lambda": true,
"mu": true,
"nu": true,
"xi": true,
"omicron": true,
"pi": true,
"rho": true,
"sigma": true,
"tau": true,
"upsilon": true,
"phi": true,
"chi": true,
"psi": true,
"omega": true,
}
var ms = map[string]struct{}{
"alpha": struct{}{},
"beta": struct{}{},
"gamma": struct{}{},
"delta": struct{}{},
"epsilon": struct{}{},
"zeta": struct{}{},
"eta": struct{}{},
"theta": struct{}{},
"iota": struct{}{},
"kappa": struct{}{},
"lambda": struct{}{},
"mu": struct{}{},
"nu": struct{}{},
"xi": struct{}{},
"omicron": struct{}{},
"pi": struct{}{},
"rho": struct{}{},
"sigma": struct{}{},
"tau": struct{}{},
"upsilon": struct{}{},
"phi": struct{}{},
"chi": struct{}{},
"psi": struct{}{},
"omega": struct{}{},
}
var bb bool
func BenchmarkMapBool(B *testing.B) {
var b bool
for i := 0; i < B.N; i++ {
b = mb["alpha"]
b = mb["gamma"]
b = mb["mu"]
b = mb["pi"]
b = mb["omega"]
}
bb = b
}
func BenchmarkMapStruct(B *testing.B) {
var b bool
for i := 0; i < B.N; i++ {
_, b = ms["alpha"]
_, b = ms["gamma"]
_, b = ms["mu"]
_, b = ms["pi"]
_, b = ms["omega"]
}
bb = b
}
### update ### Cyrill Schumacher pointed out that my first attempt
was incorrect as I was using the mb not the ms map in the second
test. The updated results are below.
% go test -bench=. -benchtime=10s
testing: warning: no tests to run
PASS
BenchmarkMapBool 100000000 127 ns/op
BenchmarkMapStruct 100000000 138 ns/op
ok bm 26.802s
% go test -bench=. -benchtime=10s
testing: warning: no tests to run
PASS
BenchmarkMapBool 100000000 127 ns/op
BenchmarkMapStruct 100000000 133 ns/op
ok bm 26.300s
% go test -bench=. -benchtime=10s
testing: warning: no tests to run
PASS
BenchmarkMapBool 100000000 128 ns/op
BenchmarkMapStruct 100000000 142 ns/op
ok bm 27.383s
% go test -bench=. -benchtime=10s
testing: warning: no tests to run
PASS
BenchmarkMapBool 100000000 110 ns/op
BenchmarkMapStruct 100000000 128 ns/op
Conclusion: they are effectively the same, use whichever one
makes your code clearer. There is a small advantage to the
map[string]bool type, but the difference is below the variance
in the test runs. It is also worth noting that the map[string]struct{}
version is considered to consume less memory* but this was not
tested in this benchmark.
*you're welcome to extend this benchmark to prove/disprove this.
@mipearson
Copy link

I would have thought that every b = assignment except the last would have been optimised out by the compiler. I see you've added the bb = b to ensure that the whole thing doesn't just get optimised out.

@mipearson
Copy link

@trusch
Copy link

trusch commented Aug 7, 2018

To the memory discussion:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    variant1 := make(map[string]bool)
    variant2 := make(map[string]struct{})
    for i := 0; i < 1<<16; i++ {
        key := fmt.Sprintf("%v", i)
        variant1[key] = true
        variant2[key] = struct{}{}
    }
    size1 := unsafe.Sizeof(variant1)
    size2 := unsafe.Sizeof(variant2)
    for k, v := range variant1 {
        size1 += unsafe.Sizeof(k)
        size1 += unsafe.Sizeof(v)
    }
    for k, v := range variant2 {
        size2 += unsafe.Sizeof(k)
        size2 += unsafe.Sizeof(v)
    }
    fmt.Printf("bool variant  : %v bytes\n", size1)
    fmt.Printf("struct variant: %v bytes\n", size2)
    // bool variant  : 1114120 bytes
    // struct variant: 1048584 bytes
}

@0x-2a
Copy link

0x-2a commented Mar 15, 2019

@trusch thank you for this. size2 += unsafe.Sizeof(v) will always evaluate to size2 += 0. If you subtract the key sizes it will show that the struct variant allocates 0 extra bytes for the values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment