This repository was archived by the owner on Oct 17, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
[WIP] Optimize encoding and decoding float64 slices #117
Open
xichen2020
wants to merge
7
commits into
master
Choose a base branch
from
xichen-optimize-float64s-encoding-decoding
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
868e5a3
Optimize encoding float64 slice
xichen2020 a5c275a
Optimize decoding float64 slices and some refactoring
xichen2020 d7eef07
Fix tests
xichen2020 68eec50
More refactoring
xichen2020 08dc67b
Fixing more tests
xichen2020 7bd85c5
Add benchmarks
xichen2020 3e1ee15
Fix metalinter
xichen2020 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| // Copyright (c) 2017 Uber Technologies, Inc. | ||
| // | ||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| // of this software and associated documentation files (the "Software"), to deal | ||
| // in the Software without restriction, including without limitation the rights | ||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| // copies of the Software, and to permit persons to whom the Software is | ||
| // furnished to do so, subject to the following conditions: | ||
| // | ||
| // The above copyright notice and this permission notice shall be included in | ||
| // all copies or substantial portions of the Software. | ||
| // | ||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| // THE SOFTWARE. | ||
|
|
||
| package msgpack | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "math/rand" | ||
| "testing" | ||
| ) | ||
|
|
||
| var ( | ||
| smallFloat64s []float64 | ||
| mediumFloat64s []float64 | ||
| largeFloat64s []float64 | ||
| ) | ||
|
|
||
| func BenchmarkEncodeFloat64NativeSmall(b *testing.B) { | ||
| benchEncode(b, smallFloat64s, nonPackedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkEncodeFloat64NativeMedium(b *testing.B) { | ||
| benchEncode(b, mediumFloat64s, nonPackedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkEncodeFloat64NativeLarge(b *testing.B) { | ||
| benchEncode(b, largeFloat64s, nonPackedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkEncodeFloat64PackedSmall(b *testing.B) { | ||
| benchEncode(b, smallFloat64s, packedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkEncodeFloat64PackedMedium(b *testing.B) { | ||
| benchEncode(b, mediumFloat64s, packedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkEncodeFloat64PackedLarge(b *testing.B) { | ||
| benchEncode(b, largeFloat64s, packedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkDecodeFloat64NativeSmall(b *testing.B) { | ||
| benchDecode(b, smallFloat64s, nonPackedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkDecodeFloat64NativeMedium(b *testing.B) { | ||
| benchDecode(b, mediumFloat64s, nonPackedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkDecodeFloat64NativeLarge(b *testing.B) { | ||
| benchDecode(b, largeFloat64s, nonPackedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkDecodeFloat64PackedSmall(b *testing.B) { | ||
| benchDecode(b, smallFloat64s, packedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkDecodeFloat64PackedMedium(b *testing.B) { | ||
| benchDecode(b, mediumFloat64s, packedEncoding) | ||
| } | ||
|
|
||
| func BenchmarkDecodeFloat64PackedLarge(b *testing.B) { | ||
| benchDecode(b, largeFloat64s, packedEncoding) | ||
| } | ||
|
|
||
| func benchEncode(b *testing.B, values []float64, encodingType encodingType) { | ||
| buffer := NewBufferedEncoder() | ||
| encoder := newBaseEncoder(buffer) | ||
| b.ResetTimer() | ||
| for n := 0; n < b.N; n++ { | ||
| encoder.encodeFloat64Slice(values, encodingType) | ||
| } | ||
| } | ||
|
|
||
| func benchDecode(b *testing.B, values []float64, encodingType encodingType) { | ||
| encoded := encodedBytes(values, encodingType) | ||
| iterator := newBaseIterator(nil, nil) | ||
| b.ResetTimer() | ||
| for n := 0; n < b.N; n++ { | ||
| reader := bytes.NewBuffer(encoded) | ||
| iterator.reset(reader) | ||
| iterator.decodeFloat64Slice(encodingType) | ||
| } | ||
| } | ||
|
|
||
| func encodedBytes(values []float64, encodingType encodingType) []byte { | ||
| buffer := NewBufferedEncoder() | ||
| encoder := newBaseEncoder(buffer) | ||
| encoder.encodeFloat64Slice(values, encodingType) | ||
| return buffer.Bytes() | ||
| } | ||
|
|
||
| func init() { | ||
| smallFloat64s = make([]float64, 16) | ||
| for i := 0; i < len(smallFloat64s); i++ { | ||
| smallFloat64s[i] = rand.Float64() * 100 | ||
| } | ||
|
|
||
| mediumFloat64s = make([]float64, 1120) | ||
| for i := 0; i < len(mediumFloat64s); i++ { | ||
| mediumFloat64s[i] = rand.Float64() * 100 | ||
| } | ||
|
|
||
| largeFloat64s = make([]float64, 65536) | ||
| for i := 0; i < len(largeFloat64s); i++ { | ||
| largeFloat64s[i] = rand.Float64() * 100 | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,13 +21,16 @@ | |
| package msgpack | ||
|
|
||
| import ( | ||
| "math" | ||
|
|
||
| "github.com/m3db/m3metrics/metric/id" | ||
| "github.com/m3db/m3metrics/policy" | ||
| ) | ||
|
|
||
| type encodeVarintFn func(value int64) | ||
| type encodeBoolFn func(value bool) | ||
| type encodeFloat64Fn func(value float64) | ||
| type encodeFloat64SliceFn func(value []float64, encodingType encodingType) | ||
| type encodeBytesFn func(value []byte) | ||
| type encodeBytesLenFn func(value int) | ||
| type encodeArrayLenFn func(value int) | ||
|
|
@@ -37,10 +40,12 @@ type encodePolicyFn func(p policy.Policy) | |
| // baseEncoder is the base encoder that provides common encoding APIs. | ||
| type baseEncoder struct { | ||
| bufEncoder BufferedEncoder | ||
| tmpBuf []byte | ||
| encodeErr error | ||
| encodeVarintFn encodeVarintFn | ||
| encodeBoolFn encodeBoolFn | ||
| encodeFloat64Fn encodeFloat64Fn | ||
| encodeFloat64SliceFn encodeFloat64SliceFn | ||
| encodeBytesFn encodeBytesFn | ||
| encodeBytesLenFn encodeBytesLenFn | ||
| encodeArrayLenFn encodeArrayLenFn | ||
|
|
@@ -49,11 +54,15 @@ type baseEncoder struct { | |
| } | ||
|
|
||
| func newBaseEncoder(encoder BufferedEncoder) encoderBase { | ||
| enc := &baseEncoder{bufEncoder: encoder} | ||
| enc := &baseEncoder{ | ||
| bufEncoder: encoder, | ||
| tmpBuf: make([]byte, numBytesInFloat64), | ||
| } | ||
|
|
||
| enc.encodeVarintFn = enc.encodeVarintInternal | ||
| enc.encodeBoolFn = enc.encodeBoolInternal | ||
| enc.encodeFloat64Fn = enc.encodeFloat64Internal | ||
| enc.encodeFloat64SliceFn = enc.encodeFloat64SliceInternal | ||
| enc.encodeBytesFn = enc.encodeBytesInternal | ||
| enc.encodeBytesLenFn = enc.encodeBytesLenInternal | ||
| enc.encodeArrayLenFn = enc.encodeArrayLenInternal | ||
|
|
@@ -79,6 +88,10 @@ func (enc *baseEncoder) encodeArrayLen(value int) { enc.encode | |
| func (enc *baseEncoder) encodeStoragePolicy(p policy.StoragePolicy) { enc.encodeStoragePolicyFn(p) } | ||
| func (enc *baseEncoder) encodePolicy(p policy.Policy) { enc.encodePolicyFn(p) } | ||
|
|
||
| func (enc *baseEncoder) encodeFloat64Slice(values []float64, encodingType encodingType) { | ||
| enc.encodeFloat64SliceFn(values, encodingType) | ||
| } | ||
|
|
||
| func (enc *baseEncoder) reset(encoder BufferedEncoder) { | ||
| enc.bufEncoder = encoder | ||
| enc.encodeErr = nil | ||
|
|
@@ -187,6 +200,50 @@ func (enc *baseEncoder) encodeFloat64Internal(value float64) { | |
| enc.encodeErr = enc.bufEncoder.EncodeFloat64(value) | ||
| } | ||
|
|
||
| func (enc *baseEncoder) encodeFloat64SliceInternal(values []float64, encodingType encodingType) { | ||
| if encodingType == nonPackedEncoding { | ||
| enc.encodeFloat64SliceNative(values) | ||
| return | ||
| } | ||
| enc.encodeFloat64SlicePacked(values) | ||
| } | ||
|
|
||
| // encodeFloat64SliceNative encodes a slice of float64 values using | ||
| // native MessagePack encoding. | ||
| func (enc *baseEncoder) encodeFloat64SliceNative(values []float64) { | ||
| if enc.encodeErr != nil { | ||
| return | ||
| } | ||
| if enc.encodeErr = enc.bufEncoder.EncodeArrayLen(len(values)); enc.encodeErr != nil { | ||
| return | ||
| } | ||
| for _, v := range values { | ||
| if enc.encodeErr = enc.bufEncoder.EncodeFloat64(v); enc.encodeErr != nil { | ||
| return | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // encodeFloat64SliceNative encodes a slice of float64 values using | ||
| // more compact encoding by encoding the float64 values as byte. | ||
| func (enc *baseEncoder) encodeFloat64SlicePacked(values []float64) { | ||
| if enc.encodeErr != nil { | ||
| return | ||
| } | ||
| numValues := len(values) | ||
| numBytes := numValues * numBytesInFloat64 | ||
| if enc.encodeErr = enc.bufEncoder.EncodeBytesLen(numBytes); enc.encodeErr != nil { | ||
| return | ||
| } | ||
| for i := 0; i < numValues; i++ { | ||
| byteOrder.PutUint64(enc.tmpBuf, math.Float64bits(values[i])) | ||
| _, enc.encodeErr = enc.bufEncoder.Buffer().Write(enc.tmpBuf) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to write the bytes on at a time or would it perhaps be beneficial to write them only into the buffer in the loop and then write the entire buffer once outside the loop? |
||
| if enc.encodeErr != nil { | ||
| return | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func (enc *baseEncoder) encodeBytesInternal(value []byte) { | ||
| if enc.encodeErr != nil { | ||
| return | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: take it or leave it, but could combine these into a single benchmark with a table, for example: