An interesting fact is that Go maps are optimized for certain integer types. Quite a while ago (go 1.13) I did some testing (haven’t done it recently though), and as a result added this comment to one of my codebases:
// go maps are optimized for only certain int types:
// -- results as of go 1.13 on my slow laptop --
// BenchmarkInt 297391227 3.99 ns/op
// BenchmarkInt8 68107761 17.90 ns/op
// BenchmarkInt16 65628482 18.30 ns/op
// BenchmarkInt32 292725417 4.08 ns/op
// BenchmarkInt64 293602374 4.11 ns/op
// BenchmarkUInt 298711089 3.99 ns/op
// BenchmarkUInt8 68173198 17.80 ns/op
// BenchmarkUInt16 67566312 18.10 ns/op
// BenchmarkUInt32 298597942 3.99 ns/op
// BenchmarkUInt64 300239860 4.02 ns/op
// Since we would /want/ to use uint8 here, use uint32 instead
// Ugly and wasteful, but quite a bit faster for now...
subtrees map[uint32]*SomeStruct
Using one of the optimized map key types might improve the benchmarks a bit. So uint16 in the article may be a poor choice, though I doubt it would change the overall outcome (likely still the slowest).
I thought maps were optimized for 2-byte sized objects as well but I guess that’s not the case.
Using a 4-byte key like int32 does make them go faster.
Reset becomes a bit slower, but Get and Set see some improvements; I only did a lazy run without a high -count and benchstat, so to get the exact % someone would need to do a couple of extra steps.
But if no new map specializations were added, your results are probably still relevant.
if you were feeling brave, you could use a random sequence number, and skip initializing the memory. (the sparse set it’s replacing can use uninitialized memory)
For my use case, I wouldn’t mind doing a zeroing even if the language would implicitly do this for me. That array is initialized once and then re-used thousands of times. So I need the get/set/reset operations to be as fast as possible, given the constraints.
An interesting fact is that Go maps are optimized for certain integer types. Quite a while ago (go 1.13) I did some testing (haven’t done it recently though), and as a result added this comment to one of my codebases:
Using one of the optimized map key types might improve the benchmarks a bit. So
uint16
in the article may be a poor choice, though I doubt it would change the overall outcome (likely still the slowest).I thought maps were optimized for 2-byte sized objects as well but I guess that’s not the case. Using a 4-byte key like int32 does make them go faster. Reset becomes a bit slower, but Get and Set see some improvements; I only did a lazy run without a high -count and benchstat, so to get the exact % someone would need to do a couple of extra steps. But if no new map specializations were added, your results are probably still relevant.
if you were feeling brave, you could use a random sequence number, and skip initializing the memory. (the sparse set it’s replacing can use uninitialized memory)
For my use case, I wouldn’t mind doing a zeroing even if the language would implicitly do this for me. That array is initialized once and then re-used thousands of times. So I need the get/set/reset operations to be as fast as possible, given the constraints.