From f5f5a8b6209f84961687d993b93ea0d397f5d5bf Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 3 Apr 2014 19:05:17 -0400 Subject: 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 --- src/pkg/runtime/map_test.go | 37 +++++++++++++++++++++++++++++++++++++ src/pkg/runtime/string.goc | 19 +++++++++++++++++++ 2 files changed, 56 insertions(+) (limited to 'src/pkg/runtime') 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; -- cgit v1.3-5-g9baa