void
order(Node *fn)
{
+ char s[50];
+
+ if(debug['W'] > 1) {
+ snprint(s, sizeof(s), "\nbefore order %S", fn->nname->sym);
+ dumplist(s, fn->nbody);
+ }
+
orderblock(&fn->nbody);
}
Node *n;
NodeList *mark, *l;
Type *t;
- int lno;
+ int lno, haslit, hasbyte;
n = *np;
if(n == N)
t->type = types[TSTRING];
n->alloc = ordertemp(t, order, 0);
}
+
+ // Mark string(byteSlice) arguments to reuse byteSlice backing
+ // buffer during conversion. String concatenation does not
+ // memorize the strings for later use, so it is safe.
+ // However, we can do it only if there is at least one non-empty string literal.
+ // Otherwise if all other arguments are empty strings,
+ // concatstrings will return the reference to the temp string
+ // to the caller.
+ hasbyte = 0;
+ haslit = 0;
+ for(l=n->list; l != nil; l=l->next) {
+ hasbyte |= l->n->op == OARRAYBYTESTR;
+ haslit |= l->n->op == OLITERAL && l->n->val.u.sval->len != 0;
+ }
+ if(haslit && hasbyte) {
+ for(l=n->list; l != nil; l=l->next) {
+ if(l->n->op == OARRAYBYTESTR)
+ l->n->op = OARRAYBYTESTRTMP;
+ }
+ }
break;
case OINDEXMAP:
}
}
+func TestStringConcatenationAllocs(t *testing.T) {
+ n := testing.AllocsPerRun(1e3, func() {
+ b := make([]byte, 10)
+ for i := 0; i < 10; i++ {
+ b[i] = byte(i) + '0'
+ }
+ s := "foo" + string(b)
+ if want := "foo0123456789"; s != want {
+ t.Fatalf("want %v, got %v", want, s)
+ }
+ })
+ // Only string concatenation allocates.
+ if n != 1 {
+ t.Fatalf("want 1 allocation, got %v", n)
+ }
+}
+
var mallocSink uintptr
func BenchmarkMalloc8(b *testing.B) {
// 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
+ // First such case is a m[string(k)] lookup where
// m is a string-keyed map and k is a []byte.
+ // Second such case is "<"+string(b)+">" concatenation where b is []byte.
if raceenabled && len(b) > 0 {
racereadrangepc(unsafe.Pointer(&b[0]),