Changes the behavior of `AttrStore.SetAttrs()` to first verify
that the attributes haven't changed using a read-only lock before
obtaining a write lock to update the attributes.
Since the write lock serializes access, the previous insert time
sufferred lock contention when inserting a lot of duplicate
attributes. With the `RWMutex`, reads can be done in parallel so
multiple requests don't block each other.
Below is a simple benchmark showing the performance at 1, 5, & 10
goroutines:
BenchmarkAttrStore_Duplicate 100000 137120 ns/op 8428 B/op 76 allocs/op
BenchmarkAttrStore_Duplicate-5 100000 417459 ns/op 8431 B/op 76 allocs/op
BenchmarkAttrStore_Duplicate-10 100000 139466 ns/op 8433 B/op 76 allocs/op
BenchmarkAttrStore_Duplicate 20000000 703 ns/op 368 B/op 5 allocs/op
BenchmarkAttrStore_Duplicate-5 100000000 213 ns/op 368 B/op 5 allocs/op
BenchmarkAttrStore_Duplicate-10 100000000 223 ns/op 368 B/op 5 allocs/op
this affects the scanner/parser and makes it produce signed ints rather than
uints, and so it affects all code that is expecting unsighed ints to come out of
the parser.
This commit refactors the profile and bitmap attribute stores so
that they share the same code. There is a new `AttrStore` which
associates key/value pairs with a `uint64` identifier.
The previous JSON encoding has been fixed to use protobufs which
fixes encoding issues for int64.