mirror of
https://github.com/golang/go
synced 2024-11-15 06:50:32 -07:00
5bba5b256c
The existing implementation of traceMap is a hash map with a fixed bucket table size which scales poorly with the number of elements added to the map. After a few thousands elements are in the map, it tends to fall over. Furthermore, cleaning up the trace map is currently non-preemptible, without very good reason. This change replaces the traceMap implementation with a simple append-only concurrent hash-trie. The data structure is incredibly simple and does not suffer at all from the same scaling issues. Because the traceMap no longer has a lock, and the traceRegionAlloc it embeds is not thread-safe, we have to push that lock down. While we're here, this change also makes the fast path for the traceRegionAlloc lock-free. This may not be inherently faster due to contention on the atomic add, but it creates an easy path to sharding the main allocation buffer to reduce contention in the future. (We might want to also consider a fully thread-local allocator that covers both string and stack tables. The only reason a thread-local allocator isn't feasible right now is because each of these has their own region, but we could certainly group all them together.) Change-Id: I8c06d42825c326061a1b8569e322afc4bc2a513a Reviewed-on: https://go-review.googlesource.com/c/go/+/570035 Reviewed-by: Carlos Amedee <carlos@golang.org> Auto-Submit: Michael Knyszek <mknyszek@google.com> TryBot-Bypass: Michael Knyszek <mknyszek@google.com> Reviewed-by: David Chase <drchase@google.com>
90 lines
1.8 KiB
Go
90 lines
1.8 KiB
Go
// Copyright 2024 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package runtime_test
|
|
|
|
import (
|
|
. "runtime"
|
|
"strconv"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func TestTraceMap(t *testing.T) {
|
|
var m TraceMap
|
|
|
|
// Try all these operations multiple times between resets, to make sure
|
|
// we're resetting properly.
|
|
for range 3 {
|
|
var d = [...]string{
|
|
"a",
|
|
"b",
|
|
"aa",
|
|
"ab",
|
|
"ba",
|
|
"bb",
|
|
}
|
|
for i, s := range d {
|
|
id, inserted := m.PutString(s)
|
|
if !inserted {
|
|
t.Errorf("expected to have inserted string %q, but did not", s)
|
|
}
|
|
if id != uint64(i+1) {
|
|
t.Errorf("expected string %q to have ID %d, but got %d instead", s, i+1, id)
|
|
}
|
|
}
|
|
for i, s := range d {
|
|
id, inserted := m.PutString(s)
|
|
if inserted {
|
|
t.Errorf("inserted string %q, but expected to have not done so", s)
|
|
}
|
|
if id != uint64(i+1) {
|
|
t.Errorf("expected string %q to have ID %d, but got %d instead", s, i+1, id)
|
|
}
|
|
}
|
|
m.Reset()
|
|
}
|
|
}
|
|
|
|
func TestTraceMapConcurrent(t *testing.T) {
|
|
var m TraceMap
|
|
|
|
var wg sync.WaitGroup
|
|
for i := range 3 {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
|
|
si := strconv.Itoa(i)
|
|
var d = [...]string{
|
|
"a" + si,
|
|
"b" + si,
|
|
"aa" + si,
|
|
"ab" + si,
|
|
"ba" + si,
|
|
"bb" + si,
|
|
}
|
|
ids := make([]uint64, 0, len(d))
|
|
for _, s := range d {
|
|
id, inserted := m.PutString(s)
|
|
if !inserted {
|
|
t.Errorf("expected to have inserted string %q, but did not", s)
|
|
}
|
|
ids = append(ids, id)
|
|
}
|
|
for i, s := range d {
|
|
id, inserted := m.PutString(s)
|
|
if inserted {
|
|
t.Errorf("inserted string %q, but expected to have not done so", s)
|
|
}
|
|
if id != ids[i] {
|
|
t.Errorf("expected string %q to have ID %d, but got %d instead", s, ids[i], id)
|
|
}
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
m.Reset()
|
|
}
|