From fbc62c54471f91b4e122ce4462f1e6c0e12db787 Mon Sep 17 00:00:00 2001 From: tdakkota Date: Fri, 18 Mar 2022 09:55:58 +0300 Subject: [PATCH 1/2] test: add more float benchmarks --- dec_float_test.go | 36 ++++--- testdata/floats.json | 200 +++++++++++++++++++------------------- testdata/integers.json | 102 +++++++++++++++++++ testdata/slow_floats.json | 102 +++++++++++++++++++ 4 files changed, 325 insertions(+), 115 deletions(-) create mode 100644 testdata/integers.json create mode 100644 testdata/slow_floats.json diff --git a/dec_float_test.go b/dec_float_test.go index 4fbfc9e..206e186 100644 --- a/dec_float_test.go +++ b/dec_float_test.go @@ -134,23 +134,29 @@ func TestDecoder_Float64(t *testing.T) { } func BenchmarkDecoder_Float64(b *testing.B) { - runTestdataFile("floats.json", b.Fatal, func(name string, data []byte) { - b.Run(name, func(b *testing.B) { - d := GetDecoder() - cb := func(d *Decoder) error { - _, err := d.Float64() - return err - } - b.ReportAllocs() - b.ResetTimer() + for _, file := range []string{ + "floats.json", + "slow_floats.json", + "integers.json", + } { + runTestdataFile(file, b.Fatal, func(name string, data []byte) { + b.Run(name, func(b *testing.B) { + d := GetDecoder() + cb := func(d *Decoder) error { + _, err := d.Float64() + return err + } + b.ReportAllocs() + b.ResetTimer() - for i := 0; i < b.N; i++ { - d.ResetBytes(data) + for i := 0; i < b.N; i++ { + d.ResetBytes(data) - if err := d.Arr(cb); err != nil { - b.Fatal(err) + if err := d.Arr(cb); err != nil { + b.Fatal(err) + } } - } + }) }) - }) + } } diff --git a/testdata/floats.json b/testdata/floats.json index 981c45b..1986fbe 100644 --- a/testdata/floats.json +++ b/testdata/floats.json @@ -1,102 +1,102 @@ [ - 0.6046602879796196, - 0.9405090880450124, - 0.6645600532184904, - 0.4377141871869802, - 0.4246374970712657, - 0.6868230728671094, - 0.06563701921747622, - 0.15651925473279124, - 0.09696951891448456, - 0.30091186058528707, - 0.5152126285020654, - 0.8136399609900968, - 0.21426387258237492, - 0.380657189299686, - 0.31805817433032985, - 0.4688898449024232, - 0.28303415118044517, - 0.29310185733681576, - 0.6790846759202163, - 0.21855305259276428, - 0.20318687664732285, - 0.360871416856906, - 0.5706732760710226, - 0.8624914374478864, - 0.29311424455385804, - 0.29708256355629153, - 0.7525730355516119, - 0.2065826619136986, - 0.865335013001561, - 0.6967191657466347, - 0.5238203060500009, - 0.028303083325889995, - 0.15832827774512764, - 0.6072534395455154, - 0.9752416188605784, - 0.07945362337387198, - 0.5948085976830626, - 0.05912065131387529, - 0.692024587353112, - 0.30152268100656, - 0.17326623818270528, - 0.5410998550087353, - 0.544155573000885, - 0.27850762181610883, - 0.4231522015718281, - 0.5305857153507052, - 0.2535405005150605, - 0.28208099496492467, - 0.7886049150193449, - 0.3618054804803169, - 0.8805431227416171, - 0.2971122606397708, - 0.8943617293304537, - 0.09745461839911657, - 0.9769168685862624, - 0.07429099894984302, - 0.22228941700678773, - 0.6810783123925709, - 0.24151508854715265, - 0.31152244431052484, - 0.932846428518434, - 0.741848959991823, - 0.8010550426526613, - 0.7302314772948083, - 0.18292491645390843, - 0.4283570818068078, - 0.8969919575618727, - 0.6826534880132438, - 0.9789293555766876, - 0.9222122589217269, - 0.09083727535388708, - 0.4931419977048804, - 0.9269868035744142, - 0.9549454404167818, - 0.3479539636282229, - 0.6908388315056789, - 0.7109071952999951, - 0.5637795958152644, - 0.6494894605929404, - 0.5517650490127749, - 0.7558235074915978, - 0.40380328579570035, - 0.13065111702897217, - 0.9859647293402467, - 0.8963417453962161, - 0.3220839705208817, - 0.7211477651926741, - 0.6445397825093294, - 0.08552050754191123, - 0.6695752976997745, - 0.6227283173637045, - 0.3696928436398219, - 0.2368225468054852, - 0.5352818906344061, - 0.18724610140105305, - 0.2388407028053186, - 0.6280981712183633, - 0.1267529293726013, - 0.28133029380535923, - 0.41032284435628247 + 0.6, + 0.9, + 0.6, + 0.4, + 0.4, + 0.6, + 0.0, + 0.1, + 0.0, + 0.3, + 0.5, + 0.8, + 0.2, + 0.3, + 0.3, + 0.4, + 0.2, + 0.2, + 0.6, + 0.2, + 0.2, + 0.3, + 0.5, + 0.8, + 0.2, + 0.2, + 0.7, + 0.2, + 0.8, + 0.6, + 0.5, + 0.0, + 0.1, + 0.6, + 0.9, + 0.0, + 0.5, + 0.0, + 0.6, + 0.3, + 0.1, + 0.5, + 0.5, + 0.2, + 0.4, + 0.5, + 0.2, + 0.2, + 0.7, + 0.3, + 0.8, + 0.2, + 0.8, + 0.0, + 0.9, + 0.0, + 0.2, + 0.6, + 0.2, + 0.3, + 0.9, + 0.7, + 0.8, + 0.7, + 0.1, + 0.4, + 0.8, + 0.6, + 0.9, + 0.9, + 0.0, + 0.4, + 0.9, + 0.9, + 0.3, + 0.6, + 0.7, + 0.5, + 0.6, + 0.5, + 0.7, + 0.4, + 0.1, + 0.9, + 0.8, + 0.3, + 0.7, + 0.6, + 0.0, + 0.6, + 0.6, + 0.3, + 0.2, + 0.5, + 0.1, + 0.2, + 0.6, + 0.1, + 0.2, + 0.4 ] diff --git a/testdata/integers.json b/testdata/integers.json new file mode 100644 index 0000000..fd5efe6 --- /dev/null +++ b/testdata/integers.json @@ -0,0 +1,102 @@ +[ + 604660287, + 940509088, + 664560053, + 437714187, + 424637497, + 686823072, + 656370192, + 156519254, + 969695189, + 300911860, + 515212628, + 813639960, + 214263872, + 380657189, + 318058174, + 468889844, + 283034151, + 293101857, + 679084675, + 218553052, + 203186876, + 360871416, + 570673276, + 862491437, + 293114244, + 297082563, + 752573035, + 206582661, + 865335013, + 696719165, + 523820306, + 283030833, + 158328277, + 607253439, + 975241618, + 794536233, + 594808597, + 591206513, + 692024587, + 301522681, + 173266238, + 541099855, + 544155573, + 278507621, + 423152201, + 530585715, + 253540500, + 282080994, + 788604915, + 361805480, + 880543122, + 297112260, + 894361729, + 974546183, + 976916868, + 742909989, + 222289417, + 681078312, + 241515088, + 311522444, + 932846428, + 741848923, + 801055013, + 730231483, + 182924943, + 428357078, + 896991927, + 682653438, + 978929376, + 922212269, + 908372708, + 493141904, + 926986842, + 954945418, + 347953929, + 690838889, + 710907151, + 563779544, + 649489404, + 551765049, + 755823578, + 403803235, + 130651117, + 985964767, + 896341761, + 322083917, + 721147741, + 644539794, + 855205023, + 669575245, + 622728345, + 369692819, + 236822552, + 535281861, + 187246105, + 238840786, + 628098133, + 126752913, + 281330223, + 410322847 +] diff --git a/testdata/slow_floats.json b/testdata/slow_floats.json new file mode 100644 index 0000000..981c45b --- /dev/null +++ b/testdata/slow_floats.json @@ -0,0 +1,102 @@ +[ + 0.6046602879796196, + 0.9405090880450124, + 0.6645600532184904, + 0.4377141871869802, + 0.4246374970712657, + 0.6868230728671094, + 0.06563701921747622, + 0.15651925473279124, + 0.09696951891448456, + 0.30091186058528707, + 0.5152126285020654, + 0.8136399609900968, + 0.21426387258237492, + 0.380657189299686, + 0.31805817433032985, + 0.4688898449024232, + 0.28303415118044517, + 0.29310185733681576, + 0.6790846759202163, + 0.21855305259276428, + 0.20318687664732285, + 0.360871416856906, + 0.5706732760710226, + 0.8624914374478864, + 0.29311424455385804, + 0.29708256355629153, + 0.7525730355516119, + 0.2065826619136986, + 0.865335013001561, + 0.6967191657466347, + 0.5238203060500009, + 0.028303083325889995, + 0.15832827774512764, + 0.6072534395455154, + 0.9752416188605784, + 0.07945362337387198, + 0.5948085976830626, + 0.05912065131387529, + 0.692024587353112, + 0.30152268100656, + 0.17326623818270528, + 0.5410998550087353, + 0.544155573000885, + 0.27850762181610883, + 0.4231522015718281, + 0.5305857153507052, + 0.2535405005150605, + 0.28208099496492467, + 0.7886049150193449, + 0.3618054804803169, + 0.8805431227416171, + 0.2971122606397708, + 0.8943617293304537, + 0.09745461839911657, + 0.9769168685862624, + 0.07429099894984302, + 0.22228941700678773, + 0.6810783123925709, + 0.24151508854715265, + 0.31152244431052484, + 0.932846428518434, + 0.741848959991823, + 0.8010550426526613, + 0.7302314772948083, + 0.18292491645390843, + 0.4283570818068078, + 0.8969919575618727, + 0.6826534880132438, + 0.9789293555766876, + 0.9222122589217269, + 0.09083727535388708, + 0.4931419977048804, + 0.9269868035744142, + 0.9549454404167818, + 0.3479539636282229, + 0.6908388315056789, + 0.7109071952999951, + 0.5637795958152644, + 0.6494894605929404, + 0.5517650490127749, + 0.7558235074915978, + 0.40380328579570035, + 0.13065111702897217, + 0.9859647293402467, + 0.8963417453962161, + 0.3220839705208817, + 0.7211477651926741, + 0.6445397825093294, + 0.08552050754191123, + 0.6695752976997745, + 0.6227283173637045, + 0.3696928436398219, + 0.2368225468054852, + 0.5352818906344061, + 0.18724610140105305, + 0.2388407028053186, + 0.6280981712183633, + 0.1267529293726013, + 0.28133029380535923, + 0.41032284435628247 +] From 9f27e284349673096cafc4200e9e089b6f93d66a Mon Sep 17 00:00:00 2001 From: tdakkota Date: Fri, 18 Mar 2022 11:04:40 +0300 Subject: [PATCH 2/2] feat: improve decoding layout, reduce bound checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name old time/op new time/op delta Decoder_Float64/floats.json-4 3.17µs ± 4% 2.96µs ± 1% -6.66% (p=0.000 n=23+25) Decoder_Float64/slow_floats.json-4 21.2µs ± 2% 20.8µs ± 2% -1.93% (p=0.000 n=23+25) Decoder_Float64/integers.json-4 4.32µs ± 2% 3.85µs ± 4% -10.92% (p=0.000 n=24+25) --- dec_float.go | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/dec_float.go b/dec_float.go index dad6565..d6a7915 100644 --- a/dec_float.go +++ b/dec_float.go @@ -8,17 +8,19 @@ import ( "github.com/go-faster/errors" ) -var pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000} - -var floatDigits []int8 +var ( + pow10 = [...]uint64{1, 10, 100, 1000, 10000, 100000, 1000000} + floatDigits = [256]int8{} +) -const invalidCharForNumber = int8(-1) -const endOfNumber = int8(-2) -const dotInNumber = int8(-3) -const maxFloat64 = 1<<63 - 1 +const ( + invalidCharForNumber = int8(-1) + endOfNumber = int8(-2) + dotInNumber = int8(-3) + maxFloat64 = 1<<63 - 1 +) func init() { - floatDigits = make([]int8, 256) for i := 0; i < len(floatDigits); i++ { floatDigits[i] = invalidCharForNumber } @@ -88,25 +90,24 @@ func (d *Decoder) positiveFloat32() (float32, error) { i := d.head // First char. if i == d.tail { - return d.f32Slow() + return d.float32Slow() } c := d.buf[i] i++ ind := floatDigits[c] switch ind { case invalidCharForNumber: - return d.f32Slow() + return d.float32Slow() case endOfNumber: return 0, errors.New("empty") case dotInNumber: return 0, errors.New("leading dot") case 0: if i == d.tail { - return d.f32Slow() + return d.float32Slow() } c = d.buf[i] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + if floatDigits[c] >= 0 { return 0, errors.New("leading zero") } } @@ -118,7 +119,7 @@ NonDecimalLoop: ind := floatDigits[c] switch ind { case invalidCharForNumber: - return d.f32Slow() + return d.float32Slow() case endOfNumber: d.head = i return float32(value), nil @@ -126,7 +127,7 @@ NonDecimalLoop: break NonDecimalLoop } if value > uint64SafeToMultiple10 { - return d.f32Slow() + return d.float32Slow() } value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; } @@ -135,7 +136,7 @@ NonDecimalLoop: i++ decimalPlaces := 0 if i == d.tail { - return d.f32Slow() + return d.float32Slow() } for ; i < d.tail; i++ { c = d.buf[i] @@ -147,18 +148,18 @@ NonDecimalLoop: return float32(float64(value) / float64(pow10[decimalPlaces])), nil } // too many decimal places - return d.f32Slow() + return d.float32Slow() case invalidCharForNumber, dotInNumber: - return d.f32Slow() + return d.float32Slow() } decimalPlaces++ if value > uint64SafeToMultiple10 { - return d.f32Slow() + return d.float32Slow() } value = (value << 3) + (value << 1) + uint64(ind) } } - return d.f32Slow() + return d.float32Slow() } var numberSet = [256]byte{ @@ -214,7 +215,7 @@ const ( size64 = 64 ) -func (d *Decoder) f32Slow() (float32, error) { +func (d *Decoder) float32Slow() (float32, error) { v, err := d.floatSlow(size32) if err != nil { return 0, err @@ -228,6 +229,10 @@ func (d *Decoder) Float64() (float64, error) { if err != nil { return 0, errors.Wrap(err, "byte") } + if floatDigits[c] >= 0 { + d.unread() + return d.positiveFloat64() + } switch c { case '-': v, err := d.positiveFloat64() @@ -235,9 +240,6 @@ func (d *Decoder) Float64() (float64, error) { return 0, err } return -v, err - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - d.unread() - return d.positiveFloat64() default: return 0, badToken(c) }