github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/ptable/block_test.go (about)

     1  // Copyright 2018 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package ptable
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"testing"
    11  	"time"
    12  	"unsafe"
    13  
    14  	"golang.org/x/exp/rand"
    15  )
    16  
    17  func randBlock(rng *rand.Rand, rows int, schema []ColumnType) ([]byte, []interface{}) {
    18  	data := make([]interface{}, len(schema))
    19  	for col := range data {
    20  		switch schema[col] {
    21  		case ColumnTypeBool:
    22  			var v Bitmap
    23  			for row := 0; row < rows; row++ {
    24  				v = v.set(row, rng.Int31n(2) == 0)
    25  			}
    26  			data[col] = v
    27  		case ColumnTypeInt8:
    28  			v := make([]int8, rows)
    29  			for row := 0; row < rows; row++ {
    30  				v[row] = int8(rng.Int31n(1 << 8))
    31  			}
    32  			data[col] = v
    33  		case ColumnTypeInt16:
    34  			v := make([]int16, rows)
    35  			for row := 0; row < rows; row++ {
    36  				v[row] = int16(rng.Int31n(1 << 16))
    37  			}
    38  			data[col] = v
    39  		case ColumnTypeInt32:
    40  			v := make([]int32, rows)
    41  			for row := 0; row < rows; row++ {
    42  				v[row] = rng.Int31()
    43  			}
    44  			data[col] = v
    45  		case ColumnTypeInt64:
    46  			v := make([]int64, rows)
    47  			for row := 0; row < rows; row++ {
    48  				v[row] = rng.Int63()
    49  			}
    50  			data[col] = v
    51  		case ColumnTypeFloat32:
    52  			v := make([]float32, rows)
    53  			for row := 0; row < rows; row++ {
    54  				v[row] = rng.Float32()
    55  			}
    56  			data[col] = v
    57  		case ColumnTypeFloat64:
    58  			v := make([]float64, rows)
    59  			for row := 0; row < rows; row++ {
    60  				v[row] = rng.Float64()
    61  			}
    62  			data[col] = v
    63  		case ColumnTypeBytes:
    64  			v := make([][]byte, rows)
    65  			for row := 0; row < rows; row++ {
    66  				v[row] = make([]byte, rng.Intn(20))
    67  				rng.Read(v[row])
    68  			}
    69  			data[col] = v
    70  		}
    71  	}
    72  
    73  	var w blockWriter
    74  	w.init(schema)
    75  
    76  	for row := 0; row < rows; row++ {
    77  		for col := 0; col < len(schema); col++ {
    78  			switch schema[col] {
    79  			case ColumnTypeBool:
    80  				w.PutBool(col, data[col].(Bitmap).Get(row))
    81  			case ColumnTypeInt8:
    82  				w.PutInt8(col, data[col].([]int8)[row])
    83  			case ColumnTypeInt16:
    84  				w.PutInt16(col, data[col].([]int16)[row])
    85  			case ColumnTypeInt32:
    86  				w.PutInt32(col, data[col].([]int32)[row])
    87  			case ColumnTypeInt64:
    88  				w.PutInt64(col, data[col].([]int64)[row])
    89  			case ColumnTypeFloat32:
    90  				w.PutFloat32(col, data[col].([]float32)[row])
    91  			case ColumnTypeFloat64:
    92  				w.PutFloat64(col, data[col].([]float64)[row])
    93  			case ColumnTypeBytes:
    94  				w.PutBytes(col, data[col].([][]byte)[row])
    95  			}
    96  		}
    97  	}
    98  
    99  	return w.Finish(), data
   100  }
   101  
   102  func testSchema(t *testing.T, rng *rand.Rand, rows int, schema []ColumnType) {
   103  	name := (ColumnTypes)(schema).String()
   104  	t.Run(name, func(t *testing.T) {
   105  		block, data := randBlock(rng, rows, schema)
   106  
   107  		r := NewBlock(block)
   108  		if r.cols != int32(len(schema)) {
   109  			t.Fatalf("expected %d columns, but found %d\n", len(schema), r.cols)
   110  		}
   111  		if r.rows != int32(rows) {
   112  			t.Fatalf("expected %d rows, but found %d\n", rows, r.rows)
   113  		}
   114  		for col := range schema {
   115  			if schema[col] != r.Column(col).Type {
   116  				t.Fatalf("schema mismatch: %s != %s\n", schema[col], r.Column(col).Type)
   117  			}
   118  		}
   119  
   120  		for col := range data {
   121  			vec := r.Column(col)
   122  			for i := int32(0); i < vec.N; i++ {
   123  				if i != int32(vec.Rank(int(i))) {
   124  					t.Fatalf("expected rank %d, but found %d", i, vec.Rank(int(i)))
   125  				}
   126  			}
   127  
   128  			var got interface{}
   129  			switch schema[col] {
   130  			case ColumnTypeBool:
   131  				got = r.Column(col).Bool()
   132  			case ColumnTypeInt8:
   133  				got = r.Column(col).Int8()
   134  			case ColumnTypeInt16:
   135  				got = r.Column(col).Int16()
   136  				if v := uintptr(unsafe.Pointer(&(got.([]int16)[0]))); v%2 != 0 {
   137  					t.Fatalf("expected 2-byte alignment, but found %x\n", v)
   138  				}
   139  			case ColumnTypeInt32:
   140  				got = r.Column(col).Int32()
   141  				if v := uintptr(unsafe.Pointer(&(got.([]int32)[0]))); v%4 != 0 {
   142  					t.Fatalf("expected 2-byte alignment, but found %x\n", v)
   143  				}
   144  			case ColumnTypeInt64:
   145  				got = r.Column(col).Int64()
   146  				if v := uintptr(unsafe.Pointer(&(got.([]int64)[0]))); v%8 != 0 {
   147  					t.Fatalf("expected 2-byte alignment, but found %x\n", v)
   148  				}
   149  			case ColumnTypeFloat32:
   150  				got = r.Column(col).Float32()
   151  				if v := uintptr(unsafe.Pointer(&(got.([]float32)[0]))); v%4 != 0 {
   152  					t.Fatalf("expected 2-byte alignment, but found %x\n", v)
   153  				}
   154  			case ColumnTypeFloat64:
   155  				got = r.Column(col).Float64()
   156  				if v := uintptr(unsafe.Pointer(&(got.([]float64)[0]))); v%8 != 0 {
   157  					t.Fatalf("expected 2-byte alignment, but found %x\n", v)
   158  				}
   159  			case ColumnTypeBytes:
   160  				vals := r.Column(col).Bytes()
   161  				vals2 := make([][]byte, r.rows)
   162  				for i := range vals2 {
   163  					vals2[i] = vals.At(i)
   164  				}
   165  				got = vals2
   166  			}
   167  			if !reflect.DeepEqual(data[col], got) {
   168  				t.Fatalf("expected\n%+v\ngot\n%+v\n% x", data[col], got, r.data())
   169  			}
   170  		}
   171  	})
   172  }
   173  
   174  func TestBlockWriter(t *testing.T) {
   175  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   176  	randInt := func(lo, hi int) int {
   177  		return lo + rng.Intn(hi-lo)
   178  	}
   179  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeBool})
   180  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeInt8})
   181  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeInt16})
   182  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeInt32})
   183  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeInt64})
   184  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeFloat32})
   185  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeFloat64})
   186  	testSchema(t, rng, randInt(1, 100), []ColumnType{ColumnTypeBytes})
   187  
   188  	for i := 0; i < 100; i++ {
   189  		schema := make([]ColumnType, 2+rng.Intn(8))
   190  		for j := range schema {
   191  			schema[j] = ColumnType(1 + rng.Intn(int(ColumnTypeBytes)))
   192  		}
   193  		testSchema(t, rng, randInt(1, 100), schema)
   194  	}
   195  }
   196  
   197  func TestBlockWriterNullValues(t *testing.T) {
   198  	var w blockWriter
   199  	w.init([]ColumnType{ColumnTypeInt8})
   200  	for i := 0; i <= 16; i++ {
   201  		if i%2 == 0 {
   202  			w.PutNull(0)
   203  		} else {
   204  			w.PutInt8(0, int8(i))
   205  		}
   206  	}
   207  	r := NewBlock(w.Finish())
   208  	col := r.Column(0)
   209  	for i := 0; i < int(col.N); i++ {
   210  		if j := col.Rank(i); j < 0 {
   211  			if i%2 != 0 {
   212  				t.Fatalf("expected non-NULL value, but found NULL")
   213  			}
   214  		} else if i%2 == 0 {
   215  			t.Fatalf("expected NULL value, but found %d", col.Int8()[j])
   216  		}
   217  	}
   218  }
   219  
   220  func BenchmarkBlock(b *testing.B) {
   221  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   222  	blocks := make([][]byte, 128)
   223  	for i := range blocks {
   224  		blocks[i], _ = randBlock(rng, 4096, []ColumnType{ColumnTypeInt64})
   225  	}
   226  
   227  	b.Run("not-null", func(b *testing.B) {
   228  		var sum int64
   229  		for i, k := 0, 0; i < b.N; i += k {
   230  			r := NewBlock(blocks[rng.Intn(len(blocks))])
   231  			col := r.Column(0)
   232  			vals := col.Int64()
   233  
   234  			k = int(col.N)
   235  			if k > b.N-i {
   236  				k = b.N - i
   237  			}
   238  			for j := 0; j < k; j++ {
   239  				sum += vals[j]
   240  			}
   241  		}
   242  		if testing.Verbose() {
   243  			fmt.Println(sum)
   244  		}
   245  	})
   246  
   247  	b.Run("null-get", func(b *testing.B) {
   248  		var sum int64
   249  		for i, k := 0, 0; i < b.N; i += k {
   250  			r := NewBlock(blocks[rng.Intn(len(blocks))])
   251  			col := r.Column(0)
   252  			vals := col.Int64()
   253  
   254  			k = int(col.N)
   255  			if k > b.N-i {
   256  				k = b.N - i
   257  			}
   258  			for j := 0; j < k; j++ {
   259  				if !col.Null(j) {
   260  					sum += vals[j]
   261  				}
   262  			}
   263  		}
   264  		if testing.Verbose() {
   265  			fmt.Println(sum)
   266  		}
   267  	})
   268  
   269  	b.Run("null-rank", func(b *testing.B) {
   270  		var sum int64
   271  		for i, k := 0, 0; i < b.N; i += k {
   272  			r := NewBlock(blocks[rng.Intn(len(blocks))])
   273  			col := r.Column(0)
   274  			vals := col.Int64()
   275  
   276  			k = int(col.N)
   277  			if k > b.N-i {
   278  				k = b.N - i
   279  			}
   280  			for j := 0; j < k; j++ {
   281  				if r := col.Rank(j); r >= 0 {
   282  					sum += vals[r]
   283  				}
   284  			}
   285  		}
   286  		if testing.Verbose() {
   287  			fmt.Println(sum)
   288  		}
   289  	})
   290  }