From 2423370136d4b1915d06bb1aaacbedaa900bc5c7 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 25 Nov 2022 14:22:36 +0100 Subject: utf16: reduce utf16.Decode allocations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL avoids allocating in utf16.Decode for code point sequences with less than 64 elements. It does so by splitting the function in two, one that can be inlined that preallocates a buffer and the other that does the heavy-lifting. The mid-stack inliner will allocate the buffer in the caller stack, and in many cases this will be enough to avoid the allocation. unicode/utf16 benchmarks: name old time/op new time/op delta DecodeValidASCII-12 60.1ns ± 3% 16.0ns ±20% -73.40% (p=0.000 n=8+10) DecodeValidJapaneseChars-12 61.3ns ±10% 14.9ns ±39% -75.71% (p=0.000 n=10+10) name old alloc/op new alloc/op delta DecodeValidASCII-12 48.0B ± 0% 0.0B -100.00% (p=0.000 n=10+10) DecodeValidJapaneseChars-12 48.0B ± 0% 0.0B -100.00% (p=0.000 n=10+10) name old allocs/op new allocs/op delta DecodeValidASCII-12 1.00 ± 0% 0.00 -100.00% (p=0.000 n=10+10) DecodeValidJapaneseChars-12 1.00 ± 0% 0.00 -100.00% (p=0.000 n=10+10) I've also benchmarked os.File.ReadDir with this change applied to demonstrate that it does make a difference in the caller site, in this case via syscall.UTF16ToString: name old time/op new time/op delta ReadDir-12 592µs ± 8% 620µs ±16% ~ (p=0.280 n=10+10) name old alloc/op new alloc/op delta ReadDir-12 30.4kB ± 0% 22.4kB ± 0% -26.10% (p=0.000 n=8+10) name old allocs/op new allocs/op delta ReadDir-12 402 ± 0% 272 ± 0% -32.34% (p=0.000 n=10+10) Change-Id: I65cf5caa3fd3b3a466c0ed837a50a96e975bbe6b Reviewed-on: https://go-review.googlesource.com/c/go/+/453415 Reviewed-by: Damien Neil Reviewed-by: Cherry Mui Reviewed-by: Alex Brainman TryBot-Result: Gopher Robot Run-TryBot: Quim Muntal --- src/unicode/utf16/utf16_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/unicode/utf16/utf16_test.go') diff --git a/src/unicode/utf16/utf16_test.go b/src/unicode/utf16/utf16_test.go index be339b1fdf..a5a503d387 100644 --- a/src/unicode/utf16/utf16_test.go +++ b/src/unicode/utf16/utf16_test.go @@ -5,6 +5,7 @@ package utf16_test import ( + "internal/testenv" "reflect" "testing" "unicode" @@ -103,6 +104,22 @@ var decodeTests = []decodeTest{ {[]uint16{0xdfff}, []rune{0xfffd}}, } +func TestAllocationsDecode(t *testing.T) { + testenv.SkipIfOptimizationOff(t) + + for _, tt := range decodeTests { + allocs := testing.AllocsPerRun(10, func() { + out := Decode(tt.in) + if out == nil { + t.Errorf("Decode(%x) = nil", tt.in) + } + }) + if allocs > 0 { + t.Errorf("Decode allocated %v times", allocs) + } + } +} + func TestDecode(t *testing.T) { for _, tt := range decodeTests { out := Decode(tt.in) -- cgit v1.3