mirror of
https://github.com/golang/go
synced 2024-11-18 15:44:41 -07:00
cmd/gc: don't copy string in range []byte(str)
Using benchmark from the issue: benchmark old ns/op new ns/op delta BenchmarkRangeStringCast 2162 1152 -46.72% benchmark old allocs new allocs delta BenchmarkRangeStringCast 1 0 -100.00% Fixes #2204 Change-Id: I92c5edd2adca4a7b6fba00713a581bf49dc59afe Reviewed-on: https://go-review.googlesource.com/3790 Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
70321df02f
commit
71be013842
@ -38,6 +38,7 @@ char *runtimeimport =
|
||||
"func @\"\".slicebytetostringtmp (? []byte) (? string)\n"
|
||||
"func @\"\".slicerunetostring (? []rune) (? string)\n"
|
||||
"func @\"\".stringtoslicebyte (? string) (? []byte)\n"
|
||||
"func @\"\".stringtoslicebytetmp (? string) (? []byte)\n"
|
||||
"func @\"\".stringtoslicerune (? string) (? []rune)\n"
|
||||
"func @\"\".stringiter (? string, ? int) (? int)\n"
|
||||
"func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n"
|
||||
|
@ -467,6 +467,7 @@ enum
|
||||
OARRAYBYTESTRTMP, // string(bytes) ephemeral
|
||||
OARRAYRUNESTR, // string(runes)
|
||||
OSTRARRAYBYTE, // []byte(s)
|
||||
OSTRARRAYBYTETMP, // []byte(s) ephemeral
|
||||
OSTRARRAYRUNE, // []rune(s)
|
||||
OAS, // x = y or x := y
|
||||
OAS2, // x, y, z = xx, yy, zz
|
||||
|
@ -757,6 +757,10 @@ orderstmt(Node *n, Order *order)
|
||||
default:
|
||||
fatal("orderstmt range %T", n->type);
|
||||
case TARRAY:
|
||||
// Mark []byte(str) range expression to reuse string backing storage.
|
||||
// It is safe because the storage cannot be mutated.
|
||||
if(n->right->op == OSTRARRAYBYTE)
|
||||
n->right->op = OSTRARRAYBYTETMP;
|
||||
if(count(n->list) < 2 || isblank(n->list->next->n)) {
|
||||
// for i := range x will only use x once, to compute len(x).
|
||||
// No need to copy it.
|
||||
|
@ -52,6 +52,7 @@ func slicebytetostring(*[32]byte, []byte) string
|
||||
func slicebytetostringtmp([]byte) string
|
||||
func slicerunetostring([]rune) string
|
||||
func stringtoslicebyte(string) []byte
|
||||
func stringtoslicebytetmp(string) []byte
|
||||
func stringtoslicerune(string) []rune
|
||||
func stringiter(string, int) int
|
||||
func stringiter2(string, int) (retk int, retv rune)
|
||||
|
@ -1406,6 +1406,11 @@ walkexpr(Node **np, NodeList **init)
|
||||
n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING]));
|
||||
goto ret;
|
||||
|
||||
case OSTRARRAYBYTETMP:
|
||||
// stringtoslicebytetmp(string) []byte;
|
||||
n = mkcall("stringtoslicebytetmp", n->type, init, conv(n->left, types[TSTRING]));
|
||||
goto ret;
|
||||
|
||||
case OSTRARRAYRUNE:
|
||||
// stringtoslicerune(string) []rune
|
||||
n = mkcall("stringtoslicerune", n->type, init, n->left);
|
||||
|
@ -135,6 +135,18 @@ func stringtoslicebyte(s string) []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
func stringtoslicebytetmp(s string) []byte {
|
||||
// Return a slice referring to the actual string bytes.
|
||||
// This is only for use by internal compiler optimizations
|
||||
// that know that the slice won't be mutated.
|
||||
// The only such case today is:
|
||||
// for i, c := range []byte(str)
|
||||
|
||||
str := (*stringStruct)(unsafe.Pointer(&s))
|
||||
ret := slice{array: (*byte)(str.str), len: uint(str.len), cap: uint(str.len)}
|
||||
return *(*[]byte)(unsafe.Pointer(&ret))
|
||||
}
|
||||
|
||||
func stringtoslicerune(s string) []rune {
|
||||
// two passes.
|
||||
// unlike slicerunetostring, no race because strings are immutable.
|
||||
|
@ -221,3 +221,17 @@ func TestIntStringAllocs(t *testing.T) {
|
||||
t.Fatalf("want 0 allocs, got %v", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangeStringCast(t *testing.T) {
|
||||
s := "abc"
|
||||
n := testing.AllocsPerRun(1000, func() {
|
||||
for i, c := range []byte(s) {
|
||||
if c != s[i] {
|
||||
t.Fatalf("want '%c' at pos %v, got '%c'", s[i], i, c)
|
||||
}
|
||||
}
|
||||
})
|
||||
if n != 0 {
|
||||
t.Fatalf("want 0 allocs, got %v", n)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user