go/src/bytes/bytes_test.go

1865 lines
44 KiB
Go
Raw Normal View History

// Copyright 2009 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.
package bytes_test
import (
. "bytes"
"fmt"
"internal/testenv"
"math/rand"
"reflect"
"strings"
"testing"
"unicode"
"unicode/utf8"
)
func eq(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
func sliceOfString(s [][]byte) []string {
result := make([]string, len(s))
for i, v := range s {
result[i] = string(v)
}
return result
}
// For ease of reading, the test cases use strings that are converted to byte
// slices before invoking the functions.
var abcd = "abcd"
var faces = "☺☻☹"
var commas = "1,2,3,4"
var dots = "1....2....3....4"
type BinOpTest struct {
a string
b string
i int
}
func TestEqual(t *testing.T) {
for _, tt := range compareTests {
eql := Equal(tt.a, tt.b)
if eql != (tt.i == 0) {
t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql)
}
eql = EqualPortable(tt.a, tt.b)
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
if eql != (tt.i == 0) {
t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql)
}
}
}
func TestEqualExhaustive(t *testing.T) {
var size = 128
if testing.Short() {
size = 32
}
a := make([]byte, size)
b := make([]byte, size)
b_init := make([]byte, size)
// randomish but deterministic data
for i := 0; i < size; i++ {
a[i] = byte(17 * i)
b_init[i] = byte(23*i + 100)
}
for len := 0; len <= size; len++ {
for x := 0; x <= size-len; x++ {
for y := 0; y <= size-len; y++ {
copy(b, b_init)
copy(b[y:y+len], a[x:x+len])
if !Equal(a[x:x+len], b[y:y+len]) || !Equal(b[y:y+len], a[x:x+len]) {
t.Errorf("Equal(%d, %d, %d) = false", len, x, y)
}
}
}
}
}
// make sure Equal returns false for minimally different strings. The data
// is all zeros except for a single one in one location.
func TestNotEqual(t *testing.T) {
var size = 128
if testing.Short() {
size = 32
}
a := make([]byte, size)
b := make([]byte, size)
for len := 0; len <= size; len++ {
for x := 0; x <= size-len; x++ {
for y := 0; y <= size-len; y++ {
for diffpos := x; diffpos < x+len; diffpos++ {
a[diffpos] = 1
if Equal(a[x:x+len], b[y:y+len]) || Equal(b[y:y+len], a[x:x+len]) {
t.Errorf("NotEqual(%d, %d, %d, %d) = true", len, x, y, diffpos)
}
a[diffpos] = 0
}
}
}
}
}
var indexTests = []BinOpTest{
{"", "", 0},
{"", "a", -1},
{"", "foo", -1},
{"fo", "foo", -1},
{"foo", "baz", -1},
{"foo", "foo", 0},
{"oofofoofooo", "f", 2},
{"oofofoofooo", "foo", 4},
{"barfoobarfoo", "foo", 3},
{"foo", "", 0},
{"foo", "o", 1},
{"abcABCabc", "A", 3},
// cases with one byte strings - test IndexByte and special case in Index()
{"", "a", -1},
{"x", "a", -1},
{"x", "x", 0},
{"abc", "a", 0},
{"abc", "b", 1},
{"abc", "c", 2},
{"abc", "x", -1},
{"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33},
{"foofyfoobarfoobar", "y", 4},
{"oooooooooooooooooooooo", "r", -1},
// test fallback to Rabin-Karp.
{"oxoxoxoxoxoxoxoxoxoxoxoy", "oy", 22},
{"oxoxoxoxoxoxoxoxoxoxoxox", "oy", -1},
}
var lastIndexTests = []BinOpTest{
{"", "", 0},
{"", "a", -1},
{"", "foo", -1},
{"fo", "foo", -1},
{"foo", "foo", 0},
{"foo", "f", 0},
{"oofofoofooo", "f", 7},
{"oofofoofooo", "foo", 7},
{"barfoobarfoo", "foo", 9},
{"foo", "", 3},
{"foo", "o", 2},
{"abcABCabc", "A", 3},
{"abcABCabc", "a", 6},
}
var indexAnyTests = []BinOpTest{
{"", "", -1},
{"", "a", -1},
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"aaa", "a", 0},
{"abc", "xyz", -1},
{"abc", "xcz", 2},
{"ab☺c", "x☺yz", 2},
bytes, strings: optimize for ASCII sets In a large codebase within Google, there are thousands of uses of: ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight An analysis of their usage shows that over 97% of them only use character sets consisting of only ASCII symbols. Uses of ContainsAny|IndexAny|LastIndexAny: 6% are 1 character (e.g., "\n" or " ") 58% are 2-4 characters (e.g., "<>" or "\r\n\t ") 24% are 5-9 characters (e.g., "()[]*^$") 10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ") We optimize for ASCII sets, which are commonly used to search for "control" characters in some string. We don't optimize for the single character scenario since IndexRune or IndexByte could be used. Uses of Trim|TrimLeft|TrimRight: 71% are 1 character (e.g., "\n" or " ") 14% are 2 characters (e.g., "\r\n") 10% are 3-4 characters (e.g., " \t\r\n") 5% are 10+ characters (e.g., "0123456789abcdefABCDEF") We optimize for the single character case with a simple closured function that only checks for that character's value. We optimize for the medium and larger sets using a 16-byte bit-map representing a set of ASCII characters. The benchmarks below have the following suffix name "%d:%d" where the first number is the length of the input and the second number is the length of the charset. == bytes package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75% BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69% BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88% BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91% BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71% BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56% BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67% BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21% BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15% BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79% BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41% BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82% BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55% BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24% BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31% BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08% BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92% BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99% BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68% BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27% BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94% BenchmarkTrimASCII/1:2-4 79.0 105 +32.91% BenchmarkTrimASCII/1:4-4 79.6 109 +36.93% BenchmarkTrimASCII/1:8-4 78.8 118 +49.75% BenchmarkTrimASCII/1:16-4 80.2 132 +64.59% BenchmarkTrimASCII/16:1-4 243 116 -52.26% BenchmarkTrimASCII/16:2-4 243 171 -29.63% BenchmarkTrimASCII/16:4-4 243 176 -27.57% BenchmarkTrimASCII/16:8-4 241 184 -23.65% BenchmarkTrimASCII/16:16-4 238 199 -16.39% BenchmarkTrimASCII/256:1-4 2580 840 -67.44% BenchmarkTrimASCII/256:2-4 2603 1175 -54.86% BenchmarkTrimASCII/256:4-4 2572 1188 -53.81% BenchmarkTrimASCII/256:8-4 2550 1191 -53.29% BenchmarkTrimASCII/256:16-4 2585 1208 -53.27% BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37% BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86% BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66% BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88% BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21% == strings package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33% BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57% BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22% BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85% BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15% BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54% BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24% BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10% BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03% BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48% BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74% BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08% BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37% BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44% BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21% BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95% BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19% BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25% BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37% BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26% BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14% BenchmarkTrimASCII/1:2-4 73.3 103 +40.52% BenchmarkTrimASCII/1:4-4 71.8 106 +47.63% BenchmarkTrimASCII/1:8-4 71.2 113 +58.71% BenchmarkTrimASCII/1:16-4 71.6 128 +78.77% BenchmarkTrimASCII/16:1-4 152 116 -23.68% BenchmarkTrimASCII/16:2-4 160 168 +5.00% BenchmarkTrimASCII/16:4-4 172 170 -1.16% BenchmarkTrimASCII/16:8-4 200 177 -11.50% BenchmarkTrimASCII/16:16-4 254 193 -24.02% BenchmarkTrimASCII/256:1-4 1438 864 -39.92% BenchmarkTrimASCII/256:2-4 1551 1195 -22.95% BenchmarkTrimASCII/256:4-4 1770 1200 -32.20% BenchmarkTrimASCII/256:8-4 2195 1216 -44.60% BenchmarkTrimASCII/256:16-4 3054 1224 -59.92% BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20% BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77% BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90% BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81% BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68% The benchmarks added test the worst case. For IndexAny, that is when the charset matches none of the input. For Trim, it is when the charset matches all of the input. Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea Reviewed-on: https://go-review.googlesource.com/31593 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
{"a☺b☻c☹d", "cx", len("a☺b☻")},
{"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
{"aRegExp*", ".(|)*+?^$[]", 7},
{dots + dots + dots, " ", -1},
bytes, strings: optimize for ASCII sets In a large codebase within Google, there are thousands of uses of: ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight An analysis of their usage shows that over 97% of them only use character sets consisting of only ASCII symbols. Uses of ContainsAny|IndexAny|LastIndexAny: 6% are 1 character (e.g., "\n" or " ") 58% are 2-4 characters (e.g., "<>" or "\r\n\t ") 24% are 5-9 characters (e.g., "()[]*^$") 10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ") We optimize for ASCII sets, which are commonly used to search for "control" characters in some string. We don't optimize for the single character scenario since IndexRune or IndexByte could be used. Uses of Trim|TrimLeft|TrimRight: 71% are 1 character (e.g., "\n" or " ") 14% are 2 characters (e.g., "\r\n") 10% are 3-4 characters (e.g., " \t\r\n") 5% are 10+ characters (e.g., "0123456789abcdefABCDEF") We optimize for the single character case with a simple closured function that only checks for that character's value. We optimize for the medium and larger sets using a 16-byte bit-map representing a set of ASCII characters. The benchmarks below have the following suffix name "%d:%d" where the first number is the length of the input and the second number is the length of the charset. == bytes package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75% BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69% BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88% BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91% BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71% BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56% BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67% BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21% BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15% BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79% BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41% BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82% BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55% BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24% BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31% BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08% BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92% BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99% BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68% BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27% BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94% BenchmarkTrimASCII/1:2-4 79.0 105 +32.91% BenchmarkTrimASCII/1:4-4 79.6 109 +36.93% BenchmarkTrimASCII/1:8-4 78.8 118 +49.75% BenchmarkTrimASCII/1:16-4 80.2 132 +64.59% BenchmarkTrimASCII/16:1-4 243 116 -52.26% BenchmarkTrimASCII/16:2-4 243 171 -29.63% BenchmarkTrimASCII/16:4-4 243 176 -27.57% BenchmarkTrimASCII/16:8-4 241 184 -23.65% BenchmarkTrimASCII/16:16-4 238 199 -16.39% BenchmarkTrimASCII/256:1-4 2580 840 -67.44% BenchmarkTrimASCII/256:2-4 2603 1175 -54.86% BenchmarkTrimASCII/256:4-4 2572 1188 -53.81% BenchmarkTrimASCII/256:8-4 2550 1191 -53.29% BenchmarkTrimASCII/256:16-4 2585 1208 -53.27% BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37% BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86% BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66% BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88% BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21% == strings package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33% BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57% BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22% BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85% BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15% BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54% BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24% BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10% BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03% BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48% BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74% BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08% BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37% BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44% BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21% BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95% BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19% BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25% BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37% BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26% BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14% BenchmarkTrimASCII/1:2-4 73.3 103 +40.52% BenchmarkTrimASCII/1:4-4 71.8 106 +47.63% BenchmarkTrimASCII/1:8-4 71.2 113 +58.71% BenchmarkTrimASCII/1:16-4 71.6 128 +78.77% BenchmarkTrimASCII/16:1-4 152 116 -23.68% BenchmarkTrimASCII/16:2-4 160 168 +5.00% BenchmarkTrimASCII/16:4-4 172 170 -1.16% BenchmarkTrimASCII/16:8-4 200 177 -11.50% BenchmarkTrimASCII/16:16-4 254 193 -24.02% BenchmarkTrimASCII/256:1-4 1438 864 -39.92% BenchmarkTrimASCII/256:2-4 1551 1195 -22.95% BenchmarkTrimASCII/256:4-4 1770 1200 -32.20% BenchmarkTrimASCII/256:8-4 2195 1216 -44.60% BenchmarkTrimASCII/256:16-4 3054 1224 -59.92% BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20% BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77% BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90% BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81% BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68% The benchmarks added test the worst case. For IndexAny, that is when the charset matches none of the input. For Trim, it is when the charset matches all of the input. Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea Reviewed-on: https://go-review.googlesource.com/31593 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
{"012abcba210", "\xffb", 4},
{"012\x80bcb\x80210", "\xffb", 3},
}
var lastIndexAnyTests = []BinOpTest{
{"", "", -1},
{"", "a", -1},
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"aaa", "a", 2},
{"abc", "xyz", -1},
{"abc", "ab", 1},
bytes, strings: optimize for ASCII sets In a large codebase within Google, there are thousands of uses of: ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight An analysis of their usage shows that over 97% of them only use character sets consisting of only ASCII symbols. Uses of ContainsAny|IndexAny|LastIndexAny: 6% are 1 character (e.g., "\n" or " ") 58% are 2-4 characters (e.g., "<>" or "\r\n\t ") 24% are 5-9 characters (e.g., "()[]*^$") 10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ") We optimize for ASCII sets, which are commonly used to search for "control" characters in some string. We don't optimize for the single character scenario since IndexRune or IndexByte could be used. Uses of Trim|TrimLeft|TrimRight: 71% are 1 character (e.g., "\n" or " ") 14% are 2 characters (e.g., "\r\n") 10% are 3-4 characters (e.g., " \t\r\n") 5% are 10+ characters (e.g., "0123456789abcdefABCDEF") We optimize for the single character case with a simple closured function that only checks for that character's value. We optimize for the medium and larger sets using a 16-byte bit-map representing a set of ASCII characters. The benchmarks below have the following suffix name "%d:%d" where the first number is the length of the input and the second number is the length of the charset. == bytes package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75% BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69% BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88% BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91% BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71% BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56% BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67% BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21% BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15% BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79% BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41% BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82% BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55% BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24% BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31% BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08% BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92% BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99% BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68% BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27% BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94% BenchmarkTrimASCII/1:2-4 79.0 105 +32.91% BenchmarkTrimASCII/1:4-4 79.6 109 +36.93% BenchmarkTrimASCII/1:8-4 78.8 118 +49.75% BenchmarkTrimASCII/1:16-4 80.2 132 +64.59% BenchmarkTrimASCII/16:1-4 243 116 -52.26% BenchmarkTrimASCII/16:2-4 243 171 -29.63% BenchmarkTrimASCII/16:4-4 243 176 -27.57% BenchmarkTrimASCII/16:8-4 241 184 -23.65% BenchmarkTrimASCII/16:16-4 238 199 -16.39% BenchmarkTrimASCII/256:1-4 2580 840 -67.44% BenchmarkTrimASCII/256:2-4 2603 1175 -54.86% BenchmarkTrimASCII/256:4-4 2572 1188 -53.81% BenchmarkTrimASCII/256:8-4 2550 1191 -53.29% BenchmarkTrimASCII/256:16-4 2585 1208 -53.27% BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37% BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86% BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66% BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88% BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21% == strings package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33% BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57% BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22% BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85% BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15% BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54% BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24% BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10% BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03% BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48% BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74% BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08% BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37% BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44% BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21% BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95% BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19% BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25% BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37% BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26% BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14% BenchmarkTrimASCII/1:2-4 73.3 103 +40.52% BenchmarkTrimASCII/1:4-4 71.8 106 +47.63% BenchmarkTrimASCII/1:8-4 71.2 113 +58.71% BenchmarkTrimASCII/1:16-4 71.6 128 +78.77% BenchmarkTrimASCII/16:1-4 152 116 -23.68% BenchmarkTrimASCII/16:2-4 160 168 +5.00% BenchmarkTrimASCII/16:4-4 172 170 -1.16% BenchmarkTrimASCII/16:8-4 200 177 -11.50% BenchmarkTrimASCII/16:16-4 254 193 -24.02% BenchmarkTrimASCII/256:1-4 1438 864 -39.92% BenchmarkTrimASCII/256:2-4 1551 1195 -22.95% BenchmarkTrimASCII/256:4-4 1770 1200 -32.20% BenchmarkTrimASCII/256:8-4 2195 1216 -44.60% BenchmarkTrimASCII/256:16-4 3054 1224 -59.92% BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20% BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77% BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90% BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81% BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68% The benchmarks added test the worst case. For IndexAny, that is when the charset matches none of the input. For Trim, it is when the charset matches all of the input. Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea Reviewed-on: https://go-review.googlesource.com/31593 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
{"ab☺c", "x☺yz", 2},
{"a☺b☻c☹d", "cx", len("a☺b☻")},
{"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
{"a.RegExp*", ".(|)*+?^$[]", 8},
{dots + dots + dots, " ", -1},
bytes, strings: optimize for ASCII sets In a large codebase within Google, there are thousands of uses of: ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight An analysis of their usage shows that over 97% of them only use character sets consisting of only ASCII symbols. Uses of ContainsAny|IndexAny|LastIndexAny: 6% are 1 character (e.g., "\n" or " ") 58% are 2-4 characters (e.g., "<>" or "\r\n\t ") 24% are 5-9 characters (e.g., "()[]*^$") 10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ") We optimize for ASCII sets, which are commonly used to search for "control" characters in some string. We don't optimize for the single character scenario since IndexRune or IndexByte could be used. Uses of Trim|TrimLeft|TrimRight: 71% are 1 character (e.g., "\n" or " ") 14% are 2 characters (e.g., "\r\n") 10% are 3-4 characters (e.g., " \t\r\n") 5% are 10+ characters (e.g., "0123456789abcdefABCDEF") We optimize for the single character case with a simple closured function that only checks for that character's value. We optimize for the medium and larger sets using a 16-byte bit-map representing a set of ASCII characters. The benchmarks below have the following suffix name "%d:%d" where the first number is the length of the input and the second number is the length of the charset. == bytes package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75% BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69% BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88% BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91% BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71% BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56% BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67% BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21% BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15% BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79% BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41% BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82% BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55% BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24% BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31% BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08% BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92% BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99% BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68% BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27% BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94% BenchmarkTrimASCII/1:2-4 79.0 105 +32.91% BenchmarkTrimASCII/1:4-4 79.6 109 +36.93% BenchmarkTrimASCII/1:8-4 78.8 118 +49.75% BenchmarkTrimASCII/1:16-4 80.2 132 +64.59% BenchmarkTrimASCII/16:1-4 243 116 -52.26% BenchmarkTrimASCII/16:2-4 243 171 -29.63% BenchmarkTrimASCII/16:4-4 243 176 -27.57% BenchmarkTrimASCII/16:8-4 241 184 -23.65% BenchmarkTrimASCII/16:16-4 238 199 -16.39% BenchmarkTrimASCII/256:1-4 2580 840 -67.44% BenchmarkTrimASCII/256:2-4 2603 1175 -54.86% BenchmarkTrimASCII/256:4-4 2572 1188 -53.81% BenchmarkTrimASCII/256:8-4 2550 1191 -53.29% BenchmarkTrimASCII/256:16-4 2585 1208 -53.27% BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37% BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86% BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66% BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88% BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21% == strings package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33% BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57% BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22% BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85% BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15% BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54% BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24% BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10% BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03% BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48% BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74% BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08% BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37% BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44% BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21% BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95% BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19% BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25% BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37% BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26% BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14% BenchmarkTrimASCII/1:2-4 73.3 103 +40.52% BenchmarkTrimASCII/1:4-4 71.8 106 +47.63% BenchmarkTrimASCII/1:8-4 71.2 113 +58.71% BenchmarkTrimASCII/1:16-4 71.6 128 +78.77% BenchmarkTrimASCII/16:1-4 152 116 -23.68% BenchmarkTrimASCII/16:2-4 160 168 +5.00% BenchmarkTrimASCII/16:4-4 172 170 -1.16% BenchmarkTrimASCII/16:8-4 200 177 -11.50% BenchmarkTrimASCII/16:16-4 254 193 -24.02% BenchmarkTrimASCII/256:1-4 1438 864 -39.92% BenchmarkTrimASCII/256:2-4 1551 1195 -22.95% BenchmarkTrimASCII/256:4-4 1770 1200 -32.20% BenchmarkTrimASCII/256:8-4 2195 1216 -44.60% BenchmarkTrimASCII/256:16-4 3054 1224 -59.92% BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20% BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77% BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90% BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81% BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68% The benchmarks added test the worst case. For IndexAny, that is when the charset matches none of the input. For Trim, it is when the charset matches all of the input. Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea Reviewed-on: https://go-review.googlesource.com/31593 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
{"012abcba210", "\xffb", 6},
{"012\x80bcb\x80210", "\xffb", 7},
}
// Execute f on each test case. funcName should be the name of f; it's used
// in failure reports.
func runIndexTests(t *testing.T, f func(s, sep []byte) int, funcName string, testCases []BinOpTest) {
for _, test := range testCases {
a := []byte(test.a)
b := []byte(test.b)
actual := f(a, b)
if actual != test.i {
t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, b, actual, test.i)
}
}
}
func runIndexAnyTests(t *testing.T, f func(s []byte, chars string) int, funcName string, testCases []BinOpTest) {
for _, test := range testCases {
a := []byte(test.a)
actual := f(a, test.b)
if actual != test.i {
t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, test.b, actual, test.i)
}
}
}
func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) }
func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) }
func TestIndexAny(t *testing.T) { runIndexAnyTests(t, IndexAny, "IndexAny", indexAnyTests) }
func TestLastIndexAny(t *testing.T) {
runIndexAnyTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests)
}
func TestIndexByte(t *testing.T) {
for _, tt := range indexTests {
if len(tt.b) != 1 {
continue
}
a := []byte(tt.a)
b := tt.b[0]
pos := IndexByte(a, b)
if pos != tt.i {
t.Errorf(`IndexByte(%q, '%c') = %v`, tt.a, b, pos)
}
posp := IndexBytePortable(a, b)
bytes: asm for bytes.IndexByte PERFORMANCE DIFFERENCE SUMMARY amd64 386 2.2 GHz AMD Opteron 8214 HE (Linux) 3.0x faster 8.2x faster 3.60 GHz Intel Xeon (Linux) 2.2x faster 6.2x faster 2.53 GHz Intel Core2 Duo E7200 (Linux) 1.5x faster 4.4x faster 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Xeon E5435 (Linux) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) 1.4x SLOWER 3.0x faster 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) none* 3.0x faster * but yesterday I consistently saw 1.4x SLOWER. DETAILS 2.2 GHz AMD Opteron 8214 HE (Linux) amd64 (3x faster) IndexByte4K 500000 3733 ns/op 1097.24 MB/s IndexByte4M 500 4328042 ns/op 969.10 MB/s IndexByte64M 50 67866160 ns/op 988.84 MB/s IndexBytePortable4K 200000 11161 ns/op 366.99 MB/s IndexBytePortable4M 100 11795880 ns/op 355.57 MB/s IndexBytePortable64M 10 188675000 ns/op 355.68 MB/s 386 (8.2x faster) IndexByte4K 500000 3734 ns/op 1096.95 MB/s IndexByte4M 500 4209954 ns/op 996.28 MB/s IndexByte64M 50 68031980 ns/op 986.43 MB/s IndexBytePortable4K 50000 30670 ns/op 133.55 MB/s IndexBytePortable4M 50 31868220 ns/op 131.61 MB/s IndexBytePortable64M 2 508851500 ns/op 131.88 MB/s 3.60 GHz Intel Xeon (Linux) amd64 (2.2x faster) IndexByte4K 500000 4612 ns/op 888.12 MB/s IndexByte4M 500 4835250 ns/op 867.44 MB/s IndexByte64M 20 77388450 ns/op 867.17 MB/s IndexBytePortable4K 200000 10306 ns/op 397.44 MB/s IndexBytePortable4M 100 11201460 ns/op 374.44 MB/s IndexBytePortable64M 10 179456800 ns/op 373.96 MB/s 386 (6.3x faster) IndexByte4K 500000 4631 ns/op 884.47 MB/s IndexByte4M 500 4846388 ns/op 865.45 MB/s IndexByte64M 20 78691200 ns/op 852.81 MB/s IndexBytePortable4K 100000 28989 ns/op 141.29 MB/s IndexBytePortable4M 50 31183180 ns/op 134.51 MB/s IndexBytePortable64M 5 498347200 ns/op 134.66 MB/s 2.53 GHz Intel Core2 Duo E7200 (Linux) amd64 (1.5x faster) IndexByte4K 500000 6502 ns/op 629.96 MB/s IndexByte4M 500 6692208 ns/op 626.74 MB/s IndexByte64M 10 107410400 ns/op 624.79 MB/s IndexBytePortable4K 200000 9721 ns/op 421.36 MB/s IndexBytePortable4M 100 10013680 ns/op 418.86 MB/s IndexBytePortable64M 10 160460800 ns/op 418.23 MB/s 386 (4.4x faster) IndexByte4K 500000 6505 ns/op 629.67 MB/s IndexByte4M 500 6694078 ns/op 626.57 MB/s IndexByte64M 10 107397600 ns/op 624.86 MB/s IndexBytePortable4K 100000 28835 ns/op 142.05 MB/s IndexBytePortable4M 50 29562680 ns/op 141.88 MB/s IndexBytePortable64M 5 473221400 ns/op 141.81 MB/s 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) amd64 (1.5x SLOWER) IndexByte4K 200000 9290 ns/op 440.90 MB/s IndexByte4M 200 9568925 ns/op 438.33 MB/s IndexByte64M 10 154473600 ns/op 434.44 MB/s IndexBytePortable4K 500000 6202 ns/op 660.43 MB/s IndexBytePortable4M 500 6583614 ns/op 637.08 MB/s IndexBytePortable64M 20 107166250 ns/op 626.21 MB/s 386 (3x faster) IndexByte4K 200000 9301 ns/op 440.38 MB/s IndexByte4M 200 9568025 ns/op 438.37 MB/s IndexByte64M 10 154391000 ns/op 434.67 MB/s IndexBytePortable4K 100000 27526 ns/op 148.80 MB/s IndexBytePortable4M 100 28302490 ns/op 148.20 MB/s IndexBytePortable64M 5 454170200 ns/op 147.76 MB/s 2.33 GHz Intel Xeon E5435 (Linux) amd64 (1.5x SLOWER) IndexByte4K 200000 10601 ns/op 386.38 MB/s IndexByte4M 100 10827240 ns/op 387.38 MB/s IndexByte64M 10 173175500 ns/op 387.52 MB/s IndexBytePortable4K 500000 7082 ns/op 578.37 MB/s IndexBytePortable4M 500 7391792 ns/op 567.43 MB/s IndexBytePortable64M 20 122618550 ns/op 547.30 MB/s 386 (3x faster) IndexByte4K 200000 11074 ns/op 369.88 MB/s IndexByte4M 100 10902620 ns/op 384.71 MB/s IndexByte64M 10 181292800 ns/op 370.17 MB/s IndexBytePortable4K 50000 31725 ns/op 129.11 MB/s IndexBytePortable4M 50 32564880 ns/op 128.80 MB/s IndexBytePortable64M 2 545926000 ns/op 122.93 MB/s 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) amd64 (1.4x SLOWER) IndexByte4K 200000 11120 ns/op 368.35 MB/s IndexByte4M 100 11531950 ns/op 363.71 MB/s IndexByte64M 10 184819000 ns/op 363.11 MB/s IndexBytePortable4K 500000 7419 ns/op 552.10 MB/s IndexBytePortable4M 200 8018710 ns/op 523.06 MB/s IndexBytePortable64M 10 127614900 ns/op 525.87 MB/s 386 (3x faster) IndexByte4K 200000 11114 ns/op 368.54 MB/s IndexByte4M 100 11443530 ns/op 366.52 MB/s IndexByte64M 10 185212000 ns/op 362.34 MB/s IndexBytePortable4K 50000 32891 ns/op 124.53 MB/s IndexBytePortable4M 50 33930580 ns/op 123.61 MB/s IndexBytePortable64M 2 545400500 ns/op 123.05 MB/s 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) amd64 (no difference) IndexByte4K 200000 13497 ns/op 303.47 MB/s IndexByte4M 100 13890650 ns/op 301.95 MB/s IndexByte64M 5 222358000 ns/op 301.81 MB/s IndexBytePortable4K 200000 13584 ns/op 301.53 MB/s IndexBytePortable4M 100 13913280 ns/op 301.46 MB/s IndexBytePortable64M 10 222572600 ns/op 301.51 MB/s 386 (3x faster) IndexByte4K 200000 13565 ns/op 301.95 MB/s IndexByte4M 100 13882640 ns/op 302.13 MB/s IndexByte64M 5 221411600 ns/op 303.10 MB/s IndexBytePortable4K 50000 39978 ns/op 102.46 MB/s IndexBytePortable4M 50 41038160 ns/op 102.20 MB/s IndexBytePortable64M 2 656362500 ns/op 102.24 MB/s R=r CC=golang-dev https://golang.org/cl/166055
2009-12-04 10:23:43 -08:00
if posp != tt.i {
t.Errorf(`indexBytePortable(%q, '%c') = %v`, tt.a, b, posp)
}
}
}
func TestLastIndexByte(t *testing.T) {
testCases := []BinOpTest{
{"", "q", -1},
{"abcdef", "q", -1},
{"abcdefabcdef", "a", len("abcdef")}, // something in the middle
{"abcdefabcdef", "f", len("abcdefabcde")}, // last byte
{"zabcdefabcdef", "z", 0}, // first byte
{"a☺b☻c☹d", "b", len("a☺")}, // non-ascii
}
for _, test := range testCases {
actual := LastIndexByte([]byte(test.a), test.b[0])
if actual != test.i {
t.Errorf("LastIndexByte(%q,%c) = %v; want %v", test.a, test.b[0], actual, test.i)
}
}
}
// test a larger buffer with different sizes and alignments
func TestIndexByteBig(t *testing.T) {
var n = 1024
if testing.Short() {
n = 128
}
b := make([]byte, n)
for i := 0; i < n; i++ {
// different start alignments
b1 := b[i:]
for j := 0; j < len(b1); j++ {
b1[j] = 'x'
pos := IndexByte(b1, 'x')
if pos != j {
t.Errorf("IndexByte(%q, 'x') = %v", b1, pos)
}
b1[j] = 0
pos = IndexByte(b1, 'x')
if pos != -1 {
t.Errorf("IndexByte(%q, 'x') = %v", b1, pos)
}
}
// different end alignments
b1 = b[:i]
for j := 0; j < len(b1); j++ {
b1[j] = 'x'
pos := IndexByte(b1, 'x')
if pos != j {
t.Errorf("IndexByte(%q, 'x') = %v", b1, pos)
}
b1[j] = 0
pos = IndexByte(b1, 'x')
if pos != -1 {
t.Errorf("IndexByte(%q, 'x') = %v", b1, pos)
}
}
// different start and end alignments
b1 = b[i/2 : n-(i+1)/2]
for j := 0; j < len(b1); j++ {
b1[j] = 'x'
pos := IndexByte(b1, 'x')
if pos != j {
t.Errorf("IndexByte(%q, 'x') = %v", b1, pos)
}
b1[j] = 0
pos = IndexByte(b1, 'x')
if pos != -1 {
t.Errorf("IndexByte(%q, 'x') = %v", b1, pos)
}
}
}
}
// test a small index across all page offsets
func TestIndexByteSmall(t *testing.T) {
b := make([]byte, 5015) // bigger than a page
// Make sure we find the correct byte even when straddling a page.
for i := 0; i <= len(b)-15; i++ {
for j := 0; j < 15; j++ {
b[i+j] = byte(100 + j)
}
for j := 0; j < 15; j++ {
p := IndexByte(b[i:i+15], byte(100+j))
if p != j {
t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 100+j, p)
}
}
for j := 0; j < 15; j++ {
b[i+j] = 0
}
}
// Make sure matches outside the slice never trigger.
for i := 0; i <= len(b)-15; i++ {
for j := 0; j < 15; j++ {
b[i+j] = 1
}
for j := 0; j < 15; j++ {
p := IndexByte(b[i:i+15], byte(0))
if p != -1 {
t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 0, p)
}
}
for j := 0; j < 15; j++ {
b[i+j] = 0
}
}
}
func TestIndexRune(t *testing.T) {
tests := []struct {
in string
rune rune
want int
}{
{"", 'a', -1},
{"", '☺', -1},
{"foo", '☹', -1},
{"foo", 'o', 1},
{"foo☺bar", '☺', 3},
{"foo☺☻☹bar", '☹', 9},
{"a A x", 'A', 2},
{"some_text=some_value", '=', 9},
{"☺a", 'a', 3},
{"a☻☺b", '☺', 4},
// RuneError should match any invalid UTF-8 byte sequence.
{"<22>", '<27>', 0},
{"\xff", '<27>', 0},
{"☻x<E298BB>", '<27>', len("☻x")},
{"☻x\xe2\x98", '<27>', len("☻x")},
{"☻x\xe2\x98<39>", '<27>', len("☻x")},
{"☻x\xe2\x98x", '<27>', len("☻x")},
// Invalid rune values should never match.
{"a☺b☻c☹d\xe2\x98<39>\xff<66>\xed\xa0\x80", -1, -1},
{"a☺b☻c☹d\xe2\x98<39>\xff<66>\xed\xa0\x80", 0xD800, -1}, // Surrogate pair
{"a☺b☻c☹d\xe2\x98<39>\xff<66>\xed\xa0\x80", utf8.MaxRune + 1, -1},
}
for _, tt := range tests {
if got := IndexRune([]byte(tt.in), tt.rune); got != tt.want {
t.Errorf("IndexRune(%q, %d) = %v; want %v", tt.in, tt.rune, got, tt.want)
}
}
bytes: make IndexRune faster re-implement IndexRune by IndexByte and Index which are well optimized to get performance gain. name old time/op new time/op delta IndexRune/10-4 53.2ns ± 1% 29.1ns ± 1% -45.32% (p=0.008 n=5+5) IndexRune/32-4 191ns ± 1% 27ns ± 1% -85.75% (p=0.008 n=5+5) IndexRune/4K-4 23.5µs ± 1% 1.0µs ± 1% -95.77% (p=0.008 n=5+5) IndexRune/4M-4 23.8ms ± 0% 1.0ms ± 2% -95.90% (p=0.008 n=5+5) IndexRune/64M-4 384ms ± 1% 15ms ± 1% -95.98% (p=0.008 n=5+5) IndexRuneASCII/10-4 61.5ns ± 0% 10.3ns ± 4% -83.17% (p=0.008 n=5+5) IndexRuneASCII/32-4 203ns ± 0% 11ns ± 5% -94.68% (p=0.008 n=5+5) IndexRuneASCII/4K-4 23.4µs ± 0% 0.3µs ± 2% -98.60% (p=0.008 n=5+5) IndexRuneASCII/4M-4 24.0ms ± 1% 0.3ms ± 1% -98.60% (p=0.008 n=5+5) IndexRuneASCII/64M-4 386ms ± 2% 6ms ± 1% -98.57% (p=0.008 n=5+5) name old speed new speed delta IndexRune/10-4 188MB/s ± 1% 344MB/s ± 1% +82.91% (p=0.008 n=5+5) IndexRune/32-4 167MB/s ± 0% 1175MB/s ± 1% +603.52% (p=0.008 n=5+5) IndexRune/4K-4 174MB/s ± 1% 4117MB/s ± 1% +2262.71% (p=0.008 n=5+5) IndexRune/4M-4 176MB/s ± 0% 4299MB/s ± 2% +2340.46% (p=0.008 n=5+5) IndexRune/64M-4 175MB/s ± 1% 4354MB/s ± 1% +2388.57% (p=0.008 n=5+5) IndexRuneASCII/10-4 163MB/s ± 0% 968MB/s ± 4% +494.66% (p=0.008 n=5+5) IndexRuneASCII/32-4 157MB/s ± 0% 2974MB/s ± 4% +1788.59% (p=0.008 n=5+5) IndexRuneASCII/4K-4 175MB/s ± 0% 12481MB/s ± 2% +7027.71% (p=0.008 n=5+5) IndexRuneASCII/4M-4 175MB/s ± 1% 12510MB/s ± 1% +7061.15% (p=0.008 n=5+5) IndexRuneASCII/64M-4 174MB/s ± 2% 12143MB/s ± 1% +6881.70% (p=0.008 n=5+5) Change-Id: I0632eadb83937c2a9daa7f0ce79df1dee64f992e Reviewed-on: https://go-review.googlesource.com/28537 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-09-06 08:09:27 +09:00
haystack := []byte("test世界")
allocs := testing.AllocsPerRun(1000, func() {
if i := IndexRune(haystack, 's'); i != 2 {
t.Fatalf("'s' at %d; want 2", i)
}
if i := IndexRune(haystack, '世'); i != 4 {
t.Fatalf("'世' at %d; want 4", i)
}
})
if allocs != 0 {
t.Errorf("expected no allocations, got %f", allocs)
bytes: make IndexRune faster re-implement IndexRune by IndexByte and Index which are well optimized to get performance gain. name old time/op new time/op delta IndexRune/10-4 53.2ns ± 1% 29.1ns ± 1% -45.32% (p=0.008 n=5+5) IndexRune/32-4 191ns ± 1% 27ns ± 1% -85.75% (p=0.008 n=5+5) IndexRune/4K-4 23.5µs ± 1% 1.0µs ± 1% -95.77% (p=0.008 n=5+5) IndexRune/4M-4 23.8ms ± 0% 1.0ms ± 2% -95.90% (p=0.008 n=5+5) IndexRune/64M-4 384ms ± 1% 15ms ± 1% -95.98% (p=0.008 n=5+5) IndexRuneASCII/10-4 61.5ns ± 0% 10.3ns ± 4% -83.17% (p=0.008 n=5+5) IndexRuneASCII/32-4 203ns ± 0% 11ns ± 5% -94.68% (p=0.008 n=5+5) IndexRuneASCII/4K-4 23.4µs ± 0% 0.3µs ± 2% -98.60% (p=0.008 n=5+5) IndexRuneASCII/4M-4 24.0ms ± 1% 0.3ms ± 1% -98.60% (p=0.008 n=5+5) IndexRuneASCII/64M-4 386ms ± 2% 6ms ± 1% -98.57% (p=0.008 n=5+5) name old speed new speed delta IndexRune/10-4 188MB/s ± 1% 344MB/s ± 1% +82.91% (p=0.008 n=5+5) IndexRune/32-4 167MB/s ± 0% 1175MB/s ± 1% +603.52% (p=0.008 n=5+5) IndexRune/4K-4 174MB/s ± 1% 4117MB/s ± 1% +2262.71% (p=0.008 n=5+5) IndexRune/4M-4 176MB/s ± 0% 4299MB/s ± 2% +2340.46% (p=0.008 n=5+5) IndexRune/64M-4 175MB/s ± 1% 4354MB/s ± 1% +2388.57% (p=0.008 n=5+5) IndexRuneASCII/10-4 163MB/s ± 0% 968MB/s ± 4% +494.66% (p=0.008 n=5+5) IndexRuneASCII/32-4 157MB/s ± 0% 2974MB/s ± 4% +1788.59% (p=0.008 n=5+5) IndexRuneASCII/4K-4 175MB/s ± 0% 12481MB/s ± 2% +7027.71% (p=0.008 n=5+5) IndexRuneASCII/4M-4 175MB/s ± 1% 12510MB/s ± 1% +7061.15% (p=0.008 n=5+5) IndexRuneASCII/64M-4 174MB/s ± 2% 12143MB/s ± 1% +6881.70% (p=0.008 n=5+5) Change-Id: I0632eadb83937c2a9daa7f0ce79df1dee64f992e Reviewed-on: https://go-review.googlesource.com/28537 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-09-06 08:09:27 +09:00
}
}
bytes: add optimized countByte for amd64 Use SSE/AVX2 when counting a single byte. Inspired from runtime indexbyte implementation. Benchmark against previous implementation, where 1 byte in every 8 is the one we are looking for: * On a machine without AVX2 name old time/op new time/op delta CountSingle/10-4 61.8ns ±10% 15.6ns ±11% -74.83% (p=0.000 n=10+10) CountSingle/32-4 100ns ± 4% 17ns ±10% -82.54% (p=0.000 n=10+9) CountSingle/4K-4 9.66µs ± 3% 0.37µs ± 6% -96.21% (p=0.000 n=10+10) CountSingle/4M-4 11.0ms ± 6% 0.4ms ± 4% -96.04% (p=0.000 n=10+10) CountSingle/64M-4 194ms ± 8% 8ms ± 2% -95.64% (p=0.000 n=10+10) name old speed new speed delta CountSingle/10-4 162MB/s ±10% 645MB/s ±10% +297.00% (p=0.000 n=10+10) CountSingle/32-4 321MB/s ± 5% 1844MB/s ± 9% +474.79% (p=0.000 n=10+9) CountSingle/4K-4 424MB/s ± 3% 11169MB/s ± 6% +2533.10% (p=0.000 n=10+10) CountSingle/4M-4 381MB/s ± 7% 9609MB/s ± 4% +2421.88% (p=0.000 n=10+10) CountSingle/64M-4 346MB/s ± 7% 7924MB/s ± 2% +2188.78% (p=0.000 n=10+10) * On a machine with AVX2 name old time/op new time/op delta CountSingle/10-8 37.1ns ± 3% 8.2ns ± 1% -77.80% (p=0.000 n=10+10) CountSingle/32-8 66.1ns ± 3% 9.8ns ± 2% -85.23% (p=0.000 n=10+10) CountSingle/4K-8 7.36µs ± 3% 0.11µs ± 1% -98.54% (p=0.000 n=10+10) CountSingle/4M-8 7.46ms ± 2% 0.15ms ± 2% -97.95% (p=0.000 n=10+9) CountSingle/64M-8 124ms ± 2% 6ms ± 4% -95.09% (p=0.000 n=10+10) name old speed new speed delta CountSingle/10-8 269MB/s ± 3% 1213MB/s ± 1% +350.32% (p=0.000 n=10+10) CountSingle/32-8 484MB/s ± 4% 3277MB/s ± 2% +576.66% (p=0.000 n=10+10) CountSingle/4K-8 556MB/s ± 3% 37933MB/s ± 1% +6718.36% (p=0.000 n=10+10) CountSingle/4M-8 562MB/s ± 2% 27444MB/s ± 3% +4783.43% (p=0.000 n=10+9) CountSingle/64M-8 543MB/s ± 2% 11054MB/s ± 3% +1935.81% (p=0.000 n=10+10) Fixes #19411 Change-Id: Ieaf20b1fabccabe767c55c66e242e86f3617f883 Reviewed-on: https://go-review.googlesource.com/38258 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2017-03-19 12:18:08 +01:00
// test count of a single byte across page offsets
func TestCountByte(t *testing.T) {
b := make([]byte, 5015) // bigger than a page
windows := []int{1, 2, 3, 4, 15, 16, 17, 31, 32, 33, 63, 64, 65, 128}
testCountWindow := func(i, window int) {
for j := 0; j < window; j++ {
b[i+j] = byte(100)
p := Count(b[i:i+window], []byte{100})
if p != j+1 {
t.Errorf("TestCountByte.Count(%q, 100) = %d", b[i:i+window], p)
}
}
}
maxWnd := windows[len(windows)-1]
for i := 0; i <= 2*maxWnd; i++ {
for _, window := range windows {
if window > len(b[i:]) {
window = len(b[i:])
}
testCountWindow(i, window)
for j := 0; j < window; j++ {
b[i+j] = byte(0)
}
}
}
for i := 4096 - (maxWnd + 1); i < len(b); i++ {
for _, window := range windows {
if window > len(b[i:]) {
window = len(b[i:])
}
testCountWindow(i, window)
for j := 0; j < window; j++ {
b[i+j] = byte(0)
}
}
}
}
// Make sure we don't count bytes outside our window
func TestCountByteNoMatch(t *testing.T) {
b := make([]byte, 5015)
windows := []int{1, 2, 3, 4, 15, 16, 17, 31, 32, 33, 63, 64, 65, 128}
for i := 0; i <= len(b); i++ {
for _, window := range windows {
if window > len(b[i:]) {
window = len(b[i:])
}
// Fill the window with non-match
for j := 0; j < window; j++ {
b[i+j] = byte(100)
}
// Try to find something that doesn't exist
p := Count(b[i:i+window], []byte{0})
if p != 0 {
t.Errorf("TestCountByteNoMatch(%q, 0) = %d", b[i:i+window], p)
}
for j := 0; j < window; j++ {
b[i+j] = byte(0)
}
}
}
}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
var bmbuf []byte
bytes: asm for bytes.IndexByte PERFORMANCE DIFFERENCE SUMMARY amd64 386 2.2 GHz AMD Opteron 8214 HE (Linux) 3.0x faster 8.2x faster 3.60 GHz Intel Xeon (Linux) 2.2x faster 6.2x faster 2.53 GHz Intel Core2 Duo E7200 (Linux) 1.5x faster 4.4x faster 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Xeon E5435 (Linux) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) 1.4x SLOWER 3.0x faster 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) none* 3.0x faster * but yesterday I consistently saw 1.4x SLOWER. DETAILS 2.2 GHz AMD Opteron 8214 HE (Linux) amd64 (3x faster) IndexByte4K 500000 3733 ns/op 1097.24 MB/s IndexByte4M 500 4328042 ns/op 969.10 MB/s IndexByte64M 50 67866160 ns/op 988.84 MB/s IndexBytePortable4K 200000 11161 ns/op 366.99 MB/s IndexBytePortable4M 100 11795880 ns/op 355.57 MB/s IndexBytePortable64M 10 188675000 ns/op 355.68 MB/s 386 (8.2x faster) IndexByte4K 500000 3734 ns/op 1096.95 MB/s IndexByte4M 500 4209954 ns/op 996.28 MB/s IndexByte64M 50 68031980 ns/op 986.43 MB/s IndexBytePortable4K 50000 30670 ns/op 133.55 MB/s IndexBytePortable4M 50 31868220 ns/op 131.61 MB/s IndexBytePortable64M 2 508851500 ns/op 131.88 MB/s 3.60 GHz Intel Xeon (Linux) amd64 (2.2x faster) IndexByte4K 500000 4612 ns/op 888.12 MB/s IndexByte4M 500 4835250 ns/op 867.44 MB/s IndexByte64M 20 77388450 ns/op 867.17 MB/s IndexBytePortable4K 200000 10306 ns/op 397.44 MB/s IndexBytePortable4M 100 11201460 ns/op 374.44 MB/s IndexBytePortable64M 10 179456800 ns/op 373.96 MB/s 386 (6.3x faster) IndexByte4K 500000 4631 ns/op 884.47 MB/s IndexByte4M 500 4846388 ns/op 865.45 MB/s IndexByte64M 20 78691200 ns/op 852.81 MB/s IndexBytePortable4K 100000 28989 ns/op 141.29 MB/s IndexBytePortable4M 50 31183180 ns/op 134.51 MB/s IndexBytePortable64M 5 498347200 ns/op 134.66 MB/s 2.53 GHz Intel Core2 Duo E7200 (Linux) amd64 (1.5x faster) IndexByte4K 500000 6502 ns/op 629.96 MB/s IndexByte4M 500 6692208 ns/op 626.74 MB/s IndexByte64M 10 107410400 ns/op 624.79 MB/s IndexBytePortable4K 200000 9721 ns/op 421.36 MB/s IndexBytePortable4M 100 10013680 ns/op 418.86 MB/s IndexBytePortable64M 10 160460800 ns/op 418.23 MB/s 386 (4.4x faster) IndexByte4K 500000 6505 ns/op 629.67 MB/s IndexByte4M 500 6694078 ns/op 626.57 MB/s IndexByte64M 10 107397600 ns/op 624.86 MB/s IndexBytePortable4K 100000 28835 ns/op 142.05 MB/s IndexBytePortable4M 50 29562680 ns/op 141.88 MB/s IndexBytePortable64M 5 473221400 ns/op 141.81 MB/s 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) amd64 (1.5x SLOWER) IndexByte4K 200000 9290 ns/op 440.90 MB/s IndexByte4M 200 9568925 ns/op 438.33 MB/s IndexByte64M 10 154473600 ns/op 434.44 MB/s IndexBytePortable4K 500000 6202 ns/op 660.43 MB/s IndexBytePortable4M 500 6583614 ns/op 637.08 MB/s IndexBytePortable64M 20 107166250 ns/op 626.21 MB/s 386 (3x faster) IndexByte4K 200000 9301 ns/op 440.38 MB/s IndexByte4M 200 9568025 ns/op 438.37 MB/s IndexByte64M 10 154391000 ns/op 434.67 MB/s IndexBytePortable4K 100000 27526 ns/op 148.80 MB/s IndexBytePortable4M 100 28302490 ns/op 148.20 MB/s IndexBytePortable64M 5 454170200 ns/op 147.76 MB/s 2.33 GHz Intel Xeon E5435 (Linux) amd64 (1.5x SLOWER) IndexByte4K 200000 10601 ns/op 386.38 MB/s IndexByte4M 100 10827240 ns/op 387.38 MB/s IndexByte64M 10 173175500 ns/op 387.52 MB/s IndexBytePortable4K 500000 7082 ns/op 578.37 MB/s IndexBytePortable4M 500 7391792 ns/op 567.43 MB/s IndexBytePortable64M 20 122618550 ns/op 547.30 MB/s 386 (3x faster) IndexByte4K 200000 11074 ns/op 369.88 MB/s IndexByte4M 100 10902620 ns/op 384.71 MB/s IndexByte64M 10 181292800 ns/op 370.17 MB/s IndexBytePortable4K 50000 31725 ns/op 129.11 MB/s IndexBytePortable4M 50 32564880 ns/op 128.80 MB/s IndexBytePortable64M 2 545926000 ns/op 122.93 MB/s 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) amd64 (1.4x SLOWER) IndexByte4K 200000 11120 ns/op 368.35 MB/s IndexByte4M 100 11531950 ns/op 363.71 MB/s IndexByte64M 10 184819000 ns/op 363.11 MB/s IndexBytePortable4K 500000 7419 ns/op 552.10 MB/s IndexBytePortable4M 200 8018710 ns/op 523.06 MB/s IndexBytePortable64M 10 127614900 ns/op 525.87 MB/s 386 (3x faster) IndexByte4K 200000 11114 ns/op 368.54 MB/s IndexByte4M 100 11443530 ns/op 366.52 MB/s IndexByte64M 10 185212000 ns/op 362.34 MB/s IndexBytePortable4K 50000 32891 ns/op 124.53 MB/s IndexBytePortable4M 50 33930580 ns/op 123.61 MB/s IndexBytePortable64M 2 545400500 ns/op 123.05 MB/s 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) amd64 (no difference) IndexByte4K 200000 13497 ns/op 303.47 MB/s IndexByte4M 100 13890650 ns/op 301.95 MB/s IndexByte64M 5 222358000 ns/op 301.81 MB/s IndexBytePortable4K 200000 13584 ns/op 301.53 MB/s IndexBytePortable4M 100 13913280 ns/op 301.46 MB/s IndexBytePortable64M 10 222572600 ns/op 301.51 MB/s 386 (3x faster) IndexByte4K 200000 13565 ns/op 301.95 MB/s IndexByte4M 100 13882640 ns/op 302.13 MB/s IndexByte64M 5 221411600 ns/op 303.10 MB/s IndexBytePortable4K 50000 39978 ns/op 102.46 MB/s IndexBytePortable4M 50 41038160 ns/op 102.20 MB/s IndexBytePortable64M 2 656362500 ns/op 102.24 MB/s R=r CC=golang-dev https://golang.org/cl/166055
2009-12-04 10:23:43 -08:00
func valName(x int) string {
if s := x >> 20; s<<20 == x {
return fmt.Sprintf("%dM", s)
}
if s := x >> 10; s<<10 == x {
return fmt.Sprintf("%dK", s)
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
return fmt.Sprint(x)
bytes: asm for bytes.IndexByte PERFORMANCE DIFFERENCE SUMMARY amd64 386 2.2 GHz AMD Opteron 8214 HE (Linux) 3.0x faster 8.2x faster 3.60 GHz Intel Xeon (Linux) 2.2x faster 6.2x faster 2.53 GHz Intel Core2 Duo E7200 (Linux) 1.5x faster 4.4x faster 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Xeon E5435 (Linux) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) 1.4x SLOWER 3.0x faster 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) none* 3.0x faster * but yesterday I consistently saw 1.4x SLOWER. DETAILS 2.2 GHz AMD Opteron 8214 HE (Linux) amd64 (3x faster) IndexByte4K 500000 3733 ns/op 1097.24 MB/s IndexByte4M 500 4328042 ns/op 969.10 MB/s IndexByte64M 50 67866160 ns/op 988.84 MB/s IndexBytePortable4K 200000 11161 ns/op 366.99 MB/s IndexBytePortable4M 100 11795880 ns/op 355.57 MB/s IndexBytePortable64M 10 188675000 ns/op 355.68 MB/s 386 (8.2x faster) IndexByte4K 500000 3734 ns/op 1096.95 MB/s IndexByte4M 500 4209954 ns/op 996.28 MB/s IndexByte64M 50 68031980 ns/op 986.43 MB/s IndexBytePortable4K 50000 30670 ns/op 133.55 MB/s IndexBytePortable4M 50 31868220 ns/op 131.61 MB/s IndexBytePortable64M 2 508851500 ns/op 131.88 MB/s 3.60 GHz Intel Xeon (Linux) amd64 (2.2x faster) IndexByte4K 500000 4612 ns/op 888.12 MB/s IndexByte4M 500 4835250 ns/op 867.44 MB/s IndexByte64M 20 77388450 ns/op 867.17 MB/s IndexBytePortable4K 200000 10306 ns/op 397.44 MB/s IndexBytePortable4M 100 11201460 ns/op 374.44 MB/s IndexBytePortable64M 10 179456800 ns/op 373.96 MB/s 386 (6.3x faster) IndexByte4K 500000 4631 ns/op 884.47 MB/s IndexByte4M 500 4846388 ns/op 865.45 MB/s IndexByte64M 20 78691200 ns/op 852.81 MB/s IndexBytePortable4K 100000 28989 ns/op 141.29 MB/s IndexBytePortable4M 50 31183180 ns/op 134.51 MB/s IndexBytePortable64M 5 498347200 ns/op 134.66 MB/s 2.53 GHz Intel Core2 Duo E7200 (Linux) amd64 (1.5x faster) IndexByte4K 500000 6502 ns/op 629.96 MB/s IndexByte4M 500 6692208 ns/op 626.74 MB/s IndexByte64M 10 107410400 ns/op 624.79 MB/s IndexBytePortable4K 200000 9721 ns/op 421.36 MB/s IndexBytePortable4M 100 10013680 ns/op 418.86 MB/s IndexBytePortable64M 10 160460800 ns/op 418.23 MB/s 386 (4.4x faster) IndexByte4K 500000 6505 ns/op 629.67 MB/s IndexByte4M 500 6694078 ns/op 626.57 MB/s IndexByte64M 10 107397600 ns/op 624.86 MB/s IndexBytePortable4K 100000 28835 ns/op 142.05 MB/s IndexBytePortable4M 50 29562680 ns/op 141.88 MB/s IndexBytePortable64M 5 473221400 ns/op 141.81 MB/s 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) amd64 (1.5x SLOWER) IndexByte4K 200000 9290 ns/op 440.90 MB/s IndexByte4M 200 9568925 ns/op 438.33 MB/s IndexByte64M 10 154473600 ns/op 434.44 MB/s IndexBytePortable4K 500000 6202 ns/op 660.43 MB/s IndexBytePortable4M 500 6583614 ns/op 637.08 MB/s IndexBytePortable64M 20 107166250 ns/op 626.21 MB/s 386 (3x faster) IndexByte4K 200000 9301 ns/op 440.38 MB/s IndexByte4M 200 9568025 ns/op 438.37 MB/s IndexByte64M 10 154391000 ns/op 434.67 MB/s IndexBytePortable4K 100000 27526 ns/op 148.80 MB/s IndexBytePortable4M 100 28302490 ns/op 148.20 MB/s IndexBytePortable64M 5 454170200 ns/op 147.76 MB/s 2.33 GHz Intel Xeon E5435 (Linux) amd64 (1.5x SLOWER) IndexByte4K 200000 10601 ns/op 386.38 MB/s IndexByte4M 100 10827240 ns/op 387.38 MB/s IndexByte64M 10 173175500 ns/op 387.52 MB/s IndexBytePortable4K 500000 7082 ns/op 578.37 MB/s IndexBytePortable4M 500 7391792 ns/op 567.43 MB/s IndexBytePortable64M 20 122618550 ns/op 547.30 MB/s 386 (3x faster) IndexByte4K 200000 11074 ns/op 369.88 MB/s IndexByte4M 100 10902620 ns/op 384.71 MB/s IndexByte64M 10 181292800 ns/op 370.17 MB/s IndexBytePortable4K 50000 31725 ns/op 129.11 MB/s IndexBytePortable4M 50 32564880 ns/op 128.80 MB/s IndexBytePortable64M 2 545926000 ns/op 122.93 MB/s 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) amd64 (1.4x SLOWER) IndexByte4K 200000 11120 ns/op 368.35 MB/s IndexByte4M 100 11531950 ns/op 363.71 MB/s IndexByte64M 10 184819000 ns/op 363.11 MB/s IndexBytePortable4K 500000 7419 ns/op 552.10 MB/s IndexBytePortable4M 200 8018710 ns/op 523.06 MB/s IndexBytePortable64M 10 127614900 ns/op 525.87 MB/s 386 (3x faster) IndexByte4K 200000 11114 ns/op 368.54 MB/s IndexByte4M 100 11443530 ns/op 366.52 MB/s IndexByte64M 10 185212000 ns/op 362.34 MB/s IndexBytePortable4K 50000 32891 ns/op 124.53 MB/s IndexBytePortable4M 50 33930580 ns/op 123.61 MB/s IndexBytePortable64M 2 545400500 ns/op 123.05 MB/s 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) amd64 (no difference) IndexByte4K 200000 13497 ns/op 303.47 MB/s IndexByte4M 100 13890650 ns/op 301.95 MB/s IndexByte64M 5 222358000 ns/op 301.81 MB/s IndexBytePortable4K 200000 13584 ns/op 301.53 MB/s IndexBytePortable4M 100 13913280 ns/op 301.46 MB/s IndexBytePortable64M 10 222572600 ns/op 301.51 MB/s 386 (3x faster) IndexByte4K 200000 13565 ns/op 301.95 MB/s IndexByte4M 100 13882640 ns/op 302.13 MB/s IndexByte64M 5 221411600 ns/op 303.10 MB/s IndexBytePortable4K 50000 39978 ns/op 102.46 MB/s IndexBytePortable4M 50 41038160 ns/op 102.20 MB/s IndexBytePortable64M 2 656362500 ns/op 102.24 MB/s R=r CC=golang-dev https://golang.org/cl/166055
2009-12-04 10:23:43 -08:00
}
func benchBytes(b *testing.B, sizes []int, f func(b *testing.B, n int)) {
for _, n := range sizes {
if isRaceBuilder && n > 4<<10 {
continue
}
b.Run(valName(n), func(b *testing.B) {
if len(bmbuf) < n {
bmbuf = make([]byte, n)
}
b.SetBytes(int64(n))
f(b, n)
})
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
bytes: asm for bytes.IndexByte PERFORMANCE DIFFERENCE SUMMARY amd64 386 2.2 GHz AMD Opteron 8214 HE (Linux) 3.0x faster 8.2x faster 3.60 GHz Intel Xeon (Linux) 2.2x faster 6.2x faster 2.53 GHz Intel Core2 Duo E7200 (Linux) 1.5x faster 4.4x faster 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Xeon E5435 (Linux) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) 1.4x SLOWER 3.0x faster 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) none* 3.0x faster * but yesterday I consistently saw 1.4x SLOWER. DETAILS 2.2 GHz AMD Opteron 8214 HE (Linux) amd64 (3x faster) IndexByte4K 500000 3733 ns/op 1097.24 MB/s IndexByte4M 500 4328042 ns/op 969.10 MB/s IndexByte64M 50 67866160 ns/op 988.84 MB/s IndexBytePortable4K 200000 11161 ns/op 366.99 MB/s IndexBytePortable4M 100 11795880 ns/op 355.57 MB/s IndexBytePortable64M 10 188675000 ns/op 355.68 MB/s 386 (8.2x faster) IndexByte4K 500000 3734 ns/op 1096.95 MB/s IndexByte4M 500 4209954 ns/op 996.28 MB/s IndexByte64M 50 68031980 ns/op 986.43 MB/s IndexBytePortable4K 50000 30670 ns/op 133.55 MB/s IndexBytePortable4M 50 31868220 ns/op 131.61 MB/s IndexBytePortable64M 2 508851500 ns/op 131.88 MB/s 3.60 GHz Intel Xeon (Linux) amd64 (2.2x faster) IndexByte4K 500000 4612 ns/op 888.12 MB/s IndexByte4M 500 4835250 ns/op 867.44 MB/s IndexByte64M 20 77388450 ns/op 867.17 MB/s IndexBytePortable4K 200000 10306 ns/op 397.44 MB/s IndexBytePortable4M 100 11201460 ns/op 374.44 MB/s IndexBytePortable64M 10 179456800 ns/op 373.96 MB/s 386 (6.3x faster) IndexByte4K 500000 4631 ns/op 884.47 MB/s IndexByte4M 500 4846388 ns/op 865.45 MB/s IndexByte64M 20 78691200 ns/op 852.81 MB/s IndexBytePortable4K 100000 28989 ns/op 141.29 MB/s IndexBytePortable4M 50 31183180 ns/op 134.51 MB/s IndexBytePortable64M 5 498347200 ns/op 134.66 MB/s 2.53 GHz Intel Core2 Duo E7200 (Linux) amd64 (1.5x faster) IndexByte4K 500000 6502 ns/op 629.96 MB/s IndexByte4M 500 6692208 ns/op 626.74 MB/s IndexByte64M 10 107410400 ns/op 624.79 MB/s IndexBytePortable4K 200000 9721 ns/op 421.36 MB/s IndexBytePortable4M 100 10013680 ns/op 418.86 MB/s IndexBytePortable64M 10 160460800 ns/op 418.23 MB/s 386 (4.4x faster) IndexByte4K 500000 6505 ns/op 629.67 MB/s IndexByte4M 500 6694078 ns/op 626.57 MB/s IndexByte64M 10 107397600 ns/op 624.86 MB/s IndexBytePortable4K 100000 28835 ns/op 142.05 MB/s IndexBytePortable4M 50 29562680 ns/op 141.88 MB/s IndexBytePortable64M 5 473221400 ns/op 141.81 MB/s 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) amd64 (1.5x SLOWER) IndexByte4K 200000 9290 ns/op 440.90 MB/s IndexByte4M 200 9568925 ns/op 438.33 MB/s IndexByte64M 10 154473600 ns/op 434.44 MB/s IndexBytePortable4K 500000 6202 ns/op 660.43 MB/s IndexBytePortable4M 500 6583614 ns/op 637.08 MB/s IndexBytePortable64M 20 107166250 ns/op 626.21 MB/s 386 (3x faster) IndexByte4K 200000 9301 ns/op 440.38 MB/s IndexByte4M 200 9568025 ns/op 438.37 MB/s IndexByte64M 10 154391000 ns/op 434.67 MB/s IndexBytePortable4K 100000 27526 ns/op 148.80 MB/s IndexBytePortable4M 100 28302490 ns/op 148.20 MB/s IndexBytePortable64M 5 454170200 ns/op 147.76 MB/s 2.33 GHz Intel Xeon E5435 (Linux) amd64 (1.5x SLOWER) IndexByte4K 200000 10601 ns/op 386.38 MB/s IndexByte4M 100 10827240 ns/op 387.38 MB/s IndexByte64M 10 173175500 ns/op 387.52 MB/s IndexBytePortable4K 500000 7082 ns/op 578.37 MB/s IndexBytePortable4M 500 7391792 ns/op 567.43 MB/s IndexBytePortable64M 20 122618550 ns/op 547.30 MB/s 386 (3x faster) IndexByte4K 200000 11074 ns/op 369.88 MB/s IndexByte4M 100 10902620 ns/op 384.71 MB/s IndexByte64M 10 181292800 ns/op 370.17 MB/s IndexBytePortable4K 50000 31725 ns/op 129.11 MB/s IndexBytePortable4M 50 32564880 ns/op 128.80 MB/s IndexBytePortable64M 2 545926000 ns/op 122.93 MB/s 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) amd64 (1.4x SLOWER) IndexByte4K 200000 11120 ns/op 368.35 MB/s IndexByte4M 100 11531950 ns/op 363.71 MB/s IndexByte64M 10 184819000 ns/op 363.11 MB/s IndexBytePortable4K 500000 7419 ns/op 552.10 MB/s IndexBytePortable4M 200 8018710 ns/op 523.06 MB/s IndexBytePortable64M 10 127614900 ns/op 525.87 MB/s 386 (3x faster) IndexByte4K 200000 11114 ns/op 368.54 MB/s IndexByte4M 100 11443530 ns/op 366.52 MB/s IndexByte64M 10 185212000 ns/op 362.34 MB/s IndexBytePortable4K 50000 32891 ns/op 124.53 MB/s IndexBytePortable4M 50 33930580 ns/op 123.61 MB/s IndexBytePortable64M 2 545400500 ns/op 123.05 MB/s 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) amd64 (no difference) IndexByte4K 200000 13497 ns/op 303.47 MB/s IndexByte4M 100 13890650 ns/op 301.95 MB/s IndexByte64M 5 222358000 ns/op 301.81 MB/s IndexBytePortable4K 200000 13584 ns/op 301.53 MB/s IndexBytePortable4M 100 13913280 ns/op 301.46 MB/s IndexBytePortable64M 10 222572600 ns/op 301.51 MB/s 386 (3x faster) IndexByte4K 200000 13565 ns/op 301.95 MB/s IndexByte4M 100 13882640 ns/op 302.13 MB/s IndexByte64M 5 221411600 ns/op 303.10 MB/s IndexBytePortable4K 50000 39978 ns/op 102.46 MB/s IndexBytePortable4M 50 41038160 ns/op 102.20 MB/s IndexBytePortable64M 2 656362500 ns/op 102.24 MB/s R=r CC=golang-dev https://golang.org/cl/166055
2009-12-04 10:23:43 -08:00
}
var indexSizes = []int{10, 32, 4 << 10, 4 << 20, 64 << 20}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
var isRaceBuilder = strings.HasSuffix(testenv.Builder(), "-race")
func BenchmarkIndexByte(b *testing.B) {
benchBytes(b, indexSizes, bmIndexByte(IndexByte))
}
func BenchmarkIndexBytePortable(b *testing.B) {
benchBytes(b, indexSizes, bmIndexByte(IndexBytePortable))
}
func bmIndexByte(index func([]byte, byte) int) func(b *testing.B, n int) {
return func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := index(buf, 'x')
if j != n-1 {
b.Fatal("bad index", j)
}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
buf[n-1] = '\x00'
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
bytes: asm for bytes.IndexByte PERFORMANCE DIFFERENCE SUMMARY amd64 386 2.2 GHz AMD Opteron 8214 HE (Linux) 3.0x faster 8.2x faster 3.60 GHz Intel Xeon (Linux) 2.2x faster 6.2x faster 2.53 GHz Intel Core2 Duo E7200 (Linux) 1.5x faster 4.4x faster 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Xeon E5435 (Linux) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) 1.4x SLOWER 3.0x faster 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) none* 3.0x faster * but yesterday I consistently saw 1.4x SLOWER. DETAILS 2.2 GHz AMD Opteron 8214 HE (Linux) amd64 (3x faster) IndexByte4K 500000 3733 ns/op 1097.24 MB/s IndexByte4M 500 4328042 ns/op 969.10 MB/s IndexByte64M 50 67866160 ns/op 988.84 MB/s IndexBytePortable4K 200000 11161 ns/op 366.99 MB/s IndexBytePortable4M 100 11795880 ns/op 355.57 MB/s IndexBytePortable64M 10 188675000 ns/op 355.68 MB/s 386 (8.2x faster) IndexByte4K 500000 3734 ns/op 1096.95 MB/s IndexByte4M 500 4209954 ns/op 996.28 MB/s IndexByte64M 50 68031980 ns/op 986.43 MB/s IndexBytePortable4K 50000 30670 ns/op 133.55 MB/s IndexBytePortable4M 50 31868220 ns/op 131.61 MB/s IndexBytePortable64M 2 508851500 ns/op 131.88 MB/s 3.60 GHz Intel Xeon (Linux) amd64 (2.2x faster) IndexByte4K 500000 4612 ns/op 888.12 MB/s IndexByte4M 500 4835250 ns/op 867.44 MB/s IndexByte64M 20 77388450 ns/op 867.17 MB/s IndexBytePortable4K 200000 10306 ns/op 397.44 MB/s IndexBytePortable4M 100 11201460 ns/op 374.44 MB/s IndexBytePortable64M 10 179456800 ns/op 373.96 MB/s 386 (6.3x faster) IndexByte4K 500000 4631 ns/op 884.47 MB/s IndexByte4M 500 4846388 ns/op 865.45 MB/s IndexByte64M 20 78691200 ns/op 852.81 MB/s IndexBytePortable4K 100000 28989 ns/op 141.29 MB/s IndexBytePortable4M 50 31183180 ns/op 134.51 MB/s IndexBytePortable64M 5 498347200 ns/op 134.66 MB/s 2.53 GHz Intel Core2 Duo E7200 (Linux) amd64 (1.5x faster) IndexByte4K 500000 6502 ns/op 629.96 MB/s IndexByte4M 500 6692208 ns/op 626.74 MB/s IndexByte64M 10 107410400 ns/op 624.79 MB/s IndexBytePortable4K 200000 9721 ns/op 421.36 MB/s IndexBytePortable4M 100 10013680 ns/op 418.86 MB/s IndexBytePortable64M 10 160460800 ns/op 418.23 MB/s 386 (4.4x faster) IndexByte4K 500000 6505 ns/op 629.67 MB/s IndexByte4M 500 6694078 ns/op 626.57 MB/s IndexByte64M 10 107397600 ns/op 624.86 MB/s IndexBytePortable4K 100000 28835 ns/op 142.05 MB/s IndexBytePortable4M 50 29562680 ns/op 141.88 MB/s IndexBytePortable64M 5 473221400 ns/op 141.81 MB/s 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) amd64 (1.5x SLOWER) IndexByte4K 200000 9290 ns/op 440.90 MB/s IndexByte4M 200 9568925 ns/op 438.33 MB/s IndexByte64M 10 154473600 ns/op 434.44 MB/s IndexBytePortable4K 500000 6202 ns/op 660.43 MB/s IndexBytePortable4M 500 6583614 ns/op 637.08 MB/s IndexBytePortable64M 20 107166250 ns/op 626.21 MB/s 386 (3x faster) IndexByte4K 200000 9301 ns/op 440.38 MB/s IndexByte4M 200 9568025 ns/op 438.37 MB/s IndexByte64M 10 154391000 ns/op 434.67 MB/s IndexBytePortable4K 100000 27526 ns/op 148.80 MB/s IndexBytePortable4M 100 28302490 ns/op 148.20 MB/s IndexBytePortable64M 5 454170200 ns/op 147.76 MB/s 2.33 GHz Intel Xeon E5435 (Linux) amd64 (1.5x SLOWER) IndexByte4K 200000 10601 ns/op 386.38 MB/s IndexByte4M 100 10827240 ns/op 387.38 MB/s IndexByte64M 10 173175500 ns/op 387.52 MB/s IndexBytePortable4K 500000 7082 ns/op 578.37 MB/s IndexBytePortable4M 500 7391792 ns/op 567.43 MB/s IndexBytePortable64M 20 122618550 ns/op 547.30 MB/s 386 (3x faster) IndexByte4K 200000 11074 ns/op 369.88 MB/s IndexByte4M 100 10902620 ns/op 384.71 MB/s IndexByte64M 10 181292800 ns/op 370.17 MB/s IndexBytePortable4K 50000 31725 ns/op 129.11 MB/s IndexBytePortable4M 50 32564880 ns/op 128.80 MB/s IndexBytePortable64M 2 545926000 ns/op 122.93 MB/s 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) amd64 (1.4x SLOWER) IndexByte4K 200000 11120 ns/op 368.35 MB/s IndexByte4M 100 11531950 ns/op 363.71 MB/s IndexByte64M 10 184819000 ns/op 363.11 MB/s IndexBytePortable4K 500000 7419 ns/op 552.10 MB/s IndexBytePortable4M 200 8018710 ns/op 523.06 MB/s IndexBytePortable64M 10 127614900 ns/op 525.87 MB/s 386 (3x faster) IndexByte4K 200000 11114 ns/op 368.54 MB/s IndexByte4M 100 11443530 ns/op 366.52 MB/s IndexByte64M 10 185212000 ns/op 362.34 MB/s IndexBytePortable4K 50000 32891 ns/op 124.53 MB/s IndexBytePortable4M 50 33930580 ns/op 123.61 MB/s IndexBytePortable64M 2 545400500 ns/op 123.05 MB/s 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) amd64 (no difference) IndexByte4K 200000 13497 ns/op 303.47 MB/s IndexByte4M 100 13890650 ns/op 301.95 MB/s IndexByte64M 5 222358000 ns/op 301.81 MB/s IndexBytePortable4K 200000 13584 ns/op 301.53 MB/s IndexBytePortable4M 100 13913280 ns/op 301.46 MB/s IndexBytePortable64M 10 222572600 ns/op 301.51 MB/s 386 (3x faster) IndexByte4K 200000 13565 ns/op 301.95 MB/s IndexByte4M 100 13882640 ns/op 302.13 MB/s IndexByte64M 5 221411600 ns/op 303.10 MB/s IndexBytePortable4K 50000 39978 ns/op 102.46 MB/s IndexBytePortable4M 50 41038160 ns/op 102.20 MB/s IndexBytePortable64M 2 656362500 ns/op 102.24 MB/s R=r CC=golang-dev https://golang.org/cl/166055
2009-12-04 10:23:43 -08:00
}
bytes: make IndexRune faster re-implement IndexRune by IndexByte and Index which are well optimized to get performance gain. name old time/op new time/op delta IndexRune/10-4 53.2ns ± 1% 29.1ns ± 1% -45.32% (p=0.008 n=5+5) IndexRune/32-4 191ns ± 1% 27ns ± 1% -85.75% (p=0.008 n=5+5) IndexRune/4K-4 23.5µs ± 1% 1.0µs ± 1% -95.77% (p=0.008 n=5+5) IndexRune/4M-4 23.8ms ± 0% 1.0ms ± 2% -95.90% (p=0.008 n=5+5) IndexRune/64M-4 384ms ± 1% 15ms ± 1% -95.98% (p=0.008 n=5+5) IndexRuneASCII/10-4 61.5ns ± 0% 10.3ns ± 4% -83.17% (p=0.008 n=5+5) IndexRuneASCII/32-4 203ns ± 0% 11ns ± 5% -94.68% (p=0.008 n=5+5) IndexRuneASCII/4K-4 23.4µs ± 0% 0.3µs ± 2% -98.60% (p=0.008 n=5+5) IndexRuneASCII/4M-4 24.0ms ± 1% 0.3ms ± 1% -98.60% (p=0.008 n=5+5) IndexRuneASCII/64M-4 386ms ± 2% 6ms ± 1% -98.57% (p=0.008 n=5+5) name old speed new speed delta IndexRune/10-4 188MB/s ± 1% 344MB/s ± 1% +82.91% (p=0.008 n=5+5) IndexRune/32-4 167MB/s ± 0% 1175MB/s ± 1% +603.52% (p=0.008 n=5+5) IndexRune/4K-4 174MB/s ± 1% 4117MB/s ± 1% +2262.71% (p=0.008 n=5+5) IndexRune/4M-4 176MB/s ± 0% 4299MB/s ± 2% +2340.46% (p=0.008 n=5+5) IndexRune/64M-4 175MB/s ± 1% 4354MB/s ± 1% +2388.57% (p=0.008 n=5+5) IndexRuneASCII/10-4 163MB/s ± 0% 968MB/s ± 4% +494.66% (p=0.008 n=5+5) IndexRuneASCII/32-4 157MB/s ± 0% 2974MB/s ± 4% +1788.59% (p=0.008 n=5+5) IndexRuneASCII/4K-4 175MB/s ± 0% 12481MB/s ± 2% +7027.71% (p=0.008 n=5+5) IndexRuneASCII/4M-4 175MB/s ± 1% 12510MB/s ± 1% +7061.15% (p=0.008 n=5+5) IndexRuneASCII/64M-4 174MB/s ± 2% 12143MB/s ± 1% +6881.70% (p=0.008 n=5+5) Change-Id: I0632eadb83937c2a9daa7f0ce79df1dee64f992e Reviewed-on: https://go-review.googlesource.com/28537 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-09-06 08:09:27 +09:00
func BenchmarkIndexRune(b *testing.B) {
benchBytes(b, indexSizes, bmIndexRune(IndexRune))
}
func BenchmarkIndexRuneASCII(b *testing.B) {
benchBytes(b, indexSizes, bmIndexRuneASCII(IndexRune))
}
func bmIndexRuneASCII(index func([]byte, rune) int) func(b *testing.B, n int) {
return func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := index(buf, 'x')
if j != n-1 {
b.Fatal("bad index", j)
}
}
buf[n-1] = '\x00'
}
}
func bmIndexRune(index func([]byte, rune) int) func(b *testing.B, n int) {
return func(b *testing.B, n int) {
buf := bmbuf[0:n]
utf8.EncodeRune(buf[n-3:], '世')
for i := 0; i < b.N; i++ {
j := index(buf, '世')
if j != n-3 {
b.Fatal("bad index", j)
}
}
buf[n-3] = '\x00'
buf[n-2] = '\x00'
buf[n-1] = '\x00'
}
}
func BenchmarkEqual(b *testing.B) {
b.Run("0", func(b *testing.B) {
var buf [4]byte
buf1 := buf[0:0]
buf2 := buf[1:1]
for i := 0; i < b.N; i++ {
eq := Equal(buf1, buf2)
if !eq {
b.Fatal("bad equal")
}
}
})
bytes: asm for bytes.IndexByte PERFORMANCE DIFFERENCE SUMMARY amd64 386 2.2 GHz AMD Opteron 8214 HE (Linux) 3.0x faster 8.2x faster 3.60 GHz Intel Xeon (Linux) 2.2x faster 6.2x faster 2.53 GHz Intel Core2 Duo E7200 (Linux) 1.5x faster 4.4x faster 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Xeon E5435 (Linux) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) 1.4x SLOWER 3.0x faster 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) none* 3.0x faster * but yesterday I consistently saw 1.4x SLOWER. DETAILS 2.2 GHz AMD Opteron 8214 HE (Linux) amd64 (3x faster) IndexByte4K 500000 3733 ns/op 1097.24 MB/s IndexByte4M 500 4328042 ns/op 969.10 MB/s IndexByte64M 50 67866160 ns/op 988.84 MB/s IndexBytePortable4K 200000 11161 ns/op 366.99 MB/s IndexBytePortable4M 100 11795880 ns/op 355.57 MB/s IndexBytePortable64M 10 188675000 ns/op 355.68 MB/s 386 (8.2x faster) IndexByte4K 500000 3734 ns/op 1096.95 MB/s IndexByte4M 500 4209954 ns/op 996.28 MB/s IndexByte64M 50 68031980 ns/op 986.43 MB/s IndexBytePortable4K 50000 30670 ns/op 133.55 MB/s IndexBytePortable4M 50 31868220 ns/op 131.61 MB/s IndexBytePortable64M 2 508851500 ns/op 131.88 MB/s 3.60 GHz Intel Xeon (Linux) amd64 (2.2x faster) IndexByte4K 500000 4612 ns/op 888.12 MB/s IndexByte4M 500 4835250 ns/op 867.44 MB/s IndexByte64M 20 77388450 ns/op 867.17 MB/s IndexBytePortable4K 200000 10306 ns/op 397.44 MB/s IndexBytePortable4M 100 11201460 ns/op 374.44 MB/s IndexBytePortable64M 10 179456800 ns/op 373.96 MB/s 386 (6.3x faster) IndexByte4K 500000 4631 ns/op 884.47 MB/s IndexByte4M 500 4846388 ns/op 865.45 MB/s IndexByte64M 20 78691200 ns/op 852.81 MB/s IndexBytePortable4K 100000 28989 ns/op 141.29 MB/s IndexBytePortable4M 50 31183180 ns/op 134.51 MB/s IndexBytePortable64M 5 498347200 ns/op 134.66 MB/s 2.53 GHz Intel Core2 Duo E7200 (Linux) amd64 (1.5x faster) IndexByte4K 500000 6502 ns/op 629.96 MB/s IndexByte4M 500 6692208 ns/op 626.74 MB/s IndexByte64M 10 107410400 ns/op 624.79 MB/s IndexBytePortable4K 200000 9721 ns/op 421.36 MB/s IndexBytePortable4M 100 10013680 ns/op 418.86 MB/s IndexBytePortable64M 10 160460800 ns/op 418.23 MB/s 386 (4.4x faster) IndexByte4K 500000 6505 ns/op 629.67 MB/s IndexByte4M 500 6694078 ns/op 626.57 MB/s IndexByte64M 10 107397600 ns/op 624.86 MB/s IndexBytePortable4K 100000 28835 ns/op 142.05 MB/s IndexBytePortable4M 50 29562680 ns/op 141.88 MB/s IndexBytePortable64M 5 473221400 ns/op 141.81 MB/s 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) amd64 (1.5x SLOWER) IndexByte4K 200000 9290 ns/op 440.90 MB/s IndexByte4M 200 9568925 ns/op 438.33 MB/s IndexByte64M 10 154473600 ns/op 434.44 MB/s IndexBytePortable4K 500000 6202 ns/op 660.43 MB/s IndexBytePortable4M 500 6583614 ns/op 637.08 MB/s IndexBytePortable64M 20 107166250 ns/op 626.21 MB/s 386 (3x faster) IndexByte4K 200000 9301 ns/op 440.38 MB/s IndexByte4M 200 9568025 ns/op 438.37 MB/s IndexByte64M 10 154391000 ns/op 434.67 MB/s IndexBytePortable4K 100000 27526 ns/op 148.80 MB/s IndexBytePortable4M 100 28302490 ns/op 148.20 MB/s IndexBytePortable64M 5 454170200 ns/op 147.76 MB/s 2.33 GHz Intel Xeon E5435 (Linux) amd64 (1.5x SLOWER) IndexByte4K 200000 10601 ns/op 386.38 MB/s IndexByte4M 100 10827240 ns/op 387.38 MB/s IndexByte64M 10 173175500 ns/op 387.52 MB/s IndexBytePortable4K 500000 7082 ns/op 578.37 MB/s IndexBytePortable4M 500 7391792 ns/op 567.43 MB/s IndexBytePortable64M 20 122618550 ns/op 547.30 MB/s 386 (3x faster) IndexByte4K 200000 11074 ns/op 369.88 MB/s IndexByte4M 100 10902620 ns/op 384.71 MB/s IndexByte64M 10 181292800 ns/op 370.17 MB/s IndexBytePortable4K 50000 31725 ns/op 129.11 MB/s IndexBytePortable4M 50 32564880 ns/op 128.80 MB/s IndexBytePortable64M 2 545926000 ns/op 122.93 MB/s 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) amd64 (1.4x SLOWER) IndexByte4K 200000 11120 ns/op 368.35 MB/s IndexByte4M 100 11531950 ns/op 363.71 MB/s IndexByte64M 10 184819000 ns/op 363.11 MB/s IndexBytePortable4K 500000 7419 ns/op 552.10 MB/s IndexBytePortable4M 200 8018710 ns/op 523.06 MB/s IndexBytePortable64M 10 127614900 ns/op 525.87 MB/s 386 (3x faster) IndexByte4K 200000 11114 ns/op 368.54 MB/s IndexByte4M 100 11443530 ns/op 366.52 MB/s IndexByte64M 10 185212000 ns/op 362.34 MB/s IndexBytePortable4K 50000 32891 ns/op 124.53 MB/s IndexBytePortable4M 50 33930580 ns/op 123.61 MB/s IndexBytePortable64M 2 545400500 ns/op 123.05 MB/s 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) amd64 (no difference) IndexByte4K 200000 13497 ns/op 303.47 MB/s IndexByte4M 100 13890650 ns/op 301.95 MB/s IndexByte64M 5 222358000 ns/op 301.81 MB/s IndexBytePortable4K 200000 13584 ns/op 301.53 MB/s IndexBytePortable4M 100 13913280 ns/op 301.46 MB/s IndexBytePortable64M 10 222572600 ns/op 301.51 MB/s 386 (3x faster) IndexByte4K 200000 13565 ns/op 301.95 MB/s IndexByte4M 100 13882640 ns/op 302.13 MB/s IndexByte64M 5 221411600 ns/op 303.10 MB/s IndexBytePortable4K 50000 39978 ns/op 102.46 MB/s IndexBytePortable4M 50 41038160 ns/op 102.20 MB/s IndexBytePortable64M 2 656362500 ns/op 102.24 MB/s R=r CC=golang-dev https://golang.org/cl/166055
2009-12-04 10:23:43 -08:00
sizes := []int{1, 6, 9, 15, 16, 20, 32, 4 << 10, 4 << 20, 64 << 20}
benchBytes(b, sizes, bmEqual(Equal))
}
func BenchmarkEqualPort(b *testing.B) {
sizes := []int{1, 6, 32, 4 << 10, 4 << 20, 64 << 20}
benchBytes(b, sizes, bmEqual(EqualPortable))
}
func bmEqual(equal func([]byte, []byte) bool) func(b *testing.B, n int) {
return func(b *testing.B, n int) {
if len(bmbuf) < 2*n {
bmbuf = make([]byte, 2*n)
}
buf1 := bmbuf[0:n]
buf2 := bmbuf[n : 2*n]
buf1[n-1] = 'x'
buf2[n-1] = 'x'
for i := 0; i < b.N; i++ {
eq := equal(buf1, buf2)
if !eq {
b.Fatal("bad equal")
}
bytes: asm for bytes.IndexByte PERFORMANCE DIFFERENCE SUMMARY amd64 386 2.2 GHz AMD Opteron 8214 HE (Linux) 3.0x faster 8.2x faster 3.60 GHz Intel Xeon (Linux) 2.2x faster 6.2x faster 2.53 GHz Intel Core2 Duo E7200 (Linux) 1.5x faster 4.4x faster 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Xeon E5435 (Linux) 1.5x SLOWER 3.0x faster 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) 1.4x SLOWER 3.0x faster 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) none* 3.0x faster * but yesterday I consistently saw 1.4x SLOWER. DETAILS 2.2 GHz AMD Opteron 8214 HE (Linux) amd64 (3x faster) IndexByte4K 500000 3733 ns/op 1097.24 MB/s IndexByte4M 500 4328042 ns/op 969.10 MB/s IndexByte64M 50 67866160 ns/op 988.84 MB/s IndexBytePortable4K 200000 11161 ns/op 366.99 MB/s IndexBytePortable4M 100 11795880 ns/op 355.57 MB/s IndexBytePortable64M 10 188675000 ns/op 355.68 MB/s 386 (8.2x faster) IndexByte4K 500000 3734 ns/op 1096.95 MB/s IndexByte4M 500 4209954 ns/op 996.28 MB/s IndexByte64M 50 68031980 ns/op 986.43 MB/s IndexBytePortable4K 50000 30670 ns/op 133.55 MB/s IndexBytePortable4M 50 31868220 ns/op 131.61 MB/s IndexBytePortable64M 2 508851500 ns/op 131.88 MB/s 3.60 GHz Intel Xeon (Linux) amd64 (2.2x faster) IndexByte4K 500000 4612 ns/op 888.12 MB/s IndexByte4M 500 4835250 ns/op 867.44 MB/s IndexByte64M 20 77388450 ns/op 867.17 MB/s IndexBytePortable4K 200000 10306 ns/op 397.44 MB/s IndexBytePortable4M 100 11201460 ns/op 374.44 MB/s IndexBytePortable64M 10 179456800 ns/op 373.96 MB/s 386 (6.3x faster) IndexByte4K 500000 4631 ns/op 884.47 MB/s IndexByte4M 500 4846388 ns/op 865.45 MB/s IndexByte64M 20 78691200 ns/op 852.81 MB/s IndexBytePortable4K 100000 28989 ns/op 141.29 MB/s IndexBytePortable4M 50 31183180 ns/op 134.51 MB/s IndexBytePortable64M 5 498347200 ns/op 134.66 MB/s 2.53 GHz Intel Core2 Duo E7200 (Linux) amd64 (1.5x faster) IndexByte4K 500000 6502 ns/op 629.96 MB/s IndexByte4M 500 6692208 ns/op 626.74 MB/s IndexByte64M 10 107410400 ns/op 624.79 MB/s IndexBytePortable4K 200000 9721 ns/op 421.36 MB/s IndexBytePortable4M 100 10013680 ns/op 418.86 MB/s IndexBytePortable64M 10 160460800 ns/op 418.23 MB/s 386 (4.4x faster) IndexByte4K 500000 6505 ns/op 629.67 MB/s IndexByte4M 500 6694078 ns/op 626.57 MB/s IndexByte64M 10 107397600 ns/op 624.86 MB/s IndexBytePortable4K 100000 28835 ns/op 142.05 MB/s IndexBytePortable4M 50 29562680 ns/op 141.88 MB/s IndexBytePortable64M 5 473221400 ns/op 141.81 MB/s 2.66 Ghz Intel Xeon 5150 (Mac Pro, OS X) amd64 (1.5x SLOWER) IndexByte4K 200000 9290 ns/op 440.90 MB/s IndexByte4M 200 9568925 ns/op 438.33 MB/s IndexByte64M 10 154473600 ns/op 434.44 MB/s IndexBytePortable4K 500000 6202 ns/op 660.43 MB/s IndexBytePortable4M 500 6583614 ns/op 637.08 MB/s IndexBytePortable64M 20 107166250 ns/op 626.21 MB/s 386 (3x faster) IndexByte4K 200000 9301 ns/op 440.38 MB/s IndexByte4M 200 9568025 ns/op 438.37 MB/s IndexByte64M 10 154391000 ns/op 434.67 MB/s IndexBytePortable4K 100000 27526 ns/op 148.80 MB/s IndexBytePortable4M 100 28302490 ns/op 148.20 MB/s IndexBytePortable64M 5 454170200 ns/op 147.76 MB/s 2.33 GHz Intel Xeon E5435 (Linux) amd64 (1.5x SLOWER) IndexByte4K 200000 10601 ns/op 386.38 MB/s IndexByte4M 100 10827240 ns/op 387.38 MB/s IndexByte64M 10 173175500 ns/op 387.52 MB/s IndexBytePortable4K 500000 7082 ns/op 578.37 MB/s IndexBytePortable4M 500 7391792 ns/op 567.43 MB/s IndexBytePortable64M 20 122618550 ns/op 547.30 MB/s 386 (3x faster) IndexByte4K 200000 11074 ns/op 369.88 MB/s IndexByte4M 100 10902620 ns/op 384.71 MB/s IndexByte64M 10 181292800 ns/op 370.17 MB/s IndexBytePortable4K 50000 31725 ns/op 129.11 MB/s IndexBytePortable4M 50 32564880 ns/op 128.80 MB/s IndexBytePortable64M 2 545926000 ns/op 122.93 MB/s 2.33 GHz Intel Core2 T7600 (MacBook Pro, OS X) amd64 (1.4x SLOWER) IndexByte4K 200000 11120 ns/op 368.35 MB/s IndexByte4M 100 11531950 ns/op 363.71 MB/s IndexByte64M 10 184819000 ns/op 363.11 MB/s IndexBytePortable4K 500000 7419 ns/op 552.10 MB/s IndexBytePortable4M 200 8018710 ns/op 523.06 MB/s IndexBytePortable64M 10 127614900 ns/op 525.87 MB/s 386 (3x faster) IndexByte4K 200000 11114 ns/op 368.54 MB/s IndexByte4M 100 11443530 ns/op 366.52 MB/s IndexByte64M 10 185212000 ns/op 362.34 MB/s IndexBytePortable4K 50000 32891 ns/op 124.53 MB/s IndexBytePortable4M 50 33930580 ns/op 123.61 MB/s IndexBytePortable64M 2 545400500 ns/op 123.05 MB/s 1.83 GHz Intel Core2 T5600 (Mac Mini, OS X) amd64 (no difference) IndexByte4K 200000 13497 ns/op 303.47 MB/s IndexByte4M 100 13890650 ns/op 301.95 MB/s IndexByte64M 5 222358000 ns/op 301.81 MB/s IndexBytePortable4K 200000 13584 ns/op 301.53 MB/s IndexBytePortable4M 100 13913280 ns/op 301.46 MB/s IndexBytePortable64M 10 222572600 ns/op 301.51 MB/s 386 (3x faster) IndexByte4K 200000 13565 ns/op 301.95 MB/s IndexByte4M 100 13882640 ns/op 302.13 MB/s IndexByte64M 5 221411600 ns/op 303.10 MB/s IndexBytePortable4K 50000 39978 ns/op 102.46 MB/s IndexBytePortable4M 50 41038160 ns/op 102.20 MB/s IndexBytePortable64M 2 656362500 ns/op 102.24 MB/s R=r CC=golang-dev https://golang.org/cl/166055
2009-12-04 10:23:43 -08:00
}
buf1[n-1] = '\x00'
buf2[n-1] = '\x00'
}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
func BenchmarkIndex(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := Index(buf, buf[n-7:])
if j != n-7 {
b.Fatal("bad index", j)
}
}
buf[n-1] = '\x00'
})
}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
func BenchmarkIndexEasy(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
buf[n-7] = 'x'
for i := 0; i < b.N; i++ {
j := Index(buf, buf[n-7:])
if j != n-7 {
b.Fatal("bad index", j)
}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
buf[n-1] = '\x00'
buf[n-7] = '\x00'
})
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
func BenchmarkCount(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := Count(buf, buf[n-7:])
if j != 1 {
b.Fatal("bad count", j)
}
}
buf[n-1] = '\x00'
})
}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
func BenchmarkCountEasy(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
buf[n-7] = 'x'
for i := 0; i < b.N; i++ {
j := Count(buf, buf[n-7:])
if j != 1 {
b.Fatal("bad count", j)
}
bytes: faster Count, Index, Equal Benchmarks are from GOARCH=amd64 on a MacPro5,1. benchmark old MB/s new MB/s speedup bytes_test.BenchmarkEqual32 452.89 891.07 1.97x bytes_test.BenchmarkEqual4K 852.71 1700.44 1.99x bytes_test.BenchmarkEqual4M 841.53 1587.93 1.89x bytes_test.BenchmarkEqual64M 838.22 1578.14 1.88x bytes_test.BenchmarkIndex32 58.02 48.99 0.84x bytes_test.BenchmarkIndex4K 48.26 41.32 0.86x bytes_test.BenchmarkIndex4M 48.20 41.24 0.86x bytes_test.BenchmarkIndex64M 48.08 41.21 0.86x bytes_test.BenchmarkIndexEasy32 410.04 546.82 1.33x bytes_test.BenchmarkIndexEasy4K 849.26 14257.37 16.79x bytes_test.BenchmarkIndexEasy4M 854.54 17222.15 20.15x bytes_test.BenchmarkIndexEasy64M 843.57 11060.40 13.11x bytes_test.BenchmarkCount32 57.24 50.68 0.89x bytes_test.BenchmarkCount4K 48.19 41.82 0.87x bytes_test.BenchmarkCount4M 48.18 41.74 0.87x bytes_test.BenchmarkCount64M 48.17 41.71 0.87x bytes_test.BenchmarkCountEasy32 433.11 547.44 1.26x bytes_test.BenchmarkCountEasy4K 1130.59 14194.06 12.55x bytes_test.BenchmarkCountEasy4M 1131.23 17231.18 15.23x bytes_test.BenchmarkCountEasy64M 1111.40 11068.88 9.96x The non-easy Count/Index benchmarks are a worst case input. regexp.BenchmarkMatchEasy0_32 237.46 221.47 0.93x regexp.BenchmarkMatchEasy0_1K 553.53 1019.72 1.84x regexp.BenchmarkMatchEasy0_32K 693.99 1672.06 2.41x regexp.BenchmarkMatchEasy0_1M 688.72 1611.68 2.34x regexp.BenchmarkMatchEasy0_32M 680.70 1565.05 2.30x regexp.BenchmarkMatchEasy1_32 165.56 243.08 1.47x regexp.BenchmarkMatchEasy1_1K 336.45 496.32 1.48x regexp.BenchmarkMatchEasy1_32K 302.80 425.63 1.41x regexp.BenchmarkMatchEasy1_1M 300.42 414.20 1.38x regexp.BenchmarkMatchEasy1_32M 299.64 413.47 1.38x R=golang-dev, r, iant CC=golang-dev https://golang.org/cl/5451116
2011-12-07 15:09:56 -05:00
}
buf[n-1] = '\x00'
buf[n-7] = '\x00'
})
}
bytes: add optimized countByte for amd64 Use SSE/AVX2 when counting a single byte. Inspired from runtime indexbyte implementation. Benchmark against previous implementation, where 1 byte in every 8 is the one we are looking for: * On a machine without AVX2 name old time/op new time/op delta CountSingle/10-4 61.8ns ±10% 15.6ns ±11% -74.83% (p=0.000 n=10+10) CountSingle/32-4 100ns ± 4% 17ns ±10% -82.54% (p=0.000 n=10+9) CountSingle/4K-4 9.66µs ± 3% 0.37µs ± 6% -96.21% (p=0.000 n=10+10) CountSingle/4M-4 11.0ms ± 6% 0.4ms ± 4% -96.04% (p=0.000 n=10+10) CountSingle/64M-4 194ms ± 8% 8ms ± 2% -95.64% (p=0.000 n=10+10) name old speed new speed delta CountSingle/10-4 162MB/s ±10% 645MB/s ±10% +297.00% (p=0.000 n=10+10) CountSingle/32-4 321MB/s ± 5% 1844MB/s ± 9% +474.79% (p=0.000 n=10+9) CountSingle/4K-4 424MB/s ± 3% 11169MB/s ± 6% +2533.10% (p=0.000 n=10+10) CountSingle/4M-4 381MB/s ± 7% 9609MB/s ± 4% +2421.88% (p=0.000 n=10+10) CountSingle/64M-4 346MB/s ± 7% 7924MB/s ± 2% +2188.78% (p=0.000 n=10+10) * On a machine with AVX2 name old time/op new time/op delta CountSingle/10-8 37.1ns ± 3% 8.2ns ± 1% -77.80% (p=0.000 n=10+10) CountSingle/32-8 66.1ns ± 3% 9.8ns ± 2% -85.23% (p=0.000 n=10+10) CountSingle/4K-8 7.36µs ± 3% 0.11µs ± 1% -98.54% (p=0.000 n=10+10) CountSingle/4M-8 7.46ms ± 2% 0.15ms ± 2% -97.95% (p=0.000 n=10+9) CountSingle/64M-8 124ms ± 2% 6ms ± 4% -95.09% (p=0.000 n=10+10) name old speed new speed delta CountSingle/10-8 269MB/s ± 3% 1213MB/s ± 1% +350.32% (p=0.000 n=10+10) CountSingle/32-8 484MB/s ± 4% 3277MB/s ± 2% +576.66% (p=0.000 n=10+10) CountSingle/4K-8 556MB/s ± 3% 37933MB/s ± 1% +6718.36% (p=0.000 n=10+10) CountSingle/4M-8 562MB/s ± 2% 27444MB/s ± 3% +4783.43% (p=0.000 n=10+9) CountSingle/64M-8 543MB/s ± 2% 11054MB/s ± 3% +1935.81% (p=0.000 n=10+10) Fixes #19411 Change-Id: Ieaf20b1fabccabe767c55c66e242e86f3617f883 Reviewed-on: https://go-review.googlesource.com/38258 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2017-03-19 12:18:08 +01:00
func BenchmarkCountSingle(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
step := 8
for i := 0; i < len(buf); i += step {
buf[i] = 1
}
expect := (len(buf) + (step - 1)) / step
for i := 0; i < b.N; i++ {
j := Count(buf, []byte{1})
if j != expect {
b.Fatal("bad count", j, expect)
}
}
for i := 0; i < len(buf); i++ {
buf[i] = 0
}
})
}
type SplitTest struct {
s string
sep string
n int
a []string
}
var splittests = []SplitTest{
{"", "", -1, []string{}},
{abcd, "a", 0, nil},
{abcd, "", 2, []string{"a", "bcd"}},
{abcd, "a", -1, []string{"", "bcd"}},
{abcd, "z", -1, []string{"abcd"}},
{abcd, "", -1, []string{"a", "b", "c", "d"}},
{commas, ",", -1, []string{"1", "2", "3", "4"}},
{dots, "...", -1, []string{"1", ".2", ".3", ".4"}},
{faces, "☹", -1, []string{"☺☻", ""}},
{faces, "~", -1, []string{faces}},
{faces, "", -1, []string{"☺", "☻", "☹"}},
{"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}},
{"1 2", " ", 3, []string{"1", "2"}},
{"123", "", 2, []string{"1", "23"}},
{"123", "", 17, []string{"1", "2", "3"}},
}
func TestSplit(t *testing.T) {
for _, tt := range splittests {
a := SplitN([]byte(tt.s), []byte(tt.sep), tt.n)
// Appending to the results should not change future results.
var x []byte
for _, v := range a {
x = append(v, 'z')
}
result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
continue
}
if tt.n == 0 || len(a) == 0 {
continue
}
if want := tt.a[len(tt.a)-1] + "z"; string(x) != want {
t.Errorf("last appended result was %s; want %s", x, want)
}
s := Join(a, []byte(tt.sep))
if string(s) != tt.s {
t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s)
}
if tt.n < 0 {
b := Split([]byte(tt.s), []byte(tt.sep))
if !reflect.DeepEqual(a, b) {
t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
}
}
if len(a) > 0 {
in, out := a[0], s
if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] {
t.Errorf("Join(%#v, %q) didn't copy", a, tt.sep)
}
}
}
}
var splitaftertests = []SplitTest{
{abcd, "a", -1, []string{"a", "bcd"}},
{abcd, "z", -1, []string{"abcd"}},
{abcd, "", -1, []string{"a", "b", "c", "d"}},
{commas, ",", -1, []string{"1,", "2,", "3,", "4"}},
{dots, "...", -1, []string{"1...", ".2...", ".3...", ".4"}},
{faces, "☹", -1, []string{"☺☻☹", ""}},
{faces, "~", -1, []string{faces}},
{faces, "", -1, []string{"☺", "☻", "☹"}},
{"1 2 3 4", " ", 3, []string{"1 ", "2 ", "3 4"}},
{"1 2 3", " ", 3, []string{"1 ", "2 ", "3"}},
{"1 2", " ", 3, []string{"1 ", "2"}},
{"123", "", 2, []string{"1", "23"}},
{"123", "", 17, []string{"1", "2", "3"}},
}
func TestSplitAfter(t *testing.T) {
for _, tt := range splitaftertests {
a := SplitAfterN([]byte(tt.s), []byte(tt.sep), tt.n)
// Appending to the results should not change future results.
var x []byte
for _, v := range a {
x = append(v, 'z')
}
result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
continue
}
if want := tt.a[len(tt.a)-1] + "z"; string(x) != want {
t.Errorf("last appended result was %s; want %s", x, want)
}
s := Join(a, nil)
if string(s) != tt.s {
t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s)
}
if tt.n < 0 {
b := SplitAfter([]byte(tt.s), []byte(tt.sep))
if !reflect.DeepEqual(a, b) {
t.Errorf("SplitAfter disagrees withSplitAfterN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
}
}
}
}
type FieldsTest struct {
s string
a []string
}
var fieldstests = []FieldsTest{
{"", []string{}},
{" ", []string{}},
{" \t ", []string{}},
{" abc ", []string{"abc"}},
{"1 2 3 4", []string{"1", "2", "3", "4"}},
{"1 2 3 4", []string{"1", "2", "3", "4"}},
{"1\t\t2\t\t3\t4", []string{"1", "2", "3", "4"}},
{"1\u20002\u20013\u20024", []string{"1", "2", "3", "4"}},
{"\u2000\u2001\u2002", []string{}},
{"\n™\t™\n", []string{"™", "™"}},
{faces, []string{faces}},
}
func TestFields(t *testing.T) {
for _, tt := range fieldstests {
b := []byte(tt.s)
a := Fields(b)
// Appending to the results should not change future results.
var x []byte
for _, v := range a {
x = append(v, 'z')
}
result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("Fields(%q) = %v; want %v", tt.s, a, tt.a)
continue
}
if string(b) != tt.s {
t.Errorf("slice changed to %s; want %s", string(b), tt.s)
}
if len(tt.a) > 0 {
if want := tt.a[len(tt.a)-1] + "z"; string(x) != want {
t.Errorf("last appended result was %s; want %s", x, want)
}
}
}
}
func TestFieldsFunc(t *testing.T) {
for _, tt := range fieldstests {
a := FieldsFunc([]byte(tt.s), unicode.IsSpace)
result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a)
continue
}
}
pred := func(c rune) bool { return c == 'X' }
var fieldsFuncTests = []FieldsTest{
{"", []string{}},
{"XX", []string{}},
{"XXhiXXX", []string{"hi"}},
{"aXXbXXXcX", []string{"a", "b", "c"}},
}
for _, tt := range fieldsFuncTests {
b := []byte(tt.s)
a := FieldsFunc(b, pred)
// Appending to the results should not change future results.
var x []byte
for _, v := range a {
x = append(v, 'z')
}
result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("FieldsFunc(%q) = %v, want %v", tt.s, a, tt.a)
}
if string(b) != tt.s {
t.Errorf("slice changed to %s; want %s", b, tt.s)
}
if len(tt.a) > 0 {
if want := tt.a[len(tt.a)-1] + "z"; string(x) != want {
t.Errorf("last appended result was %s; want %s", x, want)
}
}
}
}
// Test case for any function which accepts and returns a byte slice.
// For ease of creation, we write the input byte slice as a string.
type StringTest struct {
in string
out []byte
}
var upperTests = []StringTest{
{"", []byte("")},
bytes: optimize ToLower and ToUpper for ASCII-only case Follow what CL 68370 and CL 76470 did for the respective functions in package strings. Also adjust godoc strings to match the respective strings functions and mention the special case for ASCII-only byte slices which don't need conversion. name old time/op new time/op delta ToUpper/#00-8 9.35ns ± 3% 6.08ns ± 2% -35.04% (p=0.000 n=9+9) ToUpper/ONLYUPPER-8 77.7ns ± 1% 16.9ns ± 2% -78.22% (p=0.000 n=10+10) ToUpper/abc-8 36.5ns ± 1% 22.1ns ± 1% -39.43% (p=0.000 n=10+8) ToUpper/AbC123-8 56.9ns ± 2% 28.2ns ± 2% -50.54% (p=0.000 n=8+10) ToUpper/azAZ09_-8 62.3ns ± 1% 26.9ns ± 1% -56.82% (p=0.000 n=9+10) ToUpper/longStrinGwitHmixofsmaLLandcAps-8 219ns ± 2% 63ns ± 2% -71.17% (p=0.000 n=10+10) ToUpper/longɐstringɐwithɐnonasciiⱯchars-8 367ns ± 2% 374ns ± 3% +2.05% (p=0.000 n=9+10) ToUpper/ɐɐɐɐɐ-8 200ns ± 1% 206ns ± 1% +2.49% (p=0.000 n=10+10) ToUpper/a\u0080\U0010ffff-8 90.4ns ± 1% 93.8ns ± 0% +3.82% (p=0.000 n=10+7) ToLower/#00-8 9.59ns ± 1% 6.13ns ± 2% -36.08% (p=0.000 n=10+10) ToLower/abc-8 36.4ns ± 1% 10.4ns ± 1% -71.50% (p=0.000 n=10+10) ToLower/AbC123-8 55.8ns ± 1% 27.5ns ± 1% -50.61% (p=0.000 n=10+10) ToLower/azAZ09_-8 61.7ns ± 1% 30.2ns ± 1% -50.98% (p=0.000 n=8+10) ToLower/longStrinGwitHmixofsmaLLandcAps-8 226ns ± 1% 64ns ± 1% -71.53% (p=0.000 n=10+9) ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS-8 354ns ± 0% 361ns ± 0% +2.18% (p=0.000 n=10+10) ToLower/ⱭⱭⱭⱭⱭ-8 180ns ± 1% 186ns ± 0% +3.45% (p=0.000 n=10+9) ToLower/A\u0080\U0010ffff-8 91.7ns ± 0% 94.5ns ± 0% +2.99% (p=0.000 n=10+10) Change-Id: Ifdb8ae328ff9feacd1c170db8eebbf98c399e204 Reviewed-on: https://go-review.googlesource.com/c/go/+/170954 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-08 15:53:35 +02:00
{"ONLYUPPER", []byte("ONLYUPPER")},
{"abc", []byte("ABC")},
{"AbC123", []byte("ABC123")},
{"azAZ09_", []byte("AZAZ09_")},
bytes: optimize ToLower and ToUpper for ASCII-only case Follow what CL 68370 and CL 76470 did for the respective functions in package strings. Also adjust godoc strings to match the respective strings functions and mention the special case for ASCII-only byte slices which don't need conversion. name old time/op new time/op delta ToUpper/#00-8 9.35ns ± 3% 6.08ns ± 2% -35.04% (p=0.000 n=9+9) ToUpper/ONLYUPPER-8 77.7ns ± 1% 16.9ns ± 2% -78.22% (p=0.000 n=10+10) ToUpper/abc-8 36.5ns ± 1% 22.1ns ± 1% -39.43% (p=0.000 n=10+8) ToUpper/AbC123-8 56.9ns ± 2% 28.2ns ± 2% -50.54% (p=0.000 n=8+10) ToUpper/azAZ09_-8 62.3ns ± 1% 26.9ns ± 1% -56.82% (p=0.000 n=9+10) ToUpper/longStrinGwitHmixofsmaLLandcAps-8 219ns ± 2% 63ns ± 2% -71.17% (p=0.000 n=10+10) ToUpper/longɐstringɐwithɐnonasciiⱯchars-8 367ns ± 2% 374ns ± 3% +2.05% (p=0.000 n=9+10) ToUpper/ɐɐɐɐɐ-8 200ns ± 1% 206ns ± 1% +2.49% (p=0.000 n=10+10) ToUpper/a\u0080\U0010ffff-8 90.4ns ± 1% 93.8ns ± 0% +3.82% (p=0.000 n=10+7) ToLower/#00-8 9.59ns ± 1% 6.13ns ± 2% -36.08% (p=0.000 n=10+10) ToLower/abc-8 36.4ns ± 1% 10.4ns ± 1% -71.50% (p=0.000 n=10+10) ToLower/AbC123-8 55.8ns ± 1% 27.5ns ± 1% -50.61% (p=0.000 n=10+10) ToLower/azAZ09_-8 61.7ns ± 1% 30.2ns ± 1% -50.98% (p=0.000 n=8+10) ToLower/longStrinGwitHmixofsmaLLandcAps-8 226ns ± 1% 64ns ± 1% -71.53% (p=0.000 n=10+9) ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS-8 354ns ± 0% 361ns ± 0% +2.18% (p=0.000 n=10+10) ToLower/ⱭⱭⱭⱭⱭ-8 180ns ± 1% 186ns ± 0% +3.45% (p=0.000 n=10+9) ToLower/A\u0080\U0010ffff-8 91.7ns ± 0% 94.5ns ± 0% +2.99% (p=0.000 n=10+10) Change-Id: Ifdb8ae328ff9feacd1c170db8eebbf98c399e204 Reviewed-on: https://go-review.googlesource.com/c/go/+/170954 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-08 15:53:35 +02:00
{"longStrinGwitHmixofsmaLLandcAps", []byte("LONGSTRINGWITHMIXOFSMALLANDCAPS")},
{"long\u0250string\u0250with\u0250nonascii\u2C6Fchars", []byte("LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS")},
{"\u0250\u0250\u0250\u0250\u0250", []byte("\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F")}, // grows one byte per char
bytes: optimize ToLower and ToUpper for ASCII-only case Follow what CL 68370 and CL 76470 did for the respective functions in package strings. Also adjust godoc strings to match the respective strings functions and mention the special case for ASCII-only byte slices which don't need conversion. name old time/op new time/op delta ToUpper/#00-8 9.35ns ± 3% 6.08ns ± 2% -35.04% (p=0.000 n=9+9) ToUpper/ONLYUPPER-8 77.7ns ± 1% 16.9ns ± 2% -78.22% (p=0.000 n=10+10) ToUpper/abc-8 36.5ns ± 1% 22.1ns ± 1% -39.43% (p=0.000 n=10+8) ToUpper/AbC123-8 56.9ns ± 2% 28.2ns ± 2% -50.54% (p=0.000 n=8+10) ToUpper/azAZ09_-8 62.3ns ± 1% 26.9ns ± 1% -56.82% (p=0.000 n=9+10) ToUpper/longStrinGwitHmixofsmaLLandcAps-8 219ns ± 2% 63ns ± 2% -71.17% (p=0.000 n=10+10) ToUpper/longɐstringɐwithɐnonasciiⱯchars-8 367ns ± 2% 374ns ± 3% +2.05% (p=0.000 n=9+10) ToUpper/ɐɐɐɐɐ-8 200ns ± 1% 206ns ± 1% +2.49% (p=0.000 n=10+10) ToUpper/a\u0080\U0010ffff-8 90.4ns ± 1% 93.8ns ± 0% +3.82% (p=0.000 n=10+7) ToLower/#00-8 9.59ns ± 1% 6.13ns ± 2% -36.08% (p=0.000 n=10+10) ToLower/abc-8 36.4ns ± 1% 10.4ns ± 1% -71.50% (p=0.000 n=10+10) ToLower/AbC123-8 55.8ns ± 1% 27.5ns ± 1% -50.61% (p=0.000 n=10+10) ToLower/azAZ09_-8 61.7ns ± 1% 30.2ns ± 1% -50.98% (p=0.000 n=8+10) ToLower/longStrinGwitHmixofsmaLLandcAps-8 226ns ± 1% 64ns ± 1% -71.53% (p=0.000 n=10+9) ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS-8 354ns ± 0% 361ns ± 0% +2.18% (p=0.000 n=10+10) ToLower/ⱭⱭⱭⱭⱭ-8 180ns ± 1% 186ns ± 0% +3.45% (p=0.000 n=10+9) ToLower/A\u0080\U0010ffff-8 91.7ns ± 0% 94.5ns ± 0% +2.99% (p=0.000 n=10+10) Change-Id: Ifdb8ae328ff9feacd1c170db8eebbf98c399e204 Reviewed-on: https://go-review.googlesource.com/c/go/+/170954 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-08 15:53:35 +02:00
{"a\u0080\U0010FFFF", []byte("A\u0080\U0010FFFF")}, // test utf8.RuneSelf and utf8.MaxRune
}
var lowerTests = []StringTest{
{"", []byte("")},
{"abc", []byte("abc")},
{"AbC123", []byte("abc123")},
{"azAZ09_", []byte("azaz09_")},
bytes: optimize ToLower and ToUpper for ASCII-only case Follow what CL 68370 and CL 76470 did for the respective functions in package strings. Also adjust godoc strings to match the respective strings functions and mention the special case for ASCII-only byte slices which don't need conversion. name old time/op new time/op delta ToUpper/#00-8 9.35ns ± 3% 6.08ns ± 2% -35.04% (p=0.000 n=9+9) ToUpper/ONLYUPPER-8 77.7ns ± 1% 16.9ns ± 2% -78.22% (p=0.000 n=10+10) ToUpper/abc-8 36.5ns ± 1% 22.1ns ± 1% -39.43% (p=0.000 n=10+8) ToUpper/AbC123-8 56.9ns ± 2% 28.2ns ± 2% -50.54% (p=0.000 n=8+10) ToUpper/azAZ09_-8 62.3ns ± 1% 26.9ns ± 1% -56.82% (p=0.000 n=9+10) ToUpper/longStrinGwitHmixofsmaLLandcAps-8 219ns ± 2% 63ns ± 2% -71.17% (p=0.000 n=10+10) ToUpper/longɐstringɐwithɐnonasciiⱯchars-8 367ns ± 2% 374ns ± 3% +2.05% (p=0.000 n=9+10) ToUpper/ɐɐɐɐɐ-8 200ns ± 1% 206ns ± 1% +2.49% (p=0.000 n=10+10) ToUpper/a\u0080\U0010ffff-8 90.4ns ± 1% 93.8ns ± 0% +3.82% (p=0.000 n=10+7) ToLower/#00-8 9.59ns ± 1% 6.13ns ± 2% -36.08% (p=0.000 n=10+10) ToLower/abc-8 36.4ns ± 1% 10.4ns ± 1% -71.50% (p=0.000 n=10+10) ToLower/AbC123-8 55.8ns ± 1% 27.5ns ± 1% -50.61% (p=0.000 n=10+10) ToLower/azAZ09_-8 61.7ns ± 1% 30.2ns ± 1% -50.98% (p=0.000 n=8+10) ToLower/longStrinGwitHmixofsmaLLandcAps-8 226ns ± 1% 64ns ± 1% -71.53% (p=0.000 n=10+9) ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS-8 354ns ± 0% 361ns ± 0% +2.18% (p=0.000 n=10+10) ToLower/ⱭⱭⱭⱭⱭ-8 180ns ± 1% 186ns ± 0% +3.45% (p=0.000 n=10+9) ToLower/A\u0080\U0010ffff-8 91.7ns ± 0% 94.5ns ± 0% +2.99% (p=0.000 n=10+10) Change-Id: Ifdb8ae328ff9feacd1c170db8eebbf98c399e204 Reviewed-on: https://go-review.googlesource.com/c/go/+/170954 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-08 15:53:35 +02:00
{"longStrinGwitHmixofsmaLLandcAps", []byte("longstringwithmixofsmallandcaps")},
{"LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS", []byte("long\u0250string\u0250with\u0250nonascii\u0250chars")},
{"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", []byte("\u0251\u0251\u0251\u0251\u0251")}, // shrinks one byte per char
bytes: optimize ToLower and ToUpper for ASCII-only case Follow what CL 68370 and CL 76470 did for the respective functions in package strings. Also adjust godoc strings to match the respective strings functions and mention the special case for ASCII-only byte slices which don't need conversion. name old time/op new time/op delta ToUpper/#00-8 9.35ns ± 3% 6.08ns ± 2% -35.04% (p=0.000 n=9+9) ToUpper/ONLYUPPER-8 77.7ns ± 1% 16.9ns ± 2% -78.22% (p=0.000 n=10+10) ToUpper/abc-8 36.5ns ± 1% 22.1ns ± 1% -39.43% (p=0.000 n=10+8) ToUpper/AbC123-8 56.9ns ± 2% 28.2ns ± 2% -50.54% (p=0.000 n=8+10) ToUpper/azAZ09_-8 62.3ns ± 1% 26.9ns ± 1% -56.82% (p=0.000 n=9+10) ToUpper/longStrinGwitHmixofsmaLLandcAps-8 219ns ± 2% 63ns ± 2% -71.17% (p=0.000 n=10+10) ToUpper/longɐstringɐwithɐnonasciiⱯchars-8 367ns ± 2% 374ns ± 3% +2.05% (p=0.000 n=9+10) ToUpper/ɐɐɐɐɐ-8 200ns ± 1% 206ns ± 1% +2.49% (p=0.000 n=10+10) ToUpper/a\u0080\U0010ffff-8 90.4ns ± 1% 93.8ns ± 0% +3.82% (p=0.000 n=10+7) ToLower/#00-8 9.59ns ± 1% 6.13ns ± 2% -36.08% (p=0.000 n=10+10) ToLower/abc-8 36.4ns ± 1% 10.4ns ± 1% -71.50% (p=0.000 n=10+10) ToLower/AbC123-8 55.8ns ± 1% 27.5ns ± 1% -50.61% (p=0.000 n=10+10) ToLower/azAZ09_-8 61.7ns ± 1% 30.2ns ± 1% -50.98% (p=0.000 n=8+10) ToLower/longStrinGwitHmixofsmaLLandcAps-8 226ns ± 1% 64ns ± 1% -71.53% (p=0.000 n=10+9) ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS-8 354ns ± 0% 361ns ± 0% +2.18% (p=0.000 n=10+10) ToLower/ⱭⱭⱭⱭⱭ-8 180ns ± 1% 186ns ± 0% +3.45% (p=0.000 n=10+9) ToLower/A\u0080\U0010ffff-8 91.7ns ± 0% 94.5ns ± 0% +2.99% (p=0.000 n=10+10) Change-Id: Ifdb8ae328ff9feacd1c170db8eebbf98c399e204 Reviewed-on: https://go-review.googlesource.com/c/go/+/170954 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-08 15:53:35 +02:00
{"A\u0080\U0010FFFF", []byte("a\u0080\U0010FFFF")}, // test utf8.RuneSelf and utf8.MaxRune
}
const space = "\t\v\r\f\n\u0085\u00a0\u2000\u3000"
var trimSpaceTests = []StringTest{
{"", nil},
{" a", []byte("a")},
{"b ", []byte("b")},
{"abc", []byte("abc")},
{space + "abc" + space, []byte("abc")},
{" ", nil},
{"\u3000 ", nil},
{" \u3000", nil},
{" \t\r\n \t\t\r\r\n\n ", nil},
{" \t\r\n x\t\t\r\r\n\n ", []byte("x")},
{" \u2000\t\r\n x\t\t\r\r\ny\n \u3000", []byte("x\t\t\r\r\ny")},
{"1 \t\r\n2", []byte("1 \t\r\n2")},
{" x\x80", []byte("x\x80")},
{" x\xc0", []byte("x\xc0")},
{"x \xc0\xc0 ", []byte("x \xc0\xc0")},
{"x \xc0", []byte("x \xc0")},
{"x \xc0 ", []byte("x \xc0")},
{"x \xc0\xc0 ", []byte("x \xc0\xc0")},
{"x ☺\xc0\xc0 ", []byte("x ☺\xc0\xc0")},
{"x ☺ ", []byte("x ☺")},
}
// Execute f on each test case. funcName should be the name of f; it's used
// in failure reports.
func runStringTests(t *testing.T, f func([]byte) []byte, funcName string, testCases []StringTest) {
for _, tc := range testCases {
actual := f([]byte(tc.in))
if actual == nil && tc.out != nil {
t.Errorf("%s(%q) = nil; want %q", funcName, tc.in, tc.out)
}
if actual != nil && tc.out == nil {
t.Errorf("%s(%q) = %q; want nil", funcName, tc.in, actual)
}
if !Equal(actual, tc.out) {
t.Errorf("%s(%q) = %q; want %q", funcName, tc.in, actual, tc.out)
}
}
}
func tenRunes(r rune) string {
runes := make([]rune, 10)
for i := range runes {
runes[i] = r
}
return string(runes)
}
// User-defined self-inverse mapping function
func rot13(r rune) rune {
const step = 13
if r >= 'a' && r <= 'z' {
return ((r - 'a' + step) % 26) + 'a'
}
if r >= 'A' && r <= 'Z' {
return ((r - 'A' + step) % 26) + 'A'
}
return r
}
func TestMap(t *testing.T) {
// Run a couple of awful growth/shrinkage tests
a := tenRunes('a')
// 1. Grow. This triggers two reallocations in Map.
maxRune := func(r rune) rune { return unicode.MaxRune }
m := Map(maxRune, []byte(a))
expect := tenRunes(unicode.MaxRune)
if string(m) != expect {
t.Errorf("growing: expected %q got %q", expect, m)
}
// 2. Shrink
minRune := func(r rune) rune { return 'a' }
m = Map(minRune, []byte(tenRunes(unicode.MaxRune)))
expect = a
if string(m) != expect {
t.Errorf("shrinking: expected %q got %q", expect, m)
}
// 3. Rot13
m = Map(rot13, []byte("a to zed"))
expect = "n gb mrq"
if string(m) != expect {
t.Errorf("rot13: expected %q got %q", expect, m)
}
// 4. Rot13^2
m = Map(rot13, Map(rot13, []byte("a to zed")))
expect = "a to zed"
if string(m) != expect {
t.Errorf("rot13: expected %q got %q", expect, m)
}
// 5. Drop
dropNotLatin := func(r rune) rune {
if unicode.Is(unicode.Latin, r) {
return r
}
return -1
}
m = Map(dropNotLatin, []byte("Hello, 세계"))
expect = "Hello"
if string(m) != expect {
t.Errorf("drop: expected %q got %q", expect, m)
}
// 6. Invalid rune
invalidRune := func(r rune) rune {
return utf8.MaxRune + 1
}
m = Map(invalidRune, []byte("x"))
expect = "\uFFFD"
if string(m) != expect {
t.Errorf("invalidRune: expected %q got %q", expect, m)
}
}
func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) }
bytes: optimize ToLower and ToUpper for ASCII-only case Follow what CL 68370 and CL 76470 did for the respective functions in package strings. Also adjust godoc strings to match the respective strings functions and mention the special case for ASCII-only byte slices which don't need conversion. name old time/op new time/op delta ToUpper/#00-8 9.35ns ± 3% 6.08ns ± 2% -35.04% (p=0.000 n=9+9) ToUpper/ONLYUPPER-8 77.7ns ± 1% 16.9ns ± 2% -78.22% (p=0.000 n=10+10) ToUpper/abc-8 36.5ns ± 1% 22.1ns ± 1% -39.43% (p=0.000 n=10+8) ToUpper/AbC123-8 56.9ns ± 2% 28.2ns ± 2% -50.54% (p=0.000 n=8+10) ToUpper/azAZ09_-8 62.3ns ± 1% 26.9ns ± 1% -56.82% (p=0.000 n=9+10) ToUpper/longStrinGwitHmixofsmaLLandcAps-8 219ns ± 2% 63ns ± 2% -71.17% (p=0.000 n=10+10) ToUpper/longɐstringɐwithɐnonasciiⱯchars-8 367ns ± 2% 374ns ± 3% +2.05% (p=0.000 n=9+10) ToUpper/ɐɐɐɐɐ-8 200ns ± 1% 206ns ± 1% +2.49% (p=0.000 n=10+10) ToUpper/a\u0080\U0010ffff-8 90.4ns ± 1% 93.8ns ± 0% +3.82% (p=0.000 n=10+7) ToLower/#00-8 9.59ns ± 1% 6.13ns ± 2% -36.08% (p=0.000 n=10+10) ToLower/abc-8 36.4ns ± 1% 10.4ns ± 1% -71.50% (p=0.000 n=10+10) ToLower/AbC123-8 55.8ns ± 1% 27.5ns ± 1% -50.61% (p=0.000 n=10+10) ToLower/azAZ09_-8 61.7ns ± 1% 30.2ns ± 1% -50.98% (p=0.000 n=8+10) ToLower/longStrinGwitHmixofsmaLLandcAps-8 226ns ± 1% 64ns ± 1% -71.53% (p=0.000 n=10+9) ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS-8 354ns ± 0% 361ns ± 0% +2.18% (p=0.000 n=10+10) ToLower/ⱭⱭⱭⱭⱭ-8 180ns ± 1% 186ns ± 0% +3.45% (p=0.000 n=10+9) ToLower/A\u0080\U0010ffff-8 91.7ns ± 0% 94.5ns ± 0% +2.99% (p=0.000 n=10+10) Change-Id: Ifdb8ae328ff9feacd1c170db8eebbf98c399e204 Reviewed-on: https://go-review.googlesource.com/c/go/+/170954 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-08 15:53:35 +02:00
func BenchmarkToUpper(b *testing.B) {
for _, tc := range upperTests {
tin := []byte(tc.in)
b.Run(tc.in, func(b *testing.B) {
for i := 0; i < b.N; i++ {
actual := ToUpper(tin)
if !Equal(actual, tc.out) {
b.Errorf("ToUpper(%q) = %q; want %q", tc.in, actual, tc.out)
}
}
})
}
}
func BenchmarkToLower(b *testing.B) {
for _, tc := range lowerTests {
tin := []byte(tc.in)
b.Run(tc.in, func(b *testing.B) {
for i := 0; i < b.N; i++ {
actual := ToLower(tin)
if !Equal(actual, tc.out) {
b.Errorf("ToLower(%q) = %q; want %q", tc.in, actual, tc.out)
}
}
})
}
}
func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) }
type RepeatTest struct {
in, out string
count int
}
var RepeatTests = []RepeatTest{
{"", "", 0},
{"", "", 1},
{"", "", 2},
{"-", "", 0},
{"-", "-", 1},
{"-", "----------", 10},
{"abc ", "abc abc abc ", 3},
}
func TestRepeat(t *testing.T) {
for _, tt := range RepeatTests {
tin := []byte(tt.in)
tout := []byte(tt.out)
a := Repeat(tin, tt.count)
if !Equal(a, tout) {
t.Errorf("Repeat(%q, %d) = %q; want %q", tin, tt.count, a, tout)
continue
}
}
}
func repeat(b []byte, count int) (err error) {
defer func() {
if r := recover(); r != nil {
switch v := r.(type) {
case error:
err = v
default:
err = fmt.Errorf("%s", v)
}
}
}()
Repeat(b, count)
return
}
// See Issue golang.org/issue/16237
func TestRepeatCatchesOverflow(t *testing.T) {
tests := [...]struct {
s string
count int
errStr string
}{
0: {"--", -2147483647, "negative"},
1: {"", int(^uint(0) >> 1), ""},
2: {"-", 10, ""},
3: {"gopher", 0, ""},
4: {"-", -1, "negative"},
5: {"--", -102, "negative"},
6: {string(make([]byte, 255)), int((^uint(0))/255 + 1), "overflow"},
}
for i, tt := range tests {
err := repeat([]byte(tt.s), tt.count)
if tt.errStr == "" {
if err != nil {
t.Errorf("#%d panicked %v", i, err)
}
continue
}
if err == nil || !strings.Contains(err.Error(), tt.errStr) {
t.Errorf("#%d expected %q got %q", i, tt.errStr, err)
}
}
}
func runesEqual(a, b []rune) bool {
if len(a) != len(b) {
return false
}
for i, r := range a {
if r != b[i] {
return false
}
}
return true
}
type RunesTest struct {
in string
out []rune
lossy bool
}
var RunesTests = []RunesTest{
{"", []rune{}, false},
{" ", []rune{32}, false},
{"ABC", []rune{65, 66, 67}, false},
{"abc", []rune{97, 98, 99}, false},
{"\u65e5\u672c\u8a9e", []rune{26085, 26412, 35486}, false},
{"ab\x80c", []rune{97, 98, 0xFFFD, 99}, true},
{"ab\xc0c", []rune{97, 98, 0xFFFD, 99}, true},
}
func TestRunes(t *testing.T) {
for _, tt := range RunesTests {
tin := []byte(tt.in)
a := Runes(tin)
if !runesEqual(a, tt.out) {
t.Errorf("Runes(%q) = %v; want %v", tin, a, tt.out)
continue
}
if !tt.lossy {
// can only test reassembly if we didn't lose information
s := string(a)
if s != tt.in {
t.Errorf("string(Runes(%q)) = %x; want %x", tin, s, tin)
}
}
}
}
type TrimTest struct {
f string
in, arg, out string
}
var trimTests = []TrimTest{
{"Trim", "abba", "a", "bb"},
{"Trim", "abba", "ab", ""},
{"TrimLeft", "abba", "ab", ""},
{"TrimRight", "abba", "ab", ""},
{"TrimLeft", "abba", "a", "bba"},
{"TrimRight", "abba", "a", "abb"},
{"Trim", "<tag>", "<>", "tag"},
{"Trim", "* listitem", " *", "listitem"},
{"Trim", `"quote"`, `"`, "quote"},
{"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
bytes, strings: optimize for ASCII sets In a large codebase within Google, there are thousands of uses of: ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight An analysis of their usage shows that over 97% of them only use character sets consisting of only ASCII symbols. Uses of ContainsAny|IndexAny|LastIndexAny: 6% are 1 character (e.g., "\n" or " ") 58% are 2-4 characters (e.g., "<>" or "\r\n\t ") 24% are 5-9 characters (e.g., "()[]*^$") 10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ") We optimize for ASCII sets, which are commonly used to search for "control" characters in some string. We don't optimize for the single character scenario since IndexRune or IndexByte could be used. Uses of Trim|TrimLeft|TrimRight: 71% are 1 character (e.g., "\n" or " ") 14% are 2 characters (e.g., "\r\n") 10% are 3-4 characters (e.g., " \t\r\n") 5% are 10+ characters (e.g., "0123456789abcdefABCDEF") We optimize for the single character case with a simple closured function that only checks for that character's value. We optimize for the medium and larger sets using a 16-byte bit-map representing a set of ASCII characters. The benchmarks below have the following suffix name "%d:%d" where the first number is the length of the input and the second number is the length of the charset. == bytes package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75% BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69% BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88% BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91% BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71% BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56% BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67% BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21% BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15% BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79% BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41% BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82% BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55% BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24% BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31% BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08% BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92% BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99% BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68% BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27% BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94% BenchmarkTrimASCII/1:2-4 79.0 105 +32.91% BenchmarkTrimASCII/1:4-4 79.6 109 +36.93% BenchmarkTrimASCII/1:8-4 78.8 118 +49.75% BenchmarkTrimASCII/1:16-4 80.2 132 +64.59% BenchmarkTrimASCII/16:1-4 243 116 -52.26% BenchmarkTrimASCII/16:2-4 243 171 -29.63% BenchmarkTrimASCII/16:4-4 243 176 -27.57% BenchmarkTrimASCII/16:8-4 241 184 -23.65% BenchmarkTrimASCII/16:16-4 238 199 -16.39% BenchmarkTrimASCII/256:1-4 2580 840 -67.44% BenchmarkTrimASCII/256:2-4 2603 1175 -54.86% BenchmarkTrimASCII/256:4-4 2572 1188 -53.81% BenchmarkTrimASCII/256:8-4 2550 1191 -53.29% BenchmarkTrimASCII/256:16-4 2585 1208 -53.27% BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37% BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86% BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66% BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88% BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21% == strings package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33% BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57% BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22% BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85% BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15% BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54% BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24% BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10% BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03% BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48% BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74% BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08% BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37% BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44% BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21% BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95% BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19% BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25% BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37% BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26% BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14% BenchmarkTrimASCII/1:2-4 73.3 103 +40.52% BenchmarkTrimASCII/1:4-4 71.8 106 +47.63% BenchmarkTrimASCII/1:8-4 71.2 113 +58.71% BenchmarkTrimASCII/1:16-4 71.6 128 +78.77% BenchmarkTrimASCII/16:1-4 152 116 -23.68% BenchmarkTrimASCII/16:2-4 160 168 +5.00% BenchmarkTrimASCII/16:4-4 172 170 -1.16% BenchmarkTrimASCII/16:8-4 200 177 -11.50% BenchmarkTrimASCII/16:16-4 254 193 -24.02% BenchmarkTrimASCII/256:1-4 1438 864 -39.92% BenchmarkTrimASCII/256:2-4 1551 1195 -22.95% BenchmarkTrimASCII/256:4-4 1770 1200 -32.20% BenchmarkTrimASCII/256:8-4 2195 1216 -44.60% BenchmarkTrimASCII/256:16-4 3054 1224 -59.92% BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20% BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77% BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90% BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81% BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68% The benchmarks added test the worst case. For IndexAny, that is when the charset matches none of the input. For Trim, it is when the charset matches all of the input. Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea Reviewed-on: https://go-review.googlesource.com/31593 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
{"Trim", "\x80test\xff", "\xff", "test"},
{"Trim", " Ġ ", " ", "Ġ"},
{"Trim", " Ġİ0", "0 ", "Ġİ"},
//empty string tests
{"Trim", "abba", "", "abba"},
{"Trim", "", "123", ""},
{"Trim", "", "", ""},
{"TrimLeft", "abba", "", "abba"},
{"TrimLeft", "", "123", ""},
{"TrimLeft", "", "", ""},
{"TrimRight", "abba", "", "abba"},
{"TrimRight", "", "123", ""},
{"TrimRight", "", "", ""},
{"TrimRight", "☺\xc0", "☺", "☺\xc0"},
{"TrimPrefix", "aabb", "a", "abb"},
{"TrimPrefix", "aabb", "b", "aabb"},
{"TrimSuffix", "aabb", "a", "aabb"},
{"TrimSuffix", "aabb", "b", "aab"},
}
func TestTrim(t *testing.T) {
for _, tc := range trimTests {
name := tc.f
var f func([]byte, string) []byte
var fb func([]byte, []byte) []byte
switch name {
case "Trim":
f = Trim
case "TrimLeft":
f = TrimLeft
case "TrimRight":
f = TrimRight
case "TrimPrefix":
fb = TrimPrefix
case "TrimSuffix":
fb = TrimSuffix
default:
t.Errorf("Undefined trim function %s", name)
}
var actual string
if f != nil {
actual = string(f([]byte(tc.in), tc.arg))
} else {
actual = string(fb([]byte(tc.in), []byte(tc.arg)))
}
if actual != tc.out {
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
}
}
}
type predicate struct {
f func(r rune) bool
name string
}
var isSpace = predicate{unicode.IsSpace, "IsSpace"}
var isDigit = predicate{unicode.IsDigit, "IsDigit"}
var isUpper = predicate{unicode.IsUpper, "IsUpper"}
var isValidRune = predicate{
func(r rune) bool {
return r != utf8.RuneError
},
"IsValidRune",
}
type TrimFuncTest struct {
f predicate
in string
trimOut []byte
leftOut []byte
rightOut []byte
}
func not(p predicate) predicate {
return predicate{
func(r rune) bool {
return !p.f(r)
},
"not " + p.name,
}
}
var trimFuncTests = []TrimFuncTest{
{isSpace, space + " hello " + space,
[]byte("hello"),
[]byte("hello " + space),
[]byte(space + " hello")},
{isDigit, "\u0e50\u0e5212hello34\u0e50\u0e51",
[]byte("hello"),
[]byte("hello34\u0e50\u0e51"),
[]byte("\u0e50\u0e5212hello")},
{isUpper, "\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F",
[]byte("hello"),
[]byte("helloEF\u2C6F\u2C6FGH\u2C6F\u2C6F"),
[]byte("\u2C6F\u2C6F\u2C6F\u2C6FABCDhello")},
{not(isSpace), "hello" + space + "hello",
[]byte(space),
[]byte(space + "hello"),
[]byte("hello" + space)},
{not(isDigit), "hello\u0e50\u0e521234\u0e50\u0e51helo",
[]byte("\u0e50\u0e521234\u0e50\u0e51"),
[]byte("\u0e50\u0e521234\u0e50\u0e51helo"),
[]byte("hello\u0e50\u0e521234\u0e50\u0e51")},
{isValidRune, "ab\xc0a\xc0cd",
[]byte("\xc0a\xc0"),
[]byte("\xc0a\xc0cd"),
[]byte("ab\xc0a\xc0")},
{not(isValidRune), "\xc0a\xc0",
[]byte("a"),
[]byte("a\xc0"),
[]byte("\xc0a")},
// The nils returned by TrimLeftFunc are odd behavior, but we need
// to preserve backwards compatibility.
{isSpace, "",
nil,
nil,
[]byte("")},
{isSpace, " ",
nil,
nil,
[]byte("")},
}
func TestTrimFunc(t *testing.T) {
for _, tc := range trimFuncTests {
trimmers := []struct {
name string
trim func(s []byte, f func(r rune) bool) []byte
out []byte
}{
{"TrimFunc", TrimFunc, tc.trimOut},
{"TrimLeftFunc", TrimLeftFunc, tc.leftOut},
{"TrimRightFunc", TrimRightFunc, tc.rightOut},
}
for _, trimmer := range trimmers {
actual := trimmer.trim([]byte(tc.in), tc.f.f)
if actual == nil && trimmer.out != nil {
t.Errorf("%s(%q, %q) = nil; want %q", trimmer.name, tc.in, tc.f.name, trimmer.out)
}
if actual != nil && trimmer.out == nil {
t.Errorf("%s(%q, %q) = %q; want nil", trimmer.name, tc.in, tc.f.name, actual)
}
if !Equal(actual, trimmer.out) {
t.Errorf("%s(%q, %q) = %q; want %q", trimmer.name, tc.in, tc.f.name, actual, trimmer.out)
}
}
}
}
type IndexFuncTest struct {
in string
f predicate
first, last int
}
var indexFuncTests = []IndexFuncTest{
{"", isValidRune, -1, -1},
{"abc", isDigit, -1, -1},
{"0123", isDigit, 0, 3},
{"a1b", isDigit, 1, 1},
{space, isSpace, 0, len(space) - 3}, // last rune in space is 3 bytes
{"\u0e50\u0e5212hello34\u0e50\u0e51", isDigit, 0, 18},
{"\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F", isUpper, 0, 34},
{"12\u0e50\u0e52hello34\u0e50\u0e51", not(isDigit), 8, 12},
// tests of invalid UTF-8
{"\x801", isDigit, 1, 1},
{"\x80abc", isDigit, -1, -1},
{"\xc0a\xc0", isValidRune, 1, 1},
{"\xc0a\xc0", not(isValidRune), 0, 2},
{"\xc0☺\xc0", not(isValidRune), 0, 4},
{"\xc0☺\xc0\xc0", not(isValidRune), 0, 5},
{"ab\xc0a\xc0cd", not(isValidRune), 2, 4},
{"a\xe0\x80cd", not(isValidRune), 1, 2},
}
func TestIndexFunc(t *testing.T) {
for _, tc := range indexFuncTests {
first := IndexFunc([]byte(tc.in), tc.f.f)
if first != tc.first {
t.Errorf("IndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, first, tc.first)
}
last := LastIndexFunc([]byte(tc.in), tc.f.f)
if last != tc.last {
t.Errorf("LastIndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, last, tc.last)
}
}
}
type ReplaceTest struct {
in string
old, new string
n int
out string
}
var ReplaceTests = []ReplaceTest{
{"hello", "l", "L", 0, "hello"},
{"hello", "l", "L", -1, "heLLo"},
{"hello", "x", "X", -1, "hello"},
{"", "x", "X", -1, ""},
{"radar", "r", "<r>", -1, "<r>ada<r>"},
{"", "", "<>", -1, "<>"},
{"banana", "a", "<>", -1, "b<>n<>n<>"},
{"banana", "a", "<>", 1, "b<>nana"},
{"banana", "a", "<>", 1000, "b<>n<>n<>"},
{"banana", "an", "<>", -1, "b<><>a"},
{"banana", "ana", "<>", -1, "b<>na"},
{"banana", "", "<>", -1, "<>b<>a<>n<>a<>n<>a<>"},
{"banana", "", "<>", 10, "<>b<>a<>n<>a<>n<>a<>"},
{"banana", "", "<>", 6, "<>b<>a<>n<>a<>n<>a"},
{"banana", "", "<>", 5, "<>b<>a<>n<>a<>na"},
{"banana", "", "<>", 1, "<>banana"},
{"banana", "a", "a", -1, "banana"},
{"banana", "a", "a", 1, "banana"},
{"☺☻☹", "", "<>", -1, "<>☺<>☻<>☹<>"},
}
func TestReplace(t *testing.T) {
for _, tt := range ReplaceTests {
in := append([]byte(tt.in), "<spare>"...)
in = in[:len(tt.in)]
out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n)
if s := string(out); s != tt.out {
t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out)
}
if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] {
t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n)
}
if tt.n == -1 {
out := ReplaceAll(in, []byte(tt.old), []byte(tt.new))
if s := string(out); s != tt.out {
t.Errorf("ReplaceAll(%q, %q, %q) = %q, want %q", tt.in, tt.old, tt.new, s, tt.out)
}
}
}
}
type TitleTest struct {
in, out string
}
var TitleTests = []TitleTest{
{"", ""},
{"a", "A"},
{" aaa aaa aaa ", " Aaa Aaa Aaa "},
{" Aaa Aaa Aaa ", " Aaa Aaa Aaa "},
{"123a456", "123a456"},
{"double-blind", "Double-Blind"},
{"ÿøû", "Ÿøû"},
{"with_underscore", "With_underscore"},
{"unicode \xe2\x80\xa8 line separator", "Unicode \xe2\x80\xa8 Line Separator"},
}
func TestTitle(t *testing.T) {
for _, tt := range TitleTests {
if s := string(Title([]byte(tt.in))); s != tt.out {
t.Errorf("Title(%q) = %q, want %q", tt.in, s, tt.out)
}
}
}
var ToTitleTests = []TitleTest{
{"", ""},
{"a", "A"},
{" aaa aaa aaa ", " AAA AAA AAA "},
{" Aaa Aaa Aaa ", " AAA AAA AAA "},
{"123a456", "123A456"},
{"double-blind", "DOUBLE-BLIND"},
{"ÿøû", "ŸØÛ"},
}
func TestToTitle(t *testing.T) {
for _, tt := range ToTitleTests {
if s := string(ToTitle([]byte(tt.in))); s != tt.out {
t.Errorf("ToTitle(%q) = %q, want %q", tt.in, s, tt.out)
}
}
}
var EqualFoldTests = []struct {
s, t string
out bool
}{
{"abc", "abc", true},
{"ABcd", "ABcd", true},
{"123abc", "123ABC", true},
{"αβδ", "ΑΒΔ", true},
{"abc", "xyz", false},
{"abc", "XYZ", false},
{"abcdefghijk", "abcdefghijX", false},
{"abcdefghijk", "abcdefghij\u212A", true},
{"abcdefghijK", "abcdefghij\u212A", true},
{"abcdefghijkz", "abcdefghij\u212Ay", false},
{"abcdefghijKz", "abcdefghij\u212Ay", false},
}
func TestEqualFold(t *testing.T) {
for _, tt := range EqualFoldTests {
if out := EqualFold([]byte(tt.s), []byte(tt.t)); out != tt.out {
t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.s, tt.t, out, tt.out)
}
if out := EqualFold([]byte(tt.t), []byte(tt.s)); out != tt.out {
t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.t, tt.s, out, tt.out)
}
}
}
func TestBufferGrowNegative(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Fatal("Grow(-1) should have panicked")
}
}()
var b Buffer
b.Grow(-1)
}
func TestBufferTruncateNegative(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Fatal("Truncate(-1) should have panicked")
}
}()
var b Buffer
b.Truncate(-1)
}
func TestBufferTruncateOutOfRange(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Fatal("Truncate(20) should have panicked")
}
}()
var b Buffer
b.Write(make([]byte, 10))
b.Truncate(20)
}
var containsTests = []struct {
b, subslice []byte
want bool
}{
{[]byte("hello"), []byte("hel"), true},
{[]byte("日本語"), []byte("日本"), true},
{[]byte("hello"), []byte("Hello, world"), false},
{[]byte("東京"), []byte("京東"), false},
}
func TestContains(t *testing.T) {
for _, tt := range containsTests {
if got := Contains(tt.b, tt.subslice); got != tt.want {
t.Errorf("Contains(%q, %q) = %v, want %v", tt.b, tt.subslice, got, tt.want)
}
}
}
var ContainsAnyTests = []struct {
b []byte
substr string
expected bool
}{
{[]byte(""), "", false},
{[]byte(""), "a", false},
{[]byte(""), "abc", false},
{[]byte("a"), "", false},
{[]byte("a"), "a", true},
{[]byte("aaa"), "a", true},
{[]byte("abc"), "xyz", false},
{[]byte("abc"), "xcz", true},
{[]byte("a☺b☻c☹d"), "uvw☻xyz", true},
{[]byte("aRegExp*"), ".(|)*+?^$[]", true},
{[]byte(dots + dots + dots), " ", false},
}
func TestContainsAny(t *testing.T) {
for _, ct := range ContainsAnyTests {
if ContainsAny(ct.b, ct.substr) != ct.expected {
t.Errorf("ContainsAny(%s, %s) = %v, want %v",
ct.b, ct.substr, !ct.expected, ct.expected)
}
}
}
var ContainsRuneTests = []struct {
b []byte
r rune
expected bool
}{
{[]byte(""), 'a', false},
{[]byte("a"), 'a', true},
{[]byte("aaa"), 'a', true},
{[]byte("abc"), 'y', false},
{[]byte("abc"), 'c', true},
{[]byte("a☺b☻c☹d"), 'x', false},
{[]byte("a☺b☻c☹d"), '☻', true},
{[]byte("aRegExp*"), '*', true},
}
func TestContainsRune(t *testing.T) {
for _, ct := range ContainsRuneTests {
if ContainsRune(ct.b, ct.r) != ct.expected {
t.Errorf("ContainsRune(%q, %q) = %v, want %v",
ct.b, ct.r, !ct.expected, ct.expected)
}
}
}
var makeFieldsInput = func() []byte {
x := make([]byte, 1<<20)
// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
for i := range x {
switch rand.Intn(10) {
case 0:
x[i] = ' '
case 1:
if i > 0 && x[i-1] == 'x' {
copy(x[i-1:], "χ")
break
}
fallthrough
default:
x[i] = 'x'
}
}
return x
}
bytes: speed up Fields and FieldsFunc Applies the optimizations from golang.org/cl/42810 and golang.org/cl/37959 done to the strings package to the bytes package. name old time/op new time/op delta Fields/ASCII/16 417ns ± 4% 118ns ± 3% -71.65% (p=0.000 n=10+10) Fields/ASCII/256 5.95µs ± 3% 0.88µs ± 0% -85.23% (p=0.000 n=10+7) Fields/ASCII/4096 92.3µs ± 1% 12.8µs ± 2% -86.13% (p=0.000 n=10+10) Fields/ASCII/65536 1.49ms ± 1% 0.25ms ± 1% -83.14% (p=0.000 n=10+10) Fields/ASCII/1048576 25.0ms ± 1% 6.5ms ± 2% -74.04% (p=0.000 n=10+10) Fields/Mixed/16 406ns ± 1% 222ns ± 1% -45.24% (p=0.000 n=10+9) Fields/Mixed/256 5.78µs ± 1% 2.27µs ± 1% -60.73% (p=0.000 n=9+10) Fields/Mixed/4096 97.9µs ± 1% 40.5µs ± 3% -58.66% (p=0.000 n=10+10) Fields/Mixed/65536 1.58ms ± 1% 0.69ms ± 1% -56.58% (p=0.000 n=10+10) Fields/Mixed/1048576 26.6ms ± 1% 12.6ms ± 2% -52.44% (p=0.000 n=9+10) FieldsFunc/ASCII/16 395ns ± 1% 188ns ± 1% -52.34% (p=0.000 n=10+10) FieldsFunc/ASCII/256 5.90µs ± 1% 2.00µs ± 1% -66.06% (p=0.000 n=10+10) FieldsFunc/ASCII/4096 92.5µs ± 1% 33.0µs ± 1% -64.34% (p=0.000 n=10+9) FieldsFunc/ASCII/65536 1.48ms ± 1% 0.54ms ± 1% -63.38% (p=0.000 n=10+9) FieldsFunc/ASCII/1048576 25.1ms ± 1% 10.5ms ± 3% -58.24% (p=0.000 n=10+10) FieldsFunc/Mixed/16 401ns ± 1% 205ns ± 2% -48.87% (p=0.000 n=10+10) FieldsFunc/Mixed/256 5.70µs ± 1% 1.98µs ± 1% -65.28% (p=0.000 n=10+10) FieldsFunc/Mixed/4096 97.5µs ± 1% 35.4µs ± 1% -63.65% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 1.57ms ± 1% 0.61ms ± 1% -61.20% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 26.5ms ± 1% 11.4ms ± 2% -56.84% (p=0.000 n=10+10) name old speed new speed delta Fields/ASCII/16 38.4MB/s ± 4% 134.9MB/s ± 3% +251.55% (p=0.000 n=10+10) Fields/ASCII/256 43.0MB/s ± 3% 290.6MB/s ± 1% +575.97% (p=0.000 n=10+8) Fields/ASCII/4096 44.4MB/s ± 1% 320.0MB/s ± 2% +620.90% (p=0.000 n=10+10) Fields/ASCII/65536 44.0MB/s ± 1% 260.7MB/s ± 1% +493.15% (p=0.000 n=10+10) Fields/ASCII/1048576 42.0MB/s ± 1% 161.6MB/s ± 2% +285.21% (p=0.000 n=10+10) Fields/Mixed/16 39.4MB/s ± 1% 71.7MB/s ± 1% +82.20% (p=0.000 n=10+10) Fields/Mixed/256 44.3MB/s ± 1% 112.8MB/s ± 1% +154.64% (p=0.000 n=9+10) Fields/Mixed/4096 41.9MB/s ± 1% 101.2MB/s ± 3% +141.92% (p=0.000 n=10+10) Fields/Mixed/65536 41.5MB/s ± 1% 95.5MB/s ± 1% +130.29% (p=0.000 n=10+10) Fields/Mixed/1048576 39.4MB/s ± 1% 82.9MB/s ± 2% +110.28% (p=0.000 n=9+10) FieldsFunc/ASCII/16 40.5MB/s ± 1% 84.9MB/s ± 2% +109.80% (p=0.000 n=10+10) FieldsFunc/ASCII/256 43.4MB/s ± 1% 127.9MB/s ± 1% +194.58% (p=0.000 n=10+10) FieldsFunc/ASCII/4096 44.3MB/s ± 1% 124.2MB/s ± 1% +180.44% (p=0.000 n=10+9) FieldsFunc/ASCII/65536 44.2MB/s ± 1% 120.6MB/s ± 1% +173.06% (p=0.000 n=10+9) FieldsFunc/ASCII/1048576 41.8MB/s ± 1% 100.2MB/s ± 3% +139.53% (p=0.000 n=10+10) FieldsFunc/Mixed/16 39.8MB/s ± 1% 77.8MB/s ± 2% +95.46% (p=0.000 n=10+10) FieldsFunc/Mixed/256 44.9MB/s ± 1% 129.4MB/s ± 1% +187.97% (p=0.000 n=10+10) FieldsFunc/Mixed/4096 42.0MB/s ± 1% 115.6MB/s ± 1% +175.08% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 41.6MB/s ± 1% 107.3MB/s ± 1% +157.75% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 39.6MB/s ± 1% 91.8MB/s ± 2% +131.72% (p=0.000 n=10+10) name old alloc/op new alloc/op delta Fields/ASCII/16 80.0B ± 0% 80.0B ± 0% ~ (all equal) Fields/ASCII/256 768B ± 0% 768B ± 0% ~ (all equal) Fields/ASCII/4096 9.47kB ± 0% 9.47kB ± 0% ~ (all equal) Fields/ASCII/65536 147kB ± 0% 147kB ± 0% ~ (all equal) Fields/ASCII/1048576 2.27MB ± 0% 2.27MB ± 0% ~ (all equal) Fields/Mixed/16 96.0B ± 0% 96.0B ± 0% ~ (all equal) Fields/Mixed/256 768B ± 0% 768B ± 0% ~ (all equal) Fields/Mixed/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) Fields/Mixed/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) Fields/Mixed/1048576 2.26MB ± 0% 9.61MB ± 0% +324.89% (p=0.000 n=10+10) FieldsFunc/ASCII/16 80.0B ± 0% 80.0B ± 0% ~ (all equal) FieldsFunc/ASCII/256 768B ± 0% 768B ± 0% ~ (all equal) FieldsFunc/ASCII/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) FieldsFunc/ASCII/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) FieldsFunc/ASCII/1048576 2.27MB ± 0% 9.61MB ± 0% +323.72% (p=0.000 n=10+10) FieldsFunc/Mixed/16 96.0B ± 0% 96.0B ± 0% ~ (all equal) FieldsFunc/Mixed/256 768B ± 0% 768B ± 0% ~ (all equal) FieldsFunc/Mixed/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 2.26MB ± 0% 9.61MB ± 0% +324.89% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Fields/ASCII/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/4096 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/65536 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/1048576 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) Fields/Mixed/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) Fields/Mixed/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) FieldsFunc/ASCII/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) FieldsFunc/ASCII/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) FieldsFunc/ASCII/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) FieldsFunc/Mixed/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) Change-Id: If1926782decc2f60d3b4b8c41c2ce7d8bdedfd8f Reviewed-on: https://go-review.googlesource.com/55131 Run-TryBot: Martin Möhrmann <moehrmann@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2017-08-12 10:20:30 +02:00
var makeFieldsInputASCII = func() []byte {
x := make([]byte, 1<<20)
// Input is ~10% space, rest ASCII non-space.
for i := range x {
if rand.Intn(10) == 0 {
x[i] = ' '
} else {
x[i] = 'x'
}
}
return x
}
var bytesdata = []struct {
name string
data []byte
}{
{"ASCII", makeFieldsInputASCII()},
{"Mixed", makeFieldsInput()},
}
func BenchmarkFields(b *testing.B) {
bytes: speed up Fields and FieldsFunc Applies the optimizations from golang.org/cl/42810 and golang.org/cl/37959 done to the strings package to the bytes package. name old time/op new time/op delta Fields/ASCII/16 417ns ± 4% 118ns ± 3% -71.65% (p=0.000 n=10+10) Fields/ASCII/256 5.95µs ± 3% 0.88µs ± 0% -85.23% (p=0.000 n=10+7) Fields/ASCII/4096 92.3µs ± 1% 12.8µs ± 2% -86.13% (p=0.000 n=10+10) Fields/ASCII/65536 1.49ms ± 1% 0.25ms ± 1% -83.14% (p=0.000 n=10+10) Fields/ASCII/1048576 25.0ms ± 1% 6.5ms ± 2% -74.04% (p=0.000 n=10+10) Fields/Mixed/16 406ns ± 1% 222ns ± 1% -45.24% (p=0.000 n=10+9) Fields/Mixed/256 5.78µs ± 1% 2.27µs ± 1% -60.73% (p=0.000 n=9+10) Fields/Mixed/4096 97.9µs ± 1% 40.5µs ± 3% -58.66% (p=0.000 n=10+10) Fields/Mixed/65536 1.58ms ± 1% 0.69ms ± 1% -56.58% (p=0.000 n=10+10) Fields/Mixed/1048576 26.6ms ± 1% 12.6ms ± 2% -52.44% (p=0.000 n=9+10) FieldsFunc/ASCII/16 395ns ± 1% 188ns ± 1% -52.34% (p=0.000 n=10+10) FieldsFunc/ASCII/256 5.90µs ± 1% 2.00µs ± 1% -66.06% (p=0.000 n=10+10) FieldsFunc/ASCII/4096 92.5µs ± 1% 33.0µs ± 1% -64.34% (p=0.000 n=10+9) FieldsFunc/ASCII/65536 1.48ms ± 1% 0.54ms ± 1% -63.38% (p=0.000 n=10+9) FieldsFunc/ASCII/1048576 25.1ms ± 1% 10.5ms ± 3% -58.24% (p=0.000 n=10+10) FieldsFunc/Mixed/16 401ns ± 1% 205ns ± 2% -48.87% (p=0.000 n=10+10) FieldsFunc/Mixed/256 5.70µs ± 1% 1.98µs ± 1% -65.28% (p=0.000 n=10+10) FieldsFunc/Mixed/4096 97.5µs ± 1% 35.4µs ± 1% -63.65% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 1.57ms ± 1% 0.61ms ± 1% -61.20% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 26.5ms ± 1% 11.4ms ± 2% -56.84% (p=0.000 n=10+10) name old speed new speed delta Fields/ASCII/16 38.4MB/s ± 4% 134.9MB/s ± 3% +251.55% (p=0.000 n=10+10) Fields/ASCII/256 43.0MB/s ± 3% 290.6MB/s ± 1% +575.97% (p=0.000 n=10+8) Fields/ASCII/4096 44.4MB/s ± 1% 320.0MB/s ± 2% +620.90% (p=0.000 n=10+10) Fields/ASCII/65536 44.0MB/s ± 1% 260.7MB/s ± 1% +493.15% (p=0.000 n=10+10) Fields/ASCII/1048576 42.0MB/s ± 1% 161.6MB/s ± 2% +285.21% (p=0.000 n=10+10) Fields/Mixed/16 39.4MB/s ± 1% 71.7MB/s ± 1% +82.20% (p=0.000 n=10+10) Fields/Mixed/256 44.3MB/s ± 1% 112.8MB/s ± 1% +154.64% (p=0.000 n=9+10) Fields/Mixed/4096 41.9MB/s ± 1% 101.2MB/s ± 3% +141.92% (p=0.000 n=10+10) Fields/Mixed/65536 41.5MB/s ± 1% 95.5MB/s ± 1% +130.29% (p=0.000 n=10+10) Fields/Mixed/1048576 39.4MB/s ± 1% 82.9MB/s ± 2% +110.28% (p=0.000 n=9+10) FieldsFunc/ASCII/16 40.5MB/s ± 1% 84.9MB/s ± 2% +109.80% (p=0.000 n=10+10) FieldsFunc/ASCII/256 43.4MB/s ± 1% 127.9MB/s ± 1% +194.58% (p=0.000 n=10+10) FieldsFunc/ASCII/4096 44.3MB/s ± 1% 124.2MB/s ± 1% +180.44% (p=0.000 n=10+9) FieldsFunc/ASCII/65536 44.2MB/s ± 1% 120.6MB/s ± 1% +173.06% (p=0.000 n=10+9) FieldsFunc/ASCII/1048576 41.8MB/s ± 1% 100.2MB/s ± 3% +139.53% (p=0.000 n=10+10) FieldsFunc/Mixed/16 39.8MB/s ± 1% 77.8MB/s ± 2% +95.46% (p=0.000 n=10+10) FieldsFunc/Mixed/256 44.9MB/s ± 1% 129.4MB/s ± 1% +187.97% (p=0.000 n=10+10) FieldsFunc/Mixed/4096 42.0MB/s ± 1% 115.6MB/s ± 1% +175.08% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 41.6MB/s ± 1% 107.3MB/s ± 1% +157.75% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 39.6MB/s ± 1% 91.8MB/s ± 2% +131.72% (p=0.000 n=10+10) name old alloc/op new alloc/op delta Fields/ASCII/16 80.0B ± 0% 80.0B ± 0% ~ (all equal) Fields/ASCII/256 768B ± 0% 768B ± 0% ~ (all equal) Fields/ASCII/4096 9.47kB ± 0% 9.47kB ± 0% ~ (all equal) Fields/ASCII/65536 147kB ± 0% 147kB ± 0% ~ (all equal) Fields/ASCII/1048576 2.27MB ± 0% 2.27MB ± 0% ~ (all equal) Fields/Mixed/16 96.0B ± 0% 96.0B ± 0% ~ (all equal) Fields/Mixed/256 768B ± 0% 768B ± 0% ~ (all equal) Fields/Mixed/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) Fields/Mixed/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) Fields/Mixed/1048576 2.26MB ± 0% 9.61MB ± 0% +324.89% (p=0.000 n=10+10) FieldsFunc/ASCII/16 80.0B ± 0% 80.0B ± 0% ~ (all equal) FieldsFunc/ASCII/256 768B ± 0% 768B ± 0% ~ (all equal) FieldsFunc/ASCII/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) FieldsFunc/ASCII/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) FieldsFunc/ASCII/1048576 2.27MB ± 0% 9.61MB ± 0% +323.72% (p=0.000 n=10+10) FieldsFunc/Mixed/16 96.0B ± 0% 96.0B ± 0% ~ (all equal) FieldsFunc/Mixed/256 768B ± 0% 768B ± 0% ~ (all equal) FieldsFunc/Mixed/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 2.26MB ± 0% 9.61MB ± 0% +324.89% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Fields/ASCII/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/4096 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/65536 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/1048576 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) Fields/Mixed/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) Fields/Mixed/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) FieldsFunc/ASCII/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) FieldsFunc/ASCII/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) FieldsFunc/ASCII/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) FieldsFunc/Mixed/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) Change-Id: If1926782decc2f60d3b4b8c41c2ce7d8bdedfd8f Reviewed-on: https://go-review.googlesource.com/55131 Run-TryBot: Martin Möhrmann <moehrmann@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2017-08-12 10:20:30 +02:00
for _, sd := range bytesdata {
b.Run(sd.name, func(b *testing.B) {
for j := 1 << 4; j <= 1<<20; j <<= 4 {
b.Run(fmt.Sprintf("%d", j), func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(j))
data := sd.data[:j]
for i := 0; i < b.N; i++ {
Fields(data)
}
})
}
})
}
}
func BenchmarkFieldsFunc(b *testing.B) {
bytes: speed up Fields and FieldsFunc Applies the optimizations from golang.org/cl/42810 and golang.org/cl/37959 done to the strings package to the bytes package. name old time/op new time/op delta Fields/ASCII/16 417ns ± 4% 118ns ± 3% -71.65% (p=0.000 n=10+10) Fields/ASCII/256 5.95µs ± 3% 0.88µs ± 0% -85.23% (p=0.000 n=10+7) Fields/ASCII/4096 92.3µs ± 1% 12.8µs ± 2% -86.13% (p=0.000 n=10+10) Fields/ASCII/65536 1.49ms ± 1% 0.25ms ± 1% -83.14% (p=0.000 n=10+10) Fields/ASCII/1048576 25.0ms ± 1% 6.5ms ± 2% -74.04% (p=0.000 n=10+10) Fields/Mixed/16 406ns ± 1% 222ns ± 1% -45.24% (p=0.000 n=10+9) Fields/Mixed/256 5.78µs ± 1% 2.27µs ± 1% -60.73% (p=0.000 n=9+10) Fields/Mixed/4096 97.9µs ± 1% 40.5µs ± 3% -58.66% (p=0.000 n=10+10) Fields/Mixed/65536 1.58ms ± 1% 0.69ms ± 1% -56.58% (p=0.000 n=10+10) Fields/Mixed/1048576 26.6ms ± 1% 12.6ms ± 2% -52.44% (p=0.000 n=9+10) FieldsFunc/ASCII/16 395ns ± 1% 188ns ± 1% -52.34% (p=0.000 n=10+10) FieldsFunc/ASCII/256 5.90µs ± 1% 2.00µs ± 1% -66.06% (p=0.000 n=10+10) FieldsFunc/ASCII/4096 92.5µs ± 1% 33.0µs ± 1% -64.34% (p=0.000 n=10+9) FieldsFunc/ASCII/65536 1.48ms ± 1% 0.54ms ± 1% -63.38% (p=0.000 n=10+9) FieldsFunc/ASCII/1048576 25.1ms ± 1% 10.5ms ± 3% -58.24% (p=0.000 n=10+10) FieldsFunc/Mixed/16 401ns ± 1% 205ns ± 2% -48.87% (p=0.000 n=10+10) FieldsFunc/Mixed/256 5.70µs ± 1% 1.98µs ± 1% -65.28% (p=0.000 n=10+10) FieldsFunc/Mixed/4096 97.5µs ± 1% 35.4µs ± 1% -63.65% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 1.57ms ± 1% 0.61ms ± 1% -61.20% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 26.5ms ± 1% 11.4ms ± 2% -56.84% (p=0.000 n=10+10) name old speed new speed delta Fields/ASCII/16 38.4MB/s ± 4% 134.9MB/s ± 3% +251.55% (p=0.000 n=10+10) Fields/ASCII/256 43.0MB/s ± 3% 290.6MB/s ± 1% +575.97% (p=0.000 n=10+8) Fields/ASCII/4096 44.4MB/s ± 1% 320.0MB/s ± 2% +620.90% (p=0.000 n=10+10) Fields/ASCII/65536 44.0MB/s ± 1% 260.7MB/s ± 1% +493.15% (p=0.000 n=10+10) Fields/ASCII/1048576 42.0MB/s ± 1% 161.6MB/s ± 2% +285.21% (p=0.000 n=10+10) Fields/Mixed/16 39.4MB/s ± 1% 71.7MB/s ± 1% +82.20% (p=0.000 n=10+10) Fields/Mixed/256 44.3MB/s ± 1% 112.8MB/s ± 1% +154.64% (p=0.000 n=9+10) Fields/Mixed/4096 41.9MB/s ± 1% 101.2MB/s ± 3% +141.92% (p=0.000 n=10+10) Fields/Mixed/65536 41.5MB/s ± 1% 95.5MB/s ± 1% +130.29% (p=0.000 n=10+10) Fields/Mixed/1048576 39.4MB/s ± 1% 82.9MB/s ± 2% +110.28% (p=0.000 n=9+10) FieldsFunc/ASCII/16 40.5MB/s ± 1% 84.9MB/s ± 2% +109.80% (p=0.000 n=10+10) FieldsFunc/ASCII/256 43.4MB/s ± 1% 127.9MB/s ± 1% +194.58% (p=0.000 n=10+10) FieldsFunc/ASCII/4096 44.3MB/s ± 1% 124.2MB/s ± 1% +180.44% (p=0.000 n=10+9) FieldsFunc/ASCII/65536 44.2MB/s ± 1% 120.6MB/s ± 1% +173.06% (p=0.000 n=10+9) FieldsFunc/ASCII/1048576 41.8MB/s ± 1% 100.2MB/s ± 3% +139.53% (p=0.000 n=10+10) FieldsFunc/Mixed/16 39.8MB/s ± 1% 77.8MB/s ± 2% +95.46% (p=0.000 n=10+10) FieldsFunc/Mixed/256 44.9MB/s ± 1% 129.4MB/s ± 1% +187.97% (p=0.000 n=10+10) FieldsFunc/Mixed/4096 42.0MB/s ± 1% 115.6MB/s ± 1% +175.08% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 41.6MB/s ± 1% 107.3MB/s ± 1% +157.75% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 39.6MB/s ± 1% 91.8MB/s ± 2% +131.72% (p=0.000 n=10+10) name old alloc/op new alloc/op delta Fields/ASCII/16 80.0B ± 0% 80.0B ± 0% ~ (all equal) Fields/ASCII/256 768B ± 0% 768B ± 0% ~ (all equal) Fields/ASCII/4096 9.47kB ± 0% 9.47kB ± 0% ~ (all equal) Fields/ASCII/65536 147kB ± 0% 147kB ± 0% ~ (all equal) Fields/ASCII/1048576 2.27MB ± 0% 2.27MB ± 0% ~ (all equal) Fields/Mixed/16 96.0B ± 0% 96.0B ± 0% ~ (all equal) Fields/Mixed/256 768B ± 0% 768B ± 0% ~ (all equal) Fields/Mixed/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) Fields/Mixed/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) Fields/Mixed/1048576 2.26MB ± 0% 9.61MB ± 0% +324.89% (p=0.000 n=10+10) FieldsFunc/ASCII/16 80.0B ± 0% 80.0B ± 0% ~ (all equal) FieldsFunc/ASCII/256 768B ± 0% 768B ± 0% ~ (all equal) FieldsFunc/ASCII/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) FieldsFunc/ASCII/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) FieldsFunc/ASCII/1048576 2.27MB ± 0% 9.61MB ± 0% +323.72% (p=0.000 n=10+10) FieldsFunc/Mixed/16 96.0B ± 0% 96.0B ± 0% ~ (all equal) FieldsFunc/Mixed/256 768B ± 0% 768B ± 0% ~ (all equal) FieldsFunc/Mixed/4096 9.47kB ± 0% 24.83kB ± 0% +162.16% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 147kB ± 0% 497kB ± 0% +237.24% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 2.26MB ± 0% 9.61MB ± 0% +324.89% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Fields/ASCII/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/4096 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/65536 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/ASCII/1048576 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fields/Mixed/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) Fields/Mixed/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) Fields/Mixed/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) FieldsFunc/ASCII/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) FieldsFunc/ASCII/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) FieldsFunc/ASCII/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) FieldsFunc/Mixed/16 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/256 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/4096 1.00 ± 0% 5.00 ± 0% +400.00% (p=0.000 n=10+10) FieldsFunc/Mixed/65536 1.00 ± 0% 12.00 ± 0% +1100.00% (p=0.000 n=10+10) FieldsFunc/Mixed/1048576 1.00 ± 0% 24.00 ± 0% +2300.00% (p=0.000 n=10+10) Change-Id: If1926782decc2f60d3b4b8c41c2ce7d8bdedfd8f Reviewed-on: https://go-review.googlesource.com/55131 Run-TryBot: Martin Möhrmann <moehrmann@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2017-08-12 10:20:30 +02:00
for _, sd := range bytesdata {
b.Run(sd.name, func(b *testing.B) {
for j := 1 << 4; j <= 1<<20; j <<= 4 {
b.Run(fmt.Sprintf("%d", j), func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(j))
data := sd.data[:j]
for i := 0; i < b.N; i++ {
FieldsFunc(data, unicode.IsSpace)
}
})
}
})
}
}
func BenchmarkTrimSpace(b *testing.B) {
tests := []struct {
name string
input []byte
}{
{"NoTrim", []byte("typical")},
{"ASCII", []byte(" foo bar ")},
{"SomeNonASCII", []byte(" \u2000\t\r\n x\t\t\r\r\ny\n \u3000 ")},
{"JustNonASCII", []byte("\u2000\u2000\u2000☺☺☺☺\u3000\u3000\u3000")},
}
for _, test := range tests {
b.Run(test.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
TrimSpace(test.input)
}
})
}
}
func makeBenchInputHard() []byte {
tokens := [...]string{
"<a>", "<p>", "<b>", "<strong>",
"</a>", "</p>", "</b>", "</strong>",
"hello", "world",
}
x := make([]byte, 0, 1<<20)
for {
i := rand.Intn(len(tokens))
if len(x)+len(tokens[i]) >= 1<<20 {
break
}
x = append(x, tokens[i]...)
}
return x
}
var benchInputHard = makeBenchInputHard()
func benchmarkIndexHard(b *testing.B, sep []byte) {
for i := 0; i < b.N; i++ {
Index(benchInputHard, sep)
}
}
func benchmarkLastIndexHard(b *testing.B, sep []byte) {
for i := 0; i < b.N; i++ {
LastIndex(benchInputHard, sep)
}
}
func benchmarkCountHard(b *testing.B, sep []byte) {
for i := 0; i < b.N; i++ {
Count(benchInputHard, sep)
}
}
func BenchmarkIndexHard1(b *testing.B) { benchmarkIndexHard(b, []byte("<>")) }
func BenchmarkIndexHard2(b *testing.B) { benchmarkIndexHard(b, []byte("</pre>")) }
func BenchmarkIndexHard3(b *testing.B) { benchmarkIndexHard(b, []byte("<b>hello world</b>")) }
func BenchmarkIndexHard4(b *testing.B) {
benchmarkIndexHard(b, []byte("<pre><b>hello</b><strong>world</strong></pre>"))
}
func BenchmarkLastIndexHard1(b *testing.B) { benchmarkLastIndexHard(b, []byte("<>")) }
func BenchmarkLastIndexHard2(b *testing.B) { benchmarkLastIndexHard(b, []byte("</pre>")) }
func BenchmarkLastIndexHard3(b *testing.B) { benchmarkLastIndexHard(b, []byte("<b>hello world</b>")) }
func BenchmarkCountHard1(b *testing.B) { benchmarkCountHard(b, []byte("<>")) }
func BenchmarkCountHard2(b *testing.B) { benchmarkCountHard(b, []byte("</pre>")) }
func BenchmarkCountHard3(b *testing.B) { benchmarkCountHard(b, []byte("<b>hello world</b>")) }
func BenchmarkSplitEmptySeparator(b *testing.B) {
for i := 0; i < b.N; i++ {
Split(benchInputHard, nil)
}
}
func BenchmarkSplitSingleByteSeparator(b *testing.B) {
sep := []byte("/")
for i := 0; i < b.N; i++ {
Split(benchInputHard, sep)
}
}
func BenchmarkSplitMultiByteSeparator(b *testing.B) {
sep := []byte("hello")
for i := 0; i < b.N; i++ {
Split(benchInputHard, sep)
}
}
func BenchmarkSplitNSingleByteSeparator(b *testing.B) {
sep := []byte("/")
for i := 0; i < b.N; i++ {
SplitN(benchInputHard, sep, 10)
}
}
func BenchmarkSplitNMultiByteSeparator(b *testing.B) {
sep := []byte("hello")
for i := 0; i < b.N; i++ {
SplitN(benchInputHard, sep, 10)
}
}
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat([]byte("-"), 80)
}
}
func BenchmarkBytesCompare(b *testing.B) {
for n := 1; n <= 2048; n <<= 1 {
b.Run(fmt.Sprint(n), func(b *testing.B) {
var x = make([]byte, n)
var y = make([]byte, n)
for i := 0; i < n; i++ {
x[i] = 'a'
}
for i := 0; i < n; i++ {
y[i] = 'a'
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
Compare(x, y)
}
})
}
}
bytes, strings: optimize for ASCII sets In a large codebase within Google, there are thousands of uses of: ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight An analysis of their usage shows that over 97% of them only use character sets consisting of only ASCII symbols. Uses of ContainsAny|IndexAny|LastIndexAny: 6% are 1 character (e.g., "\n" or " ") 58% are 2-4 characters (e.g., "<>" or "\r\n\t ") 24% are 5-9 characters (e.g., "()[]*^$") 10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ") We optimize for ASCII sets, which are commonly used to search for "control" characters in some string. We don't optimize for the single character scenario since IndexRune or IndexByte could be used. Uses of Trim|TrimLeft|TrimRight: 71% are 1 character (e.g., "\n" or " ") 14% are 2 characters (e.g., "\r\n") 10% are 3-4 characters (e.g., " \t\r\n") 5% are 10+ characters (e.g., "0123456789abcdefABCDEF") We optimize for the single character case with a simple closured function that only checks for that character's value. We optimize for the medium and larger sets using a 16-byte bit-map representing a set of ASCII characters. The benchmarks below have the following suffix name "%d:%d" where the first number is the length of the input and the second number is the length of the charset. == bytes package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75% BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69% BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88% BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91% BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71% BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56% BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67% BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21% BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15% BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79% BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41% BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82% BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55% BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24% BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31% BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08% BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92% BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99% BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68% BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27% BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94% BenchmarkTrimASCII/1:2-4 79.0 105 +32.91% BenchmarkTrimASCII/1:4-4 79.6 109 +36.93% BenchmarkTrimASCII/1:8-4 78.8 118 +49.75% BenchmarkTrimASCII/1:16-4 80.2 132 +64.59% BenchmarkTrimASCII/16:1-4 243 116 -52.26% BenchmarkTrimASCII/16:2-4 243 171 -29.63% BenchmarkTrimASCII/16:4-4 243 176 -27.57% BenchmarkTrimASCII/16:8-4 241 184 -23.65% BenchmarkTrimASCII/16:16-4 238 199 -16.39% BenchmarkTrimASCII/256:1-4 2580 840 -67.44% BenchmarkTrimASCII/256:2-4 2603 1175 -54.86% BenchmarkTrimASCII/256:4-4 2572 1188 -53.81% BenchmarkTrimASCII/256:8-4 2550 1191 -53.29% BenchmarkTrimASCII/256:16-4 2585 1208 -53.27% BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37% BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86% BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66% BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88% BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21% == strings package == benchmark old ns/op new ns/op delta BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33% BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57% BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22% BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85% BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15% BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54% BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24% BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10% BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03% BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48% BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74% BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08% BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37% BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44% BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21% BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95% BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19% BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25% BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37% BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26% BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14% BenchmarkTrimASCII/1:2-4 73.3 103 +40.52% BenchmarkTrimASCII/1:4-4 71.8 106 +47.63% BenchmarkTrimASCII/1:8-4 71.2 113 +58.71% BenchmarkTrimASCII/1:16-4 71.6 128 +78.77% BenchmarkTrimASCII/16:1-4 152 116 -23.68% BenchmarkTrimASCII/16:2-4 160 168 +5.00% BenchmarkTrimASCII/16:4-4 172 170 -1.16% BenchmarkTrimASCII/16:8-4 200 177 -11.50% BenchmarkTrimASCII/16:16-4 254 193 -24.02% BenchmarkTrimASCII/256:1-4 1438 864 -39.92% BenchmarkTrimASCII/256:2-4 1551 1195 -22.95% BenchmarkTrimASCII/256:4-4 1770 1200 -32.20% BenchmarkTrimASCII/256:8-4 2195 1216 -44.60% BenchmarkTrimASCII/256:16-4 3054 1224 -59.92% BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20% BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77% BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90% BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81% BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68% The benchmarks added test the worst case. For IndexAny, that is when the charset matches none of the input. For Trim, it is when the charset matches all of the input. Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea Reviewed-on: https://go-review.googlesource.com/31593 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
func BenchmarkIndexAnyASCII(b *testing.B) {
x := Repeat([]byte{'#'}, 4096) // Never matches set
cs := "0123456789abcdef"
for k := 1; k <= 4096; k <<= 4 {
for j := 1; j <= 16; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
IndexAny(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkTrimASCII(b *testing.B) {
cs := "0123456789abcdef"
for k := 1; k <= 4096; k <<= 4 {
for j := 1; j <= 16; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
x := Repeat([]byte(cs[:j]), k) // Always matches set
for i := 0; i < b.N; i++ {
Trim(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkIndexPeriodic(b *testing.B) {
key := []byte{1, 1}
for _, skip := range [...]int{2, 4, 8, 16, 32, 64} {
b.Run(fmt.Sprintf("IndexPeriodic%d", skip), func(b *testing.B) {
buf := make([]byte, 1<<16)
for i := 0; i < len(buf); i += skip {
buf[i] = 1
}
for i := 0; i < b.N; i++ {
Index(buf, key)
}
})
}
}