proto: specialize map<string, string> for performance
Background: https://github.com/golang/protobuf/issues/624
Serialization of map fields currently hits a slow, reflection-based path. This is understandable for the general case (nested messages are hard), but there are some special cases where maps get used a lot and a fast path would be very valuable.
My specific use case is a map<string, string> used to represent arbitrary human-meaningful metadata in a distributed monitoring system (similar to Prometheus). Benchmarking shows that our tooling is spending a material amount of CPU time in the closures returned from proto.makeMapMarshaler.
fwiw, I wrote a quick benchmark for comparing against gogo faster:
func BenchmarkMarshalSSFSpan(b *testing.B) {
s := &ssf.SSFSpan{
Tags: make(map[string]string),
}
for i := 0; i < 20; i++ {
s.Tags[strconv.Itoa(i)] = strconv.Itoa(i * 1000)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, err := proto.Marshal(s)
if err != nil {
panic(err)
}
}
}
SSFSpan is from https://github.com/stripe/veneur/blob/master/ssf/sample.proto.
BenchmarkMarshalSSFSpan-4 100000 15003 ns/op 3952 B/op 165 allocs/op
BenchmarkMarshalSSFSpan-4 500000 2413 ns/op 240 B/op 1 allocs/op
Top is protoc-gen-go, bottom is protoc-gen-gogofaster.
If nobody is working on this I would like to get assigned to this.
Making a note that this should probably also handle map[string]*Message.