influxdb/tsdb/tsm1/float_test.go

339 lines
6.2 KiB
Go
Raw Normal View History

2018-09-26 17:39:21 +00:00
package tsm1_test
import (
"fmt"
"math"
"reflect"
"testing"
"testing/quick"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/platform/tsdb/tsm1"
)
func TestFloatEncoder_Simple(t *testing.T) {
// Example from the paper
s := tsm1.NewFloatEncoder()
s.Write(12)
s.Write(12)
s.Write(24)
// extra tests
// floating point masking/shifting bug
s.Write(13)
s.Write(24)
// delta-of-delta sizes
s.Write(24)
s.Write(24)
s.Write(24)
s.Flush()
b, err := s.Bytes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var it tsm1.FloatDecoder
if err := it.SetBytes(b); err != nil {
t.Fatalf("unexpected error creating float decoder: %v", err)
}
want := []float64{
12,
12,
24,
13,
24,
24,
24,
24,
}
for _, w := range want {
if !it.Next() {
t.Fatalf("Next()=false, want true")
}
vv := it.Values()
if w != vv {
t.Errorf("Values()=(%v), want (%v)\n", vv, w)
}
}
if it.Next() {
t.Fatalf("Next()=true, want false")
}
if err := it.Error(); err != nil {
t.Errorf("it.Error()=%v, want nil", err)
}
}
func TestFloatEncoder_SimilarFloats(t *testing.T) {
s := tsm1.NewFloatEncoder()
want := []float64{
6.00065e+06,
6.000656e+06,
6.000657e+06,
6.000659e+06,
6.000661e+06,
}
for _, v := range want {
s.Write(v)
}
s.Flush()
b, err := s.Bytes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var it tsm1.FloatDecoder
if err := it.SetBytes(b); err != nil {
t.Fatalf("unexpected error creating float decoder: %v", err)
}
for _, w := range want {
if !it.Next() {
t.Fatalf("Next()=false, want true")
}
vv := it.Values()
if w != vv {
t.Errorf("Values()=(%v), want (%v)\n", vv, w)
}
}
if it.Next() {
t.Fatalf("Next()=true, want false")
}
if err := it.Error(); err != nil {
t.Errorf("it.Error()=%v, want nil", err)
}
}
var twoHoursData = []float64{
// 2h of data, rows of 10 values
761, 727, 763, 706, 700, 679, 757, 708, 739, 707,
699, 740, 729, 766, 730, 715, 705, 693, 765, 724,
799, 761, 737, 766, 756, 719, 722, 801, 747, 731,
742, 744, 791, 750, 759, 809, 751, 705, 770, 792,
727, 762, 772, 721, 748, 753, 744, 716, 776, 659,
789, 766, 758, 690, 795, 770, 758, 723, 767, 765,
693, 706, 681, 727, 724, 780, 678, 696, 758, 740,
735, 700, 742, 747, 752, 734, 743, 732, 746, 770,
780, 710, 731, 712, 712, 741, 770, 770, 754, 718,
670, 775, 749, 795, 756, 741, 787, 721, 745, 782,
765, 780, 811, 790, 836, 743, 858, 739, 762, 770,
752, 763, 795, 792, 746, 786, 785, 774, 786, 718,
}
func TestFloatEncoder_Roundtrip(t *testing.T) {
s := tsm1.NewFloatEncoder()
for _, p := range twoHoursData {
s.Write(p)
}
s.Flush()
b, err := s.Bytes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var it tsm1.FloatDecoder
if err := it.SetBytes(b); err != nil {
t.Fatalf("unexpected error creating float decoder: %v", err)
}
for _, w := range twoHoursData {
if !it.Next() {
t.Fatalf("Next()=false, want true")
}
vv := it.Values()
// t.Logf("it.Values()=(%+v, %+v)\n", time.Unix(int64(tt), 0), vv)
if w != vv {
t.Errorf("Values()=(%v), want (%v)\n", vv, w)
}
}
if it.Next() {
t.Fatalf("Next()=true, want false")
}
if err := it.Error(); err != nil {
t.Errorf("it.Error()=%v, want nil", err)
}
}
func TestFloatEncoder_Roundtrip_NaN(t *testing.T) {
s := tsm1.NewFloatEncoder()
s.Write(1.0)
s.Write(math.NaN())
s.Write(2.0)
s.Flush()
_, err := s.Bytes()
if err == nil {
t.Fatalf("expected error. got nil")
}
}
Batch oriented float encoders This commit adds a tsm1 function for encoding a batch of floats into a buffer. Further, it replaces the `bitstream` library used in the existing encoders (and all the current decoders) with inlined bit expressions within the encoder, significantly reducing the function call overhead for larger batches. The following benchmarks compare the performance of the existing iterator based encoders, and the new batch oriented encoders. They look at a sequential input slice and a randomly generated input slice. name old time/op new time/op delta EncodeFloats/10_seq 1.14µs ± 3% 0.24µs ± 3% -78.94% (p=0.000 n=10+10) EncodeFloats/10_ran 1.69µs ± 2% 0.21µs ± 3% -87.43% (p=0.000 n=10+10) EncodeFloats/100_seq 7.07µs ± 1% 1.72µs ± 1% -75.62% (p=0.000 n=7+9) EncodeFloats/100_ran 15.8µs ± 4% 1.8µs ± 1% -88.60% (p=0.000 n=10+9) EncodeFloats/1000_seq 50.2µs ± 3% 16.2µs ± 2% -67.66% (p=0.000 n=10+10) EncodeFloats/1000_ran 174µs ± 2% 16µs ± 2% -90.77% (p=0.000 n=10+10) name old alloc/op new alloc/op delta EncodeFloats/10_seq 0.00B 0.00B ~ (all equal) EncodeFloats/10_ran 0.00B 0.00B ~ (all equal) EncodeFloats/100_seq 0.00B 0.00B ~ (all equal) EncodeFloats/100_ran 0.00B 0.00B ~ (all equal) EncodeFloats/1000_seq 0.00B 0.00B ~ (all equal) EncodeFloats/1000_ran 0.00B 0.00B ~ (all equal) name old allocs/op new allocs/op delta EncodeFloats/10_seq 0.00 0.00 ~ (all equal) EncodeFloats/10_ran 0.00 0.00 ~ (all equal) EncodeFloats/100_seq 0.00 0.00 ~ (all equal) EncodeFloats/100_ran 0.00 0.00 ~ (all equal) EncodeFloats/1000_seq 0.00 0.00 ~ (all equal) EncodeFloats/1000_ran 0.00 0.00 ~ (all equal)
2018-09-11 12:04:52 +00:00
func TestFloatEncoder_Empty(t *testing.T) {
s := tsm1.NewFloatEncoder()
s.Flush()
b, err := s.Bytes()
if err != nil {
t.Fatal(err)
}
var dec tsm1.FloatDecoder
if err := dec.SetBytes(b); err != nil {
t.Fatal(err)
}
var got []float64
for dec.Next() {
got = append(got, dec.Values())
}
if len(got) != 0 {
t.Fatalf("got len %d, expected 0", len(got))
}
}
2018-09-26 17:39:21 +00:00
func Test_FloatEncoder_Quick(t *testing.T) {
quick.Check(func(values []float64) bool {
expected := values
if values == nil {
expected = []float64{}
}
// Write values to encoder.
enc := tsm1.NewFloatEncoder()
for _, v := range values {
enc.Write(v)
}
enc.Flush()
// Read values out of decoder.
got := make([]float64, 0, len(values))
b, err := enc.Bytes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var dec tsm1.FloatDecoder
if err := dec.SetBytes(b); err != nil {
t.Fatal(err)
}
for dec.Next() {
got = append(got, dec.Values())
}
// Verify that input and output values match.
if !reflect.DeepEqual(expected, got) {
t.Fatalf("mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", expected, got)
}
return true
}, nil)
}
func TestFloatDecoder_Empty(t *testing.T) {
var dec tsm1.FloatDecoder
if err := dec.SetBytes([]byte{}); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if dec.Next() {
t.Fatalf("exp next == false, got true")
}
}
func BenchmarkFloatEncoder(b *testing.B) {
for i := 0; i < b.N; i++ {
s := tsm1.NewFloatEncoder()
for _, tt := range twoHoursData {
s.Write(tt)
}
s.Flush()
}
}
func BenchmarkFloatDecoder(b *testing.B) {
s := tsm1.NewFloatEncoder()
for _, tt := range twoHoursData {
s.Write(tt)
}
s.Flush()
bytes, err := s.Bytes()
if err != nil {
b.Fatalf("unexpected error: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var it tsm1.FloatDecoder
if err := it.SetBytes(bytes); err != nil {
b.Fatalf("unexpected error creating float decoder: %v", err)
}
for j := 0; j < len(twoHoursData); it.Next() {
j++
}
}
}
func BenchmarkFloatDecoder_DecodeAll(b *testing.B) {
benchmarks := []int{
1,
55,
550,
1000,
}
for _, size := range benchmarks {
s := tsm1.NewFloatEncoder()
for c := 0; c < size; c++ {
s.Write(twoHoursData[c%len(twoHoursData)])
}
s.Flush()
bytes, err := s.Bytes()
if err != nil {
b.Fatalf("unexpected error: %v", err)
}
b.Run(fmt.Sprintf("%d", size), func(b *testing.B) {
b.SetBytes(int64(len(bytes)))
dst := make([]float64, size)
for i := 0; i < b.N; i++ {
var it tsm1.FloatDecoder
if err := it.SetBytes(bytes); err != nil {
b.Fatalf("unexpected error creating float decoder: %v", err)
}
i := 0
for it.Next() {
dst[i] = it.Values()
i++
}
if len(dst) != size {
b.Fatalf("unexpected length -got/+exp\n%s", cmp.Diff(len(dst), size))
}
}
})
}
}