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 }