Commit bace9523 authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

runtime: smarter slice grow

When growing slice take into account size of the allocated memory block.
Also apply the same optimization to string->[]byte conversion.
Fixes #6307.

benchmark                    old ns/op    new ns/op    delta
BenchmarkAppendGrowByte        4541036      4434108   -2.35%
BenchmarkAppendGrowString     59885673     44813604  -25.17%

LGTM=khr
R=khr
CC=golang-codereviews, iant, rsc
https://golang.org/cl/53340044
parent 496c030c
...@@ -19,6 +19,25 @@ func BenchmarkAppend(b *testing.B) { ...@@ -19,6 +19,25 @@ func BenchmarkAppend(b *testing.B) {
} }
} }
func BenchmarkAppendGrowByte(b *testing.B) {
for i := 0; i < b.N; i++ {
var x []byte
for j := 0; j < 1<<20; j++ {
x = append(x, byte(j))
}
}
}
func BenchmarkAppendGrowString(b *testing.B) {
var s string
for i := 0; i < b.N; i++ {
var x []string
for j := 0; j < 1<<20; j++ {
x = append(x, s)
}
}
}
func benchmarkAppendBytes(b *testing.B, length int) { func benchmarkAppendBytes(b *testing.B, length int) {
b.StopTimer() b.StopTimer()
x := make([]byte, 0, N) x := make([]byte, 0, N)
......
...@@ -273,6 +273,7 @@ extern MStats mstats; ...@@ -273,6 +273,7 @@ extern MStats mstats;
// making new objects in class i // making new objects in class i
int32 runtime·SizeToClass(int32); int32 runtime·SizeToClass(int32);
uintptr runtime·roundupsize(uintptr);
extern int32 runtime·class_to_size[NumSizeClasses]; extern int32 runtime·class_to_size[NumSizeClasses];
extern int32 runtime·class_to_allocnpages[NumSizeClasses]; extern int32 runtime·class_to_allocnpages[NumSizeClasses];
extern int8 runtime·size_to_class8[1024/8 + 1]; extern int8 runtime·size_to_class8[1024/8 + 1];
......
...@@ -162,3 +162,18 @@ dump: ...@@ -162,3 +162,18 @@ dump:
} }
runtime·throw("InitSizes failed"); runtime·throw("InitSizes failed");
} }
// Returns size of the memory block that mallocgc will allocate if you ask for the size.
uintptr
runtime·roundupsize(uintptr size)
{
if(size < MaxSmallSize) {
if(size <= 1024-8)
return runtime·class_to_size[runtime·size_to_class8[(size+7)>>3]];
else
return runtime·class_to_size[runtime·size_to_class128[(size-1024+127) >> 7]];
}
if(size + PageSize < size)
return size;
return ROUND(size, PageSize);
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "typekind.h" #include "typekind.h"
#include "malloc.h" #include "malloc.h"
#include "race.h" #include "race.h"
#include "stack.h"
#include "../../cmd/ld/textflag.h" #include "../../cmd/ld/textflag.h"
enum enum
...@@ -92,26 +93,53 @@ runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret) ...@@ -92,26 +93,53 @@ runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret)
static void static void
growslice1(SliceType *t, Slice x, intgo newcap, Slice *ret) growslice1(SliceType *t, Slice x, intgo newcap, Slice *ret)
{ {
intgo m; intgo newcap1;
uintptr capmem, lenmem;
int32 flag;
Type *typ;
typ = t->elem;
if(typ->size == 0) {
*ret = x;
ret->cap = newcap;
return;
}
m = x.cap; newcap1 = x.cap;
// Using newcap directly for m+m < newcap handles // Using newcap directly for m+m < newcap handles
// both the case where m == 0 and also the case where // both the case where m == 0 and also the case where
// m+m/4 wraps around, in which case the loop // m+m/4 wraps around, in which case the loop
// below might never terminate. // below might never terminate.
if(m+m < newcap) if(newcap1+newcap1 < newcap)
m = newcap; newcap1 = newcap;
else { else {
do { do {
if(x.len < 1024) if(x.len < 1024)
m += m; newcap1 += newcap1;
else else
m += m/4; newcap1 += newcap1/4;
} while(m < newcap); } while(newcap1 < newcap);
} }
makeslice1(t, x.len, m, ret);
runtime·memmove(ret->array, x.array, ret->len * t->elem->size); if(newcap1 > MaxMem/typ->size)
runtime·panicstring("growslice: cap out of range");
capmem = runtime·roundupsize(newcap1*typ->size);
flag = FlagNoZero;
if(typ->kind&KindNoPointers)
flag |= FlagNoScan;
// Here we allocate with FlagNoZero but potentially w/o FlagNoScan,
// GC must not see this blocks until memclr below.
m->locks++;
ret->array = runtime·mallocgc(capmem, (uintptr)typ|TypeInfo_Array, flag);
ret->len = x.len;
ret->cap = capmem/typ->size;
lenmem = x.len*typ->size;
runtime·memmove(ret->array, x.array, lenmem);
runtime·memclr(ret->array+lenmem, capmem-lenmem);
m->locks--;
if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
g->stackguard0 = StackPreempt;
} }
// copy(to any, fr any, wid uintptr) int // copy(to any, fr any, wid uintptr) int
......
...@@ -78,6 +78,7 @@ runtime·gostringn(byte *str, intgo l) ...@@ -78,6 +78,7 @@ runtime·gostringn(byte *str, intgo l)
return s; return s;
} }
// used by cmd/cgo
Slice Slice
runtime·gobytes(byte *p, intgo n) runtime·gobytes(byte *p, intgo n)
{ {
...@@ -278,10 +279,15 @@ func slicebytetostring(b Slice) (s String) { ...@@ -278,10 +279,15 @@ func slicebytetostring(b Slice) (s String) {
} }
func stringtoslicebyte(s String) (b Slice) { func stringtoslicebyte(s String) (b Slice) {
b.array = runtime·mallocgc(s.len, 0, FlagNoScan|FlagNoZero); uintptr cap;
cap = runtime·roundupsize(s.len);
b.array = runtime·mallocgc(cap, 0, FlagNoScan|FlagNoZero);
b.len = s.len; b.len = s.len;
b.cap = s.len; b.cap = cap;
runtime·memmove(b.array, s.str, s.len); runtime·memmove(b.array, s.str, s.len);
if(cap != b.len)
runtime·memclr(b.array+b.len, cap-b.len);
} }
func slicerunetostring(b Slice) (s String) { func slicerunetostring(b Slice) (s String) {
...@@ -316,6 +322,7 @@ func stringtoslicerune(s String) (b Slice) { ...@@ -316,6 +322,7 @@ func stringtoslicerune(s String) (b Slice) {
intgo n; intgo n;
int32 dum, *r; int32 dum, *r;
uint8 *p, *ep; uint8 *p, *ep;
uintptr mem;
// two passes. // two passes.
// unlike slicerunetostring, no race because strings are immutable. // unlike slicerunetostring, no race because strings are immutable.
...@@ -327,13 +334,16 @@ func stringtoslicerune(s String) (b Slice) { ...@@ -327,13 +334,16 @@ func stringtoslicerune(s String) (b Slice) {
n++; n++;
} }
b.array = runtime·mallocgc(n*sizeof(r[0]), 0, FlagNoScan|FlagNoZero); mem = runtime·roundupsize(n*sizeof(r[0]));
b.array = runtime·mallocgc(mem, 0, FlagNoScan|FlagNoZero);
b.len = n; b.len = n;
b.cap = n; b.cap = mem/sizeof(r[0]);
p = s.str; p = s.str;
r = (int32*)b.array; r = (int32*)b.array;
while(p < ep) while(p < ep)
p += runtime·charntorune(r++, p, ep-p); p += runtime·charntorune(r++, p, ep-p);
if(b.cap > b.len)
runtime·memclr(b.array+b.len*sizeof(r[0]), (b.cap-b.len)*sizeof(r[0]));
} }
enum enum
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment