diff options
| author | Russ Cox <rsc@golang.org> | 2014-04-03 19:05:17 -0400 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2014-04-03 19:05:17 -0400 |
| commit | f5f5a8b6209f84961687d993b93ea0d397f5d5bf (patch) | |
| tree | 69a2398e1615a1d8d7c4aac5725274a414eb3eaf /src/pkg/runtime | |
| parent | 0e1b6bb5470701090cd8dadacc6eb5074a86cf82 (diff) | |
| download | go-f5f5a8b6209f84961687d993b93ea0d397f5d5bf.tar.xz | |
cmd/gc, runtime: optimize map[string] lookup from []byte key
Brad has been asking for this for a while.
I have resisted because I wanted to find a more general way to
do this, one that would keep the performance of code introducing
variables the same as the performance of code that did not.
(See golang.org/issue/3512#c20).
I have not found the more general way, and recent changes to
remove ambiguously live temporaries have blown away the
property I was trying to preserve, so that's no longer a reason
not to make the change.
Fixes #3512.
LGTM=iant
R=iant
CC=bradfitz, golang-codereviews, khr, r
https://golang.org/cl/83740044
Diffstat (limited to 'src/pkg/runtime')
| -rw-r--r-- | src/pkg/runtime/map_test.go | 37 | ||||
| -rw-r--r-- | src/pkg/runtime/string.goc | 19 |
2 files changed, 56 insertions, 0 deletions
diff --git a/src/pkg/runtime/map_test.go b/src/pkg/runtime/map_test.go index 9c703ba362..e4e8383493 100644 --- a/src/pkg/runtime/map_test.go +++ b/src/pkg/runtime/map_test.go @@ -438,3 +438,40 @@ func TestMapIterOrder(t *testing.T) { } } } + +func TestMapStringBytesLookup(t *testing.T) { + // Use large string keys to avoid small-allocation coalescing, + // which can cause AllocsPerRun to report lower counts than it should. + m := map[string]int{ + "1000000000000000000000000000000000000000000000000": 1, + "2000000000000000000000000000000000000000000000000": 2, + } + buf := []byte("1000000000000000000000000000000000000000000000000") + if x := m[string(buf)]; x != 1 { + t.Errorf(`m[string([]byte("1"))] = %d, want 1`, x) + } + buf[0] = '2' + if x := m[string(buf)]; x != 2 { + t.Errorf(`m[string([]byte("2"))] = %d, want 2`, x) + } + + var x int + n := testing.AllocsPerRun(100, func() { + x += m[string(buf)] + }) + if n != 0 { + t.Errorf("AllocsPerRun for m[string(buf)] = %v, want 0", n) + } + + x = 0 + n = testing.AllocsPerRun(100, func() { + y, ok := m[string(buf)] + if !ok { + panic("!ok") + } + x += y + }) + if n != 0 { + t.Errorf("AllocsPerRun for x,ok = m[string(buf)] = %v, want 0", n) + } +} diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc index 89b9130c08..97a69d07b1 100644 --- a/src/pkg/runtime/string.goc +++ b/src/pkg/runtime/string.goc @@ -294,6 +294,25 @@ func slicebytetostring(b Slice) (s String) { runtime·memmove(s.str, b.array, s.len); } +func slicebytetostringtmp(b Slice) (s String) { + void *pc; + + if(raceenabled) { + pc = runtime·getcallerpc(&b); + runtime·racereadrangepc(b.array, b.len, pc, runtime·slicebytetostringtmp); + } + + // Return a "string" referring to the actual []byte bytes. + // This is only for use by internal compiler optimizations + // that know that the string form will be discarded before + // the calling goroutine could possibly modify the original + // slice or synchronize with another goroutine. + // Today, the only such case is a m[string(k)] lookup where + // m is a string-keyed map and k is a []byte. + s.str = b.array; + s.len = b.len; +} + func stringtoslicebyte(s String) (b Slice) { uintptr cap; |
