Commit 0dae9dfb authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Martin Möhrmann

cmd/compile: improve string iteration performance

Generate a for loop for ranging over strings that only needs to call
the runtime function charntorune for non ASCII characters.

This provides faster iteration over ASCII characters and slightly
faster iteration for other characters.

The runtime function charntorune is changed to take an index from where
to start decoding and returns the index after the last byte belonging
to the decoded rune.

All call sites of charntorune in the runtime are replaced by a for loop
that will be transformed by the compiler instead of calling the charntorune
function directly.

go binary size decreases by 80 bytes.
godoc binary size increases by around 4 kilobytes.

runtime:

name                           old time/op  new time/op  delta
RuneIterate/range/ASCII-4      43.7ns ± 3%  10.3ns ± 4%  -76.33%  (p=0.000 n=44+45)
RuneIterate/range/Japanese-4   72.5ns ± 2%  62.8ns ± 2%  -13.41%  (p=0.000 n=49+50)
RuneIterate/range1/ASCII-4     43.5ns ± 2%  10.4ns ± 3%  -76.18%  (p=0.000 n=50+50)
RuneIterate/range1/Japanese-4  72.5ns ± 2%  62.9ns ± 2%  -13.26%  (p=0.000 n=50+49)
RuneIterate/range2/ASCII-4     43.5ns ± 3%  10.3ns ± 2%  -76.22%  (p=0.000 n=48+47)
RuneIterate/range2/Japanese-4  72.4ns ± 2%  62.7ns ± 2%  -13.47%  (p=0.000 n=50+50)

strings:

name                 old time/op    new time/op    delta
IndexRune-4            64.7ns ± 5%    22.4ns ± 3%  -65.43%  (p=0.000 n=25+21)
MapNoChanges-4          269ns ± 2%     157ns ± 2%  -41.46%  (p=0.000 n=23+24)
Fields-4               23.0ms ± 2%    19.7ms ± 2%  -14.35%  (p=0.000 n=25+25)
FieldsFunc-4           23.1ms ± 2%    19.6ms ± 2%  -14.94%  (p=0.000 n=25+24)

name                 old speed      new speed      delta
Fields-4             45.6MB/s ± 2%  53.2MB/s ± 2%  +16.87%  (p=0.000 n=24+25)
FieldsFunc-4         45.5MB/s ± 2%  53.5MB/s ± 2%  +17.57%  (p=0.000 n=25+24)

Updates #13162

Change-Id: I79ffaf828d82bf9887592f08e5cad883e9f39701
Reviewed-on: https://go-review.googlesource.com/27853
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Martin Möhrmann <martisch@uos.de>
parent 0d7a2241
...@@ -23,88 +23,87 @@ const runtimeimport = "" + ...@@ -23,88 +23,87 @@ const runtimeimport = "" +
"ebytetostringtmp\x00\x01\x11\"\x00\x01 \x00\t!slicerunetostr" + "ebytetostringtmp\x00\x01\x11\"\x00\x01 \x00\t!slicerunetostr" +
"ing\x00\x03\x17\x0f@\"\x00\x11|S\x00\x01 \x00\t!stringtoslicebyte\x00\x03\x17\x0f" + "ing\x00\x03\x17\x0f@\"\x00\x11|S\x00\x01 \x00\t!stringtoslicebyte\x00\x03\x17\x0f" +
"@\"\x00 \x00\x01\x11\"\x00\t'stringtoslicebytetmp\x00\x01 \x00\x01\x11\"\x00\t" + "@\"\x00 \x00\x01\x11\"\x00\t'stringtoslicebytetmp\x00\x01 \x00\x01\x11\"\x00\t" +
"!stringtoslicerune\x00\x03\x17\x0f@|S\x00 \x00\x01\x11|S\x00\t\x13strin" + "!stringtoslicerune\x00\x03\x17\x0f@|S\x00 \x00\x01\x11|S\x00\t\x15charn" +
"giter\x00\x03 \x00\x02\x00\x01\x02\x00\t\x15stringiter2\x00\x03 \x00\x02\x00\x04\x02\rretk" + "torune\x00\x03 \x00\x02\x00\x04|S\rretv·1\x00\x00\x02\rretk·2\x00\x00\t\x11sl" +
"·1\x00\x00|S\rretv·2\x00\x00\t\x11slicecopy\x00\x06:\tto·2\x00\x00:" + "icecopy\x00\x06:\tto·2\x00\x00:\tfr·3\x00\x00\x16\vwid·4\x00\x1buns" +
"\tfr·3\x00\x00\x16\vwid·4\x00\x1bunsafe-uintptr\x01\x02\x00\t\x1dsli" + "afe-uintptr\x01\x02\x00\t\x1dslicestringcopy\x00\x04:Z\x00\x00:\\\x00" +
"cestringcopy\x00\x04:\\\x00\x00:^\x00\x00\x01\x02\x00\t\rconvI2E\x00\x02:\rel" + "\x00\x01\x02\x00\t\rconvI2E\x00\x02:\relem·2\x00\x00\x02:\vret·1\x00\x00\t\rc" +
"em·2\x00\x00\x02:\vret·1\x00\x00\t\rconvI2I\x00\x04\x17\"\x06\x00\x00:\relem" + "onvI2I\x00\x04\x17\"\x06\x00\x00:\relem·3\x00\x00\x02:h\x00\x00\t\rconvT2E\x00\x06" +
"·3\x00\x00\x02:j\x00\x00\t\rconvT2E\x00\x06\x17\"\x06\x00\x00\x17:n\x00\x00\x17:\vbuf·4" + "\x17\"\x06\x00\x00\x17:l\x00\x00\x17:\vbuf·4\x00\x00\x02:h\x00\x00\t\rconvT2I\x00\x06\x17\"\v" +
"\x00\x00\x02:j\x00\x00\t\rconvT2I\x00\x06\x17\"\vtab·2\x00\x00\x17:n\x00\x00\x17:r\x00\x00\x02" + "tab·2\x00\x00\x17:l\x00\x00\x17:p\x00\x00\x02:h\x00\x00\t\x11assertE2E\x00\x06\x17\"\vt" +
":j\x00\x00\t\x11assertE2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00" + "yp·1\x00\x00:\x0fiface·2\x00\x00\x17:\vret·3\x00\x00\x00\t\x13assertE" +
"\x17:\vret·3\x00\x00\x00\t\x13assertE2E2\x00\x06\x17\"\x06\x00\x00:\x0fiface·" + "2E2\x00\x06\x17\"\x06\x00\x00:\x0fiface·3\x00\x00\x17:\vret·4\x00\x00\x01\x00\x00\t\x11as" +
"3\x00\x00\x17:\vret·4\x00\x00\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"z\x00\x00:||\x00\x00" + "sertE2I\x00\x06\x17\"x\x00\x00:z\x00\x00\x17:||\x00\x00\x00\t\x13assertE2I2\x00\x06\x17" +
"\x17:~\x00\x00\x00\t\x13assertE2I2\x00\x06\x17\"\x06\x00\x00:\x82\x01\x00\x00\x17:\x84\x01\x00\x00\x01\x00\x00\t" + "\"\x06\x00\x00:\x80\x01\x00\x00\x17:\x82\x01\x00\x00\x01\x00\x00\t\x11assertE2T\x00\x06\x17\"x\x00\x00:z\x00\x00" +
"\x11assertE2T\x00\x06\x17\"z\x00\x00:||\x00\x00\x17:~\x00\x00\x00\t\x13assertE2T2" + "\x17:||\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\x06\x00\x00:\x80\x01\x00\x00\x17:\x82\x01\x00\x00\x01\x00\x00" +
"\x00\x06\x17\"\x06\x00\x00:\x82\x01\x00\x00\x17:\x84\x01\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"z\x00\x00:" + "\t\x11assertI2E\x00\x06\x17\"x\x00\x00:z\x00\x00\x17:||\x00\x00\x00\t\x13assertI2E" +
"||\x00\x00\x17:~\x00\x00\x00\t\x13assertI2E2\x00\x06\x17\"\x06\x00\x00:\x82\x01\x00\x00\x17:\x84\x01\x00\x00" + "2\x00\x06\x17\"\x06\x00\x00:\x80\x01\x00\x00\x17:\x82\x01\x00\x00\x01\x00\x00\t\x11assertI2I\x00\x06\x17\"x\x00\x00" +
"\x01\x00\x00\t\x11assertI2I\x00\x06\x17\"z\x00\x00:||\x00\x00\x17:~\x00\x00\x00\t\x13assert" + ":z\x00\x00\x17:||\x00\x00\x00\t\x13assertI2I2\x00\x06\x17\"\x06\x00\x00:\x80\x01\x00\x00\x17:\x82\x01\x00" +
"I2I2\x00\x06\x17\"\x06\x00\x00:\x82\x01\x00\x00\x17:\x84\x01\x00\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"" + "\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"x\x00\x00:z\x00\x00\x17:||\x00\x00\x00\t\x13asser" +
"z\x00\x00:||\x00\x00\x17:~\x00\x00\x00\t\x13assertI2T2\x00\x06\x17\"\x06\x00\x00:\x82\x01\x00\x00\x17:" + "tI2T2\x00\x06\x17\"\x06\x00\x00:\x80\x01\x00\x00\x17:\x82\x01\x00\x00\x01\x00\x00\t\x17panicdottype" +
"\x84\x01\x00\x00\x01\x00\x00\t\x17panicdottype\x00\x06\x17\"\rhave·1\x00\x00\x17\"\rwa" + "\x00\x06\x17\"\rhave·1\x00\x00\x17\"\rwant·2\x00\x00\x17\"\x80\x01\x00\x00\x00\t\riface" +
"nt·2\x00\x00\x17\"\x82\x01\x00\x00\x00\t\rifaceeq\x00\x04:\ti1·2\x00\x00:\ti2·" + "eq\x00\x04:\ti1·2\x00\x00:\ti2·3\x00\x00\x02\x00h\x00\x00\t\refaceeq\x00\x04:\xa0" +
"3\x00\x00\x02\x00j\x00\x00\t\refaceeq\x00\x04:\xa2\x01\x00\x00:\xa4\x01\x00\x00\x02\x00j\x00\x00\t\rmake" + "\x01\x00\x00:\xa2\x01\x00\x00\x02\x00h\x00\x00\t\rmakemap\x00\b\x17\"\x13mapType·2\x00\x00\n" +
"map\x00\b\x17\"\x13mapType·2\x00\x00\n\rhint·3\x00\x00\x17:\x11mapbuf" + "\rhint·3\x00\x00\x17:\x11mapbuf·4\x00\x00\x17:\x17bucketbuf·5\x00" +
"·4\x00\x00\x17:\x17bucketbuf·5\x00\x00\x02\x1d::\rhmap·1\x00\x00\t\x13ma" + "\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapaccess1\x00\x06\x17\"\xa8\x01\x00\x00\x1d::\rh" +
"paccess1\x00\x06\x17\"\xaa\x01\x00\x00\x1d::\rhmap·3\x00\x00\x17:\vkey·4\x00\x00" + "map·3\x00\x00\x17:\vkey·4\x00\x00\x02\x17:\vval·1\x00\x00\t!mapacce" +
"\x02\x17:\vval·1\x00\x00\t!mapaccess1_fast32\x00\x06\x17\"\xaa\x01\x00\x00\x1d" + "ss1_fast32\x00\x06\x17\"\xa8\x01\x00\x00\x1d::\xb4\x01\x00\x00:\xb6\x01\x00\x00\x02\x17:\xb8\x01\x00\x00\t!m" +
"::\xb6\x01\x00\x00:\xb8\x01\x00\x00\x02\x17:\xba\x01\x00\x00\t!mapaccess1_fast64\x00\x06\x17" + "apaccess1_fast64\x00\x06\x17\"\xa8\x01\x00\x00\x1d::\xb4\x01\x00\x00:\xb6\x01\x00\x00\x02\x17:\xb8" +
"\"\xaa\x01\x00\x00\x1d::\xb6\x01\x00\x00:\xb8\x01\x00\x00\x02\x17:\xba\x01\x00\x00\t#mapaccess1_fas" + "\x01\x00\x00\t#mapaccess1_faststr\x00\x06\x17\"\xa8\x01\x00\x00\x1d::\xb4\x01\x00\x00:\xb6" +
"tstr\x00\x06\x17\"\xaa\x01\x00\x00\x1d::\xb6\x01\x00\x00:\xb8\x01\x00\x00\x02\x17:\xba\x01\x00\x00\t\x1bmapacce" + "\x01\x00\x00\x02\x17:\xb8\x01\x00\x00\t\x1bmapaccess1_fat\x00\b\x17\"\xa8\x01\x00\x00\x1d::\xb4\x01\x00" +
"ss1_fat\x00\b\x17\"\xaa\x01\x00\x00\x1d::\xb6\x01\x00\x00\x17:\xb8\x01\x00\x00\x17\"\rzero·5\x00\x00" + "\x00\x17:\xb6\x01\x00\x00\x17\"\rzero·5\x00\x00\x02\x17:\xb8\x01\x00\x00\t\x13mapaccess2\x00\x06" +
"\x02\x17:\xba\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapType·3\x00\x00\x1d::\r" + "\x17\"\x13mapType·3\x00\x00\x1d::\rhmap·4\x00\x00\x17:\vkey·5\x00\x00\x04" +
"hmap·4\x00\x00\x17:\vkey·5\x00\x00\x04\x17:\xba\x01\x00\x00\x00\rpres·2\x00\x00\t!" + "\x17:\xb8\x01\x00\x00\x00\rpres·2\x00\x00\t!mapaccess2_fast32\x00\x06\x17\"" +
"mapaccess2_fast32\x00\x06\x17\"\xc8\x01\x00\x00\x1d::\xca\x01\x00\x00:\xcc\x01\x00\x00\x04\x17:" + "\xc6\x01\x00\x00\x1d::\xc8\x01\x00\x00:\xca\x01\x00\x00\x04\x17:\xb8\x01\x00\x00\x00\xcc\x01\x00\x00\t!mapaccess2" +
"\xba\x01\x00\x00\x00\xce\x01\x00\x00\t!mapaccess2_fast64\x00\x06\x17\"\xc8\x01\x00\x00\x1d::\xca" + "_fast64\x00\x06\x17\"\xc6\x01\x00\x00\x1d::\xc8\x01\x00\x00:\xca\x01\x00\x00\x04\x17:\xb8\x01\x00\x00\x00\xcc\x01\x00\x00\t" +
"\x01\x00\x00:\xcc\x01\x00\x00\x04\x17:\xba\x01\x00\x00\x00\xce\x01\x00\x00\t#mapaccess2_faststr" + "#mapaccess2_faststr\x00\x06\x17\"\xc6\x01\x00\x00\x1d::\xc8\x01\x00\x00:\xca\x01\x00\x00\x04" +
"\x00\x06\x17\"\xc8\x01\x00\x00\x1d::\xca\x01\x00\x00:\xcc\x01\x00\x00\x04\x17:\xba\x01\x00\x00\x00\xce\x01\x00\x00\t\x1bmapacc" + "\x17:\xb8\x01\x00\x00\x00\xcc\x01\x00\x00\t\x1bmapaccess2_fat\x00\b\x17\"\xc6\x01\x00\x00\x1d::\xc8\x01" +
"ess2_fat\x00\b\x17\"\xc8\x01\x00\x00\x1d::\xca\x01\x00\x00\x17:\xcc\x01\x00\x00\x17\"\rzero·6\x00" + "\x00\x00\x17:\xca\x01\x00\x00\x17\"\rzero·6\x00\x00\x04\x17:\xb8\x01\x00\x00\x00\xcc\x01\x00\x00\t\x13mapass" +
"\x00\x04\x17:\xba\x01\x00\x00\x00\xce\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapType·1" + "ign1\x00\b\x17\"\x13mapType·1\x00\x00\x1d::\rhmap·2\x00\x00\x17:\vkey" +
"\x00\x00\x1d::\rhmap·2\x00\x00\x17:\vkey·3\x00\x00\x17:\vval·4\x00\x00\x00\t\x15" + "·3\x00\x00\x17:\vval·4\x00\x00\x00\t\x15mapiterinit\x00\x06\x17\"\xda\x01\x00\x00\x1d:" +
"mapiterinit\x00\x06\x17\"\xdc\x01\x00\x00\x1d::\xde\x01\x00\x00\x17:\x0fhiter·3\x00\x00\x00" + ":\xdc\x01\x00\x00\x17:\x0fhiter·3\x00\x00\x00\t\x11mapdelete\x00\x06\x17\"\xda\x01\x00\x00\x1d:" +
"\t\x11mapdelete\x00\x06\x17\"\xdc\x01\x00\x00\x1d::\xde\x01\x00\x00\x17:\xe0\x01\x00\x00\x00\t\x15mapit" + ":\xdc\x01\x00\x00\x17:\xde\x01\x00\x00\x00\t\x15mapiternext\x00\x02\x17:\x0fhiter·1\x00\x00" +
"ernext\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15cha" + "\x00\t\x0fmakechan\x00\x04\x17\"\x15chanType·2\x00\x00\n\xaa\x01\x00\x00\x02\x1f\x06:\x0fh" +
"nType·2\x00\x00\n\xac\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv" + "chan·1\x00\x00\t\x11chanrecv1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02" +
"1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00\x17:n\x00\x00\x00\t" + ":\x0fhchan·2\x00\x00\x17:l\x00\x00\x00\t\x11chanrecv2\x00\x06\x17\"\xee\x01\x00\x00\x1f\x02:" +
"\x11chanrecv2\x00\x06\x17\"\xf0\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00\x17:\relem\xc2" + "\x0fhchan·3\x00\x00\x17:\relem·4\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17" +
"\xb74\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf6\x01\x00\x00\x1f\x04:\xf8\x01\x00\x00\x17:n\x00\x00\x00\t" + "\"\xf4\x01\x00\x00\x1f\x04:\xf6\x01\x00\x00\x17:l\x00\x00\x00\t\x11closechan\x00\x02:\xf0\x01\x00\x00\x00\a\x17w" +
"\x11closechan\x00\x02:\xf2\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renab" + "riteBarrier\x00\x15\x06\renabled\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00" +
"led\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr" + "\x00\x00\t\x1dwritebarrierptr\x00\x04\x17:\vdst·1\x00\x00:\vsrc·2" +
"\x00\x04\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06" + "\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"x\x00\x00\x17:\vdst·2\x00\x00\x17:\vsr" +
"\x17\"z\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc·3\x00\x00\x00\t\x1btypedslice" + "c·3\x00\x00\x00\t\x1btypedslicecopy\x00\x06\x17\"\x06\x00\x00:\vdst·3\x00\x00" +
"copy\x00\x06\x17\"\x06\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17sele" + ":\vsrc·4\x00\x00\x01\x02\x00\t\x17selectnbsend\x00\x06\x17\"\xee\x01\x00\x00\x1f\x04:\xfa\x01" +
"ctnbsend\x00\x06\x17\"\xf0\x01\x00\x00\x1f\x04:\xfc\x01\x00\x00\x17:\xfe\x01\x00\x00\x01\x00\x00\t\x17select" + "\x00\x00\x17:\xfc\x01\x00\x00\x01\x00\x00\t\x17selectnbrecv\x00\x06\x17\"\xee\x01\x00\x00\x17:l\x00\x00\x1f\x02" +
"nbrecv\x00\x06\x17\"\xf0\x01\x00\x00\x17:n\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19se" + ":\x0fhchan·4\x00\x00\x01\x00\x00\t\x19selectnbrecv2\x00\b\x17\"\xee\x01\x00\x00\x17:" +
"lectnbrecv2\x00\b\x17\"\xf0\x01\x00\x00\x17:n\x00\x00\x17\x00\x15received·4\x00\x00" + "l\x00\x00\x17\x00\x15received·4\x00\x00\x1f\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11ne" +
"\x1f\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00" + "wselect\x00\x06\x17\"\vsel·1\x00\x00\n\x13selsize·2\x00\x00\b\rsize" +
"\x00\n\x13selsize·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00" + "·3\x00\x00\x00\t\x13selectsend\x00\x06\x17\"\vsel·2\x00\x00\x1f\x04:\xfa\x01\x00\x00\x17:" +
"\x06\x17\"\vsel·2\x00\x00\x1f\x04:\xfc\x01\x00\x00\x17:\xfe\x01\x00\x00\x02\x00\x15selected·1\x00" + "\xfc\x01\x00\x00\x02\x00\x15selected·1\x00\x00\t\x13selectrecv\x00\x06\x17\"\xb2\x02\x00\x00" +
"\x00\t\x13selectrecv\x00\x06\x17\"\xb4\x02\x00\x00\x1f\x02:\xfc\x01\x00\x00\x17:\xfe\x01\x00\x00\x02\x00\xb6\x02\x00\x00" + "\x1f\x02:\xfa\x01\x00\x00\x17:\xfc\x01\x00\x00\x02\x00\xb4\x02\x00\x00\t\x15selectrecv2\x00\b\x17\"\xb2\x02\x00\x00" +
"\t\x15selectrecv2\x00\b\x17\"\xb4\x02\x00\x00\x1f\x02:\xfc\x01\x00\x00\x17:\xfe\x01\x00\x00\x17\x00\x15rec" + "\x1f\x02:\xfa\x01\x00\x00\x17:\xfc\x01\x00\x00\x17\x00\x15received·5\x00\x00\x02\x00\xb4\x02\x00\x00\t\x19sel" +
"eived·5\x00\x00\x02\x00\xb6\x02\x00\x00\t\x19selectdefault\x00\x02\x17\"\xb4\x02\x00\x00\x02" + "ectdefault\x00\x02\x17\"\xb2\x02\x00\x00\x02\x00\xb4\x02\x00\x00\t\x0fselectgo\x00\x02\x17\"\xaa\x02" +
"\x00\xb6\x02\x00\x00\t\x0fselectgo\x00\x02\x17\"\xac\x02\x00\x00\x00\t\tblock\x00\x00\x00\t\x11make" + "\x00\x00\x00\t\tblock\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\x06\x00\x00\x02\vlen·3\x00" +
"slice\x00\x06\x17\"\x06\x00\x00\x02\vlen·3\x00\x00\x02\vcap·4\x00\x00\x02\x11:\vary\xc2" + "\x00\x02\vcap·4\x00\x00\x02\x11:\vary·1\x00\x00\t\x15makeslice64\x00\x06\x17\"" +
"\xb71\x00\x00\t\x15makeslice64\x00\x06\x17\"\x06\x00\x00\n\xc6\x02\x00\x00\n\xc8\x02\x00\x00\x02\x11:\xca\x02\x00" + "\x06\x00\x00\n\xc4\x02\x00\x00\n\xc6\x02\x00\x00\x02\x11:\xc8\x02\x00\x00\t\x11growslice\x00\x06\x17\"\x06\x00\x00\x11:" +
"\x00\t\x11growslice\x00\x06\x17\"\x06\x00\x00\x11:\vold·3\x00\x00\x02\xc8\x02\x00\x00\x02\x11:\xca\x02" + "\vold·3\x00\x00\x02\xc6\x02\x00\x00\x02\x11:\xc8\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1" +
"\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00\x17:\vfrm·2\x00\x00\x16\x11leng" + "\x00\x00\x17:\vfrm·2\x00\x00\x16\x11length·3\x00`\x00\t\vmemclr\x00\x04\x17\"\v" +
"th·3\x00b\x00\t\vmemclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length·2" + "ptr·1\x00\x00\x16\x11length·2\x00`\x00\t\x0fmemequal\x00\x06\x17:\ax·" +
"\x00b\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00\x00\x17:\ay·3\x00\x00\x16\rsize\xc2" + "2\x00\x00\x17:\ay·3\x00\x00\x16\rsize·4\x00`\x01\x00\x00\t\x11memequal8\x00\x04\x17" +
"\xb74\x00b\x01\x00\x00\t\x11memequal8\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13mem" + ":\xe0\x02\x00\x00\x17:\xe2\x02\x00\x00\x01\x00\x00\t\x13memequal16\x00\x04\x17:\xe0\x02\x00\x00\x17:\xe2\x02\x00\x00" +
"equal16\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04\x17:" + "\x01\x00\x00\t\x13memequal32\x00\x04\x17:\xe0\x02\x00\x00\x17:\xe2\x02\x00\x00\x01\x00\x00\t\x13memequ" +
"\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal64\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01" + "al64\x00\x04\x17:\xe0\x02\x00\x00\x17:\xe2\x02\x00\x00\x01\x00\x00\t\x15memequal128\x00\x04\x17:\xe0\x02" +
"\x00\x00\t\x15memequal128\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x0fint64d" + "\x00\x00\x17:\xe2\x02\x00\x00\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64di" +
"iv\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64mo" + "v\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod" +
"d\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat64t" + "\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat6" +
"oint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64touint64\x00\x01\x1a\x00\x01\x14\x00\t\x1df" + "4touint64\x00\x01\x1a\x00\x01\x14\x00\t\x1dfloat64touint32\x00\x01\x1a\x00\x01\x12\x00" +
"loat64touint32\x00\x01\x1a\x00\x01\x12\x00\t\x1bint64tofloat64\x00\x01\n" + "\t\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64" +
"\x00\x01\x1a\x00\t\x1duint64tofloat64\x00\x01\x14\x00\x01\x1a\x00\t\x1duint32tofl" + "\x00\x01\x14\x00\x01\x1a\x00\t\x1duint32tofloat64\x00\x01\x12\x00\x01\x1a\x00\t\x19complex" +
"oat64\x00\x01\x12\x00\x01\x1a\x00\t\x19complex128div\x00\x04\x1e\vnum·2\x00\x00\x1e" + "128div\x00\x04\x1e\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t" +
"\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefuncenter\x00\x01\x16b\x00" + "\x19racefuncenter\x00\x01\x16`\x00\t\x17racefuncexit\x00\x00\x00\t\x0fra" +
"\t\x17racefuncexit\x00\x00\x00\t\x0fraceread\x00\x01\x16b\x00\t\x11racewr" + "ceread\x00\x01\x16`\x00\t\x11racewrite\x00\x01\x16`\x00\t\x19racereadran" +
"ite\x00\x01\x16b\x00\t\x19racereadrange\x00\x04\x16\raddr·1\x00b\x16\rsi" + "ge\x00\x04\x16\raddr·1\x00`\x16\rsize·2\x00`\x00\t\x1bracewritera" +
"ze·2\x00b\x00\t\x1bracewriterange\x00\x04\x16\x98\x03\x00b\x16\x9a\x03\x00b\x00\t\x0fm" + "nge\x00\x04\x16\x96\x03\x00`\x16\x98\x03\x00`\x00\t\x0fmsanread\x00\x04\x16\x96\x03\x00`\x16\x98\x03\x00`\x00\t" +
"sanread\x00\x04\x16\x98\x03\x00b\x16\x9a\x03\x00b\x00\t\x11msanwrite\x00\x04\x16\x98\x03\x00b\x16\x9a" + "\x11msanwrite\x00\x04\x16\x96\x03\x00`\x16\x98\x03\x00`\x00\v\xf8\x01\v\x00\x01\x00\n$$\n"
"\x03\x00b\x00\v\xfa\x01\v\x00\x01\x00\n$$\n"
const unsafeimport = "" + const unsafeimport = "" +
"version 2\n\n\x00\x00\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOff" + "version 2\n\n\x00\x00\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOff" +
......
...@@ -54,8 +54,7 @@ func slicerunetostring(*[32]byte, []rune) string ...@@ -54,8 +54,7 @@ func slicerunetostring(*[32]byte, []rune) string
func stringtoslicebyte(*[32]byte, string) []byte func stringtoslicebyte(*[32]byte, string) []byte
func stringtoslicebytetmp(string) []byte func stringtoslicebytetmp(string) []byte
func stringtoslicerune(*[32]rune, string) []rune func stringtoslicerune(*[32]rune, string) []rune
func stringiter(string, int) int func charntorune(string, int) (retv rune, retk int)
func stringiter2(string, int) (retk int, retv rune)
func slicecopy(to any, fr any, wid uintptr) int func slicecopy(to any, fr any, wid uintptr) int
func slicestringcopy(to any, fr any) int func slicestringcopy(to any, fr any) int
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package gc package gc
import "unicode/utf8"
// range // range
func typecheckrange(n *Node) { func typecheckrange(n *Node) {
var toomany int var toomany int
...@@ -284,34 +286,63 @@ func walkrange(n *Node) { ...@@ -284,34 +286,63 @@ func walkrange(n *Node) {
body = append(body, Nod(OAS, hv1, nil)) body = append(body, Nod(OAS, hv1, nil))
case TSTRING: case TSTRING:
// Transform string range statements like "for v1, v2 = range a" into
//
// ha := a
// for hv1 := 0; hv1 < len(ha); {
// v1 = hv1
// hv2 := rune(ha[hv1])
// if hv2 < utf8.RuneSelf {
// hv1++
// } else {
// hv2, hv1 = charntorune(ha, hv1)
// }
// v2 = hv2
// // original body
// }
// orderstmt arranged for a copy of the string variable. // orderstmt arranged for a copy of the string variable.
ha := a ha := a
ohv1 := temp(Types[TINT])
hv1 := temp(Types[TINT]) hv1 := temp(Types[TINT])
init = append(init, Nod(OAS, hv1, nil)) hv2 := temp(runetype)
var a *Node // hv1 := 0
var hv2 *Node init = append(init, Nod(OAS, hv1, nil))
if v2 == nil {
a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1))
} else {
hv2 = temp(runetype)
a = Nod(OAS2, nil, nil)
a.List.Set([]*Node{hv1, hv2})
fn := syslook("stringiter2")
a.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1))
}
n.Left = Nod(ONE, hv1, Nodintconst(0)) // hv1 < len(ha)
n.Left.Ninit.Set([]*Node{Nod(OAS, ohv1, hv1), a}) n.Left = Nod(OLT, hv1, Nod(OLEN, ha, nil))
body = nil
if v1 != nil { if v1 != nil {
body = []*Node{Nod(OAS, v1, ohv1)} // v1 = hv1
body = append(body, Nod(OAS, v1, hv1))
} }
// hv2 := ha[hv1]
nind := Nod(OINDEX, ha, hv1)
nind.Bounded = true
body = append(body, Nod(OAS, hv2, conv(nind, runetype)))
// if hv2 < utf8.RuneSelf
nif := Nod(OIF, nil, nil)
nif.Left = Nod(OLT, nind, Nodintconst(utf8.RuneSelf))
// hv1++
nif.Nbody.Set1(Nod(OAS, hv1, Nod(OADD, hv1, Nodintconst(1))))
// } else {
eif := Nod(OAS2, nil, nil)
nif.Rlist.Set1(eif)
// hv2, hv1 = charntorune(ha, hv1)
eif.List.Set2(hv2, hv1)
fn := syslook("charntorune")
eif.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1))
body = append(body, nif)
if v2 != nil { if v2 != nil {
// v2 = hv2
body = append(body, Nod(OAS, v2, hv2)) body = append(body, Nod(OAS, v2, hv2))
} }
} }
......
...@@ -375,13 +375,11 @@ func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int { ...@@ -375,13 +375,11 @@ func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int {
total := len(s) total := len(s)
w := 0 w := 0
for len(s) > 0 { for _, r := range s {
if w >= len(utf16tmp)-2 { if w >= len(utf16tmp)-2 {
writeConsoleUTF16(handle, utf16tmp[:w]) writeConsoleUTF16(handle, utf16tmp[:w])
w = 0 w = 0
} }
r, n := charntorune(s)
s = s[n:]
if r < 0x10000 { if r < 0x10000 {
utf16tmp[w] = uint16(r) utf16tmp[w] = uint16(r)
w++ w++
......
...@@ -49,115 +49,97 @@ const ( ...@@ -49,115 +49,97 @@ const (
surrogateMin = 0xD800 surrogateMin = 0xD800
surrogateMax = 0xDFFF surrogateMax = 0xDFFF
bad = runeerror
runemax = 0x10FFFF /* maximum rune value */ runemax = 0x10FFFF /* maximum rune value */
) )
/* // charntorune returns the rune at the start of
* Modified by Wei-Hwa Huang, Google Inc., on 2004-09-24 // s[k:] and the index after the rune in s.
* This is a slower but "safe" version of the old chartorune //
* that works on strings that are not necessarily null-terminated. // If the string appears to be incomplete or decoding problems
* // are encountered (runeerror, k + 1) is returned to ensure
* If you know for sure that your string is null-terminated, // progress when charntorune is used to iterate over a string.
* chartorune will be a bit faster. //
* // Modified by Wei-Hwa Huang, Google Inc., on 2004-09-24
* It is guaranteed not to attempt to access "length" func charntorune(s string, k int) (rune, int) {
* past the incoming pointer. This is to avoid // When we're not allowed to read anything */
* possible access violations. If the string appears to be if len(s) <= k {
* well-formed but incomplete (i.e., to get the whole Rune return runeerror, k + 1
* we'd need to read past str+length) then we'll set the Rune
* to Bad and return 0.
*
* Note that if we have decoding problems for other
* reasons, we return 1 instead of 0.
*/
func charntorune(s string) (rune, int) {
/* When we're not allowed to read anything */
if len(s) <= 0 {
return bad, 1
} }
/* s = s[k:]
* one character sequence (7-bit value)
* 00000-0007F => T1 // one character sequence (7-bit value)
*/ // 00000-0007F => T1
c := s[0] c := s[0]
if c < tx { if c < tx {
return rune(c), 1 return rune(c), k + 1
} }
// If we can't read more than one character we must stop // If we can't read more than one character we must stop
if len(s) <= 1 { if len(s) <= 1 {
return bad, 1 return runeerror, k + 1
} }
/* // two character sequence (11-bit value)
* two character sequence (11-bit value) // 0080-07FF => t2 tx
* 0080-07FF => t2 tx
*/
c1 := s[1] ^ tx c1 := s[1] ^ tx
if (c1 & testx) != 0 { if (c1 & testx) != 0 {
return bad, 1 return runeerror, k + 1
} }
if c < t3 { if c < t3 {
if c < t2 { if c < t2 {
return bad, 1 return runeerror, k + 1
} }
l := ((rune(c) << bitx) | rune(c1)) & rune2 l := ((rune(c) << bitx) | rune(c1)) & rune2
if l <= rune1 { if l <= rune1 {
return bad, 1 return runeerror, k + 1
} }
return l, 2 return l, k + 2
} }
// If we can't read more than two characters we must stop // If we can't read more than two characters we must stop
if len(s) <= 2 { if len(s) <= 2 {
return bad, 1 return runeerror, k + 1
} }
/* // three character sequence (16-bit value)
* three character sequence (16-bit value) // 0800-FFFF => t3 tx tx
* 0800-FFFF => t3 tx tx
*/
c2 := s[2] ^ tx c2 := s[2] ^ tx
if (c2 & testx) != 0 { if (c2 & testx) != 0 {
return bad, 1 return runeerror, k + 1
} }
if c < t4 { if c < t4 {
l := ((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) & rune3 l := ((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) & rune3
if l <= rune2 { if l <= rune2 {
return bad, 1 return runeerror, k + 1
} }
if surrogateMin <= l && l <= surrogateMax { if surrogateMin <= l && l <= surrogateMax {
return bad, 1 return runeerror, k + 1
} }
return l, 3 return l, k + 3
} }
if len(s) <= 3 { if len(s) <= 3 {
return bad, 1 return runeerror, k + 1
} }
/* // four character sequence (21-bit value)
* four character sequence (21-bit value) // 10000-1FFFFF => t4 tx tx tx
* 10000-1FFFFF => t4 tx tx tx
*/
c3 := s[3] ^ tx c3 := s[3] ^ tx
if (c3 & testx) != 0 { if (c3 & testx) != 0 {
return bad, 1 return runeerror, k + 1
} }
if c < t5 { if c < t5 {
l := ((((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) << bitx) | rune(c3)) & rune4 l := ((((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) << bitx) | rune(c3)) & rune4
if l <= rune3 || l > runemax { if l <= rune3 || l > runemax {
return bad, 1 return runeerror, k + 1
} }
return l, 4 return l, k + 4
} }
// Support for 5-byte or longer UTF-8 would go here, but // Support for 5-byte or longer UTF-8 would go here, but
// since we don't have that, we'll just return bad. // since we don't have that, we'll just return runeerror.
return bad, 1 return runeerror, k + 1
} }
// runetochar converts r to bytes and writes the result to str. // runetochar converts r to bytes and writes the result to str.
......
...@@ -163,12 +163,10 @@ func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune { ...@@ -163,12 +163,10 @@ func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
// two passes. // two passes.
// unlike slicerunetostring, no race because strings are immutable. // unlike slicerunetostring, no race because strings are immutable.
n := 0 n := 0
t := s for range s {
for len(s) > 0 {
_, k := charntorune(s)
s = s[k:]
n++ n++
} }
var a []rune var a []rune
if buf != nil && n <= len(buf) { if buf != nil && n <= len(buf) {
*buf = [tmpStringBufSize]rune{} *buf = [tmpStringBufSize]rune{}
...@@ -176,10 +174,9 @@ func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune { ...@@ -176,10 +174,9 @@ func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
} else { } else {
a = rawruneslice(n) a = rawruneslice(n)
} }
n = 0 n = 0
for len(t) > 0 { for _, r := range s {
r, k := charntorune(t)
t = t[k:]
a[n] = r a[n] = r
n++ n++
} }
...@@ -244,42 +241,6 @@ func intstring(buf *[4]byte, v int64) string { ...@@ -244,42 +241,6 @@ func intstring(buf *[4]byte, v int64) string {
return s[:n] return s[:n]
} }
// stringiter returns the index of the next
// rune after the rune that starts at s[k].
func stringiter(s string, k int) int {
if k >= len(s) {
// 0 is end of iteration
return 0
}
c := s[k]
if c < runeself {
return k + 1
}
// multi-char rune
_, n := charntorune(s[k:])
return k + n
}
// stringiter2 returns the rune that starts at s[k]
// and the index where the next rune starts.
func stringiter2(s string, k int) (int, rune) {
if k >= len(s) {
// 0 is end of iteration
return 0, 0
}
c := s[k]
if c < runeself {
return k + 1, rune(c)
}
// multi-char rune
r, n := charntorune(s[k:])
return k + n, r
}
// rawstring allocates storage for a new string. The returned // rawstring allocates storage for a new string. The returned
// string and byte slice both refer to the same storage. // string and byte slice both refer to the same storage.
// The storage is not zeroed. Callers should use // The storage is not zeroed. Callers should use
......
...@@ -82,28 +82,42 @@ func BenchmarkCompareStringBig(b *testing.B) { ...@@ -82,28 +82,42 @@ func BenchmarkCompareStringBig(b *testing.B) {
b.SetBytes(int64(len(s1))) b.SetBytes(int64(len(s1)))
} }
func BenchmarkRuneIterate(b *testing.B) { var stringdata = []struct{ name, data string }{
bytes := make([]byte, 100) {"ASCII", "01234567890"},
for i := range bytes { {"Japanese", "日本語日本語日本語"},
bytes[i] = byte('A')
}
s := string(bytes)
for i := 0; i < b.N; i++ {
for range s {
}
}
} }
func BenchmarkRuneIterate2(b *testing.B) { func BenchmarkRuneIterate(b *testing.B) {
bytes := make([]byte, 100) b.Run("range", func(b *testing.B) {
for i := range bytes { for _, sd := range stringdata {
bytes[i] = byte('A') b.Run(sd.name, func(b *testing.B) {
} for i := 0; i < b.N; i++ {
s := string(bytes) for range sd.data {
for i := 0; i < b.N; i++ { }
for range s { }
})
} }
} })
b.Run("range1", func(b *testing.B) {
for _, sd := range stringdata {
b.Run(sd.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
for _ = range sd.data {
}
}
})
}
})
b.Run("range2", func(b *testing.B) {
for _, sd := range stringdata {
b.Run(sd.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, _ = range sd.data {
}
}
})
}
})
} }
func BenchmarkArrayEqual(b *testing.B) { func BenchmarkArrayEqual(b *testing.B) {
......
// run
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Ensure that range loops over a string have the requisite side-effects.
package main
import (
"fmt"
"os"
)
func check(n int) {
var i int
var r rune
b := make([]byte, n)
for i = range b {
b[i] = byte(i + 1)
}
s := string(b)
// When n == 0, i is untouched by the range loop.
// Picking an initial value of -1 for i makes the
// "want" calculation below correct in all cases.
i = -1
for i = range s {
b[i] = s[i]
}
if want := n - 1; i != want {
fmt.Printf("index after range with side-effect = %d want %d\n", i, want)
os.Exit(1)
}
i = -1
r = '\x00'
for i, r = range s {
b[i] = byte(r)
}
if want := n - 1; i != want {
fmt.Printf("index after range with side-effect = %d want %d\n", i, want)
os.Exit(1)
}
if want := rune(n); r != want {
fmt.Printf("rune after range with side-effect = %q want %q\n", r, want)
os.Exit(1)
}
i = -1
// i is shadowed here, so its value should be unchanged.
for i := range s {
b[i] = s[i]
}
if want := -1; i != want {
fmt.Printf("index after range without side-effect = %d want %d\n", i, want)
os.Exit(1)
}
i = -1
r = -1
// i and r are shadowed here, so their values should be unchanged.
for i, r := range s {
b[i] = byte(r)
}
if want := -1; i != want {
fmt.Printf("index after range without side-effect = %d want %d\n", i, want)
os.Exit(1)
}
if want := rune(-1); r != want {
fmt.Printf("rune after range without side-effect = %q want %q\n", r, want)
os.Exit(1)
}
}
func main() {
check(0)
check(1)
check(15)
}
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