github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/col/coldata/vec_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package coldata_test 12 13 import ( 14 "fmt" 15 "testing" 16 17 "github.com/cockroachdb/cockroach/pkg/col/coldata" 18 "github.com/cockroachdb/cockroach/pkg/col/coldatatestutils" 19 "github.com/cockroachdb/cockroach/pkg/sql/types" 20 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 21 "github.com/cockroachdb/cockroach/pkg/util/randutil" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func TestMemColumnWindow(t *testing.T) { 26 defer leaktest.AfterTest(t)() 27 28 rng, _ := randutil.NewPseudoRand() 29 30 c := coldata.NewMemColumn(types.Int, coldata.BatchSize(), coldata.StandardColumnFactory) 31 32 ints := c.Int64() 33 for i := 0; i < coldata.BatchSize(); i++ { 34 ints[i] = int64(i) 35 if i%2 == 0 { 36 // Set every other value to null. 37 c.Nulls().SetNull(i) 38 } 39 } 40 41 startWindow := 1 42 endWindow := 0 43 for startWindow > endWindow { 44 startWindow = rng.Intn(coldata.BatchSize()) 45 endWindow = 1 + rng.Intn(coldata.BatchSize()) 46 } 47 48 window := c.Window(startWindow, endWindow) 49 windowInts := window.Int64() 50 // Verify that every other value is null. 51 for i, j := startWindow, 0; i < endWindow; i, j = i+1, j+1 { 52 if i%2 == 0 { 53 if !window.Nulls().NullAt(j) { 54 t.Fatalf("expected null at %d (original index: %d)", j, i) 55 } 56 continue 57 } 58 if ints[i] != windowInts[j] { 59 t.Fatalf("unexected value at index %d (original index: %d): expected %d got %d", j, i, ints[i], windowInts[j]) 60 } 61 } 62 } 63 64 func TestNullRanges(t *testing.T) { 65 tcs := []struct { 66 start int 67 end int 68 }{ 69 { 70 start: 1, 71 end: 1, 72 }, 73 { 74 start: 50, 75 end: 0, 76 }, 77 { 78 start: 0, 79 end: 50, 80 }, 81 { 82 start: 0, 83 end: 64, 84 }, 85 { 86 start: 25, 87 end: 50, 88 }, 89 { 90 start: 0, 91 end: 80, 92 }, 93 { 94 start: 20, 95 end: 80, 96 }, 97 { 98 start: 0, 99 end: 387, 100 }, 101 { 102 start: 385, 103 end: 387, 104 }, 105 { 106 start: 0, 107 end: 1023, 108 }, 109 { 110 start: 1022, 111 end: 1023, 112 }, { 113 start: 1023, 114 end: 1023, 115 }, 116 } 117 118 c := coldata.NewMemColumn(types.Int, coldata.BatchSize(), coldata.StandardColumnFactory) 119 for _, tc := range tcs { 120 c.Nulls().UnsetNulls() 121 c.Nulls().SetNullRange(tc.start, tc.end) 122 123 for i := 0; i < coldata.BatchSize(); i++ { 124 if i >= tc.start && i < tc.end { 125 if !c.Nulls().NullAt(i) { 126 t.Fatalf("expected null at %d, start: %d end: %d", i, tc.start, tc.end) 127 } 128 } else { 129 if c.Nulls().NullAt(i) { 130 t.Fatalf("expected non-null at %d, start: %d end: %d", i, tc.start, tc.end) 131 } 132 } 133 } 134 } 135 } 136 137 func TestAppend(t *testing.T) { 138 // TODO(asubiotto): Test nulls. 139 var typ = types.Int 140 141 src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 142 sel := make([]int, len(src.Int64())) 143 for i := range sel { 144 sel[i] = i 145 } 146 147 testCases := []struct { 148 name string 149 args coldata.SliceArgs 150 expectedLength int 151 }{ 152 { 153 name: "AppendSimple", 154 args: coldata.SliceArgs{ 155 // DestIdx must be specified to append to the end of dest. 156 DestIdx: coldata.BatchSize(), 157 }, 158 expectedLength: coldata.BatchSize() * 2, 159 }, 160 { 161 name: "AppendOverwriteSimple", 162 args: coldata.SliceArgs{ 163 // DestIdx 0, the default value, will start appending at index 0. 164 DestIdx: 0, 165 }, 166 expectedLength: coldata.BatchSize(), 167 }, 168 { 169 name: "AppendOverwriteSlice", 170 args: coldata.SliceArgs{ 171 // Start appending at index 10. 172 DestIdx: 10, 173 }, 174 expectedLength: coldata.BatchSize() + 10, 175 }, 176 { 177 name: "AppendSlice", 178 args: coldata.SliceArgs{ 179 DestIdx: 20, 180 SrcStartIdx: 10, 181 SrcEndIdx: 20, 182 }, 183 expectedLength: 30, 184 }, 185 { 186 name: "AppendWithSel", 187 args: coldata.SliceArgs{ 188 DestIdx: 5, 189 SrcStartIdx: 10, 190 SrcEndIdx: 20, 191 Sel: sel, 192 }, 193 expectedLength: 15, 194 }, 195 { 196 name: "AppendWithHalfSel", 197 args: coldata.SliceArgs{ 198 DestIdx: 5, 199 Sel: sel[:len(sel)/2], 200 SrcEndIdx: len(sel) / 2, 201 }, 202 expectedLength: 5 + (coldata.BatchSize())/2, 203 }, 204 } 205 206 for _, tc := range testCases { 207 tc.args.Src = src 208 if tc.args.SrcEndIdx == 0 { 209 // SrcEndIdx is always required. 210 tc.args.SrcEndIdx = coldata.BatchSize() 211 } 212 t.Run(tc.name, func(t *testing.T) { 213 dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 214 dest.Append(tc.args) 215 require.Equal(t, tc.expectedLength, len(dest.Int64())) 216 }) 217 } 218 } 219 220 func TestCopy(t *testing.T) { 221 // TODO(asubiotto): Test nulls. 222 var typ = types.Int 223 224 src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 225 srcInts := src.Int64() 226 for i := range srcInts { 227 srcInts[i] = int64(i + 1) 228 } 229 sel := make([]int, len(src.Int64())) 230 for i := range sel { 231 sel[i] = i 232 } 233 234 sum := func(ints []int64) int { 235 s := 0 236 for _, i := range ints { 237 s += int(i) 238 } 239 return s 240 } 241 242 testCases := []struct { 243 name string 244 args coldata.CopySliceArgs 245 expectedSum int 246 }{ 247 { 248 name: "CopyNothing", 249 args: coldata.CopySliceArgs{}, 250 expectedSum: 0, 251 }, 252 { 253 name: "CopyBatchSizeMinus1WithOffset1", 254 args: coldata.CopySliceArgs{ 255 SliceArgs: coldata.SliceArgs{ 256 // Use DestIdx 1 to make sure that it is respected. 257 DestIdx: 1, 258 SrcEndIdx: coldata.BatchSize() - 1, 259 }, 260 }, 261 // expectedSum uses sum of positive integers formula. 262 expectedSum: (coldata.BatchSize() - 1) * coldata.BatchSize() / 2, 263 }, 264 { 265 name: "CopyWithSel", 266 args: coldata.CopySliceArgs{ 267 SliceArgs: coldata.SliceArgs{ 268 Sel: sel[1:], 269 DestIdx: 25, 270 SrcStartIdx: 1, 271 SrcEndIdx: 2, 272 }, 273 }, 274 // We'll have just the third element in the resulting slice. 275 expectedSum: 3, 276 }, 277 } 278 279 for _, tc := range testCases { 280 tc.args.Src = src 281 t.Run(tc.name, func(t *testing.T) { 282 dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 283 dest.Copy(tc.args) 284 destInts := dest.Int64() 285 firstNonZero := 0 286 for i := range destInts { 287 if destInts[i] != 0 { 288 firstNonZero = i 289 break 290 } 291 } 292 // Verify that Copy started copying where we expected it to. 293 require.Equal(t, tc.args.DestIdx, firstNonZero) 294 require.Equal(t, tc.expectedSum, sum(destInts)) 295 }) 296 } 297 } 298 299 func TestCopyNulls(t *testing.T) { 300 var typ = types.Int 301 302 // Set up the destination vector. 303 dst := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 304 dstInts := dst.Int64() 305 for i := range dstInts { 306 dstInts[i] = int64(1) 307 } 308 // Set some nulls in the destination vector. 309 for i := 0; i < 5; i++ { 310 dst.Nulls().SetNull(i) 311 } 312 313 // Set up the source vector. 314 src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 315 srcInts := src.Int64() 316 for i := range srcInts { 317 srcInts[i] = 2 318 } 319 // Set some nulls in the source. 320 for i := 3; i < 8; i++ { 321 src.Nulls().SetNull(i) 322 } 323 324 copyArgs := coldata.CopySliceArgs{ 325 SliceArgs: coldata.SliceArgs{ 326 Src: src, 327 DestIdx: 3, 328 SrcStartIdx: 3, 329 SrcEndIdx: 10, 330 }, 331 } 332 333 dst.Copy(copyArgs) 334 335 // Verify that original nulls aren't deleted, and that 336 // the nulls in the source have been copied over. 337 for i := 0; i < 8; i++ { 338 require.True(t, dst.Nulls().NullAt(i), "expected null at %d, found not null", i) 339 } 340 341 // Verify that the data from src has been copied over. 342 for i := 8; i < 10; i++ { 343 require.True(t, dstInts[i] == 2, "data from src was not copied over") 344 require.True(t, !dst.Nulls().NullAt(i), "no extra nulls were added") 345 } 346 347 // Verify that the remaining elements in dst have not been touched. 348 for i := 10; i < coldata.BatchSize(); i++ { 349 require.True(t, dstInts[i] == 1, "data in dst outside copy range has been changed") 350 require.True(t, !dst.Nulls().NullAt(i), "no extra nulls were added") 351 } 352 } 353 354 func TestCopySelOnDestDoesNotUnsetOldNulls(t *testing.T) { 355 var typ = types.Int 356 357 // Set up the destination vector. It is all nulls except for a single 358 // non-null at index 0. 359 dst := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 360 dstInts := dst.Int64() 361 for i := range dstInts { 362 dstInts[i] = 1 363 } 364 dst.Nulls().SetNulls() 365 dst.Nulls().UnsetNull(0) 366 367 // Set up the source vector with two nulls. 368 src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 369 srcInts := src.Int64() 370 for i := range srcInts { 371 srcInts[i] = 2 372 } 373 src.Nulls().SetNull(0) 374 src.Nulls().SetNull(3) 375 376 // Using a small selection vector and SelOnDest, perform a copy and verify 377 // that nulls in between the selected tuples weren't unset. 378 copyArgs := coldata.CopySliceArgs{ 379 SelOnDest: true, 380 SliceArgs: coldata.SliceArgs{ 381 Src: src, 382 SrcStartIdx: 1, 383 SrcEndIdx: 3, 384 Sel: []int{0, 1, 3}, 385 }, 386 } 387 388 dst.Copy(copyArgs) 389 390 // 0 was not null in dest and null in source, but it wasn't selected. Not null. 391 require.False(t, dst.Nulls().NullAt(0)) 392 // 1 was null in dest and not null in source: it becomes not null. 393 require.False(t, dst.Nulls().NullAt(1)) 394 // 2 wasn't included in the selection vector: it stays null. 395 require.True(t, dst.Nulls().NullAt(2)) 396 // 3 was null in dest and null in source: it stays null. 397 require.True(t, dst.Nulls().NullAt(3)) 398 // 4 wasn't included: it stays null. 399 require.True(t, dst.Nulls().NullAt(4)) 400 } 401 402 func BenchmarkAppend(b *testing.B) { 403 rng, _ := randutil.NewPseudoRand() 404 sel := rng.Perm(coldata.BatchSize()) 405 406 benchCases := []struct { 407 name string 408 args coldata.SliceArgs 409 }{ 410 { 411 name: "AppendSimple", 412 args: coldata.SliceArgs{}, 413 }, 414 { 415 name: "AppendWithSel", 416 args: coldata.SliceArgs{ 417 Sel: sel, 418 }, 419 }, 420 } 421 422 for _, typ := range []*types.T{types.Bytes, types.Decimal, types.Int} { 423 for _, nullProbability := range []float64{0, 0.2} { 424 src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 425 coldatatestutils.RandomVec(coldatatestutils.RandomVecArgs{ 426 Rand: rng, 427 Vec: src, 428 N: coldata.BatchSize(), 429 NullProbability: nullProbability, 430 BytesFixedLength: 8, 431 }) 432 for _, bc := range benchCases { 433 bc.args.Src = src 434 bc.args.SrcEndIdx = coldata.BatchSize() 435 dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 436 b.Run(fmt.Sprintf("%s/%s/NullProbability=%.1f", typ, bc.name, nullProbability), func(b *testing.B) { 437 b.SetBytes(8 * int64(coldata.BatchSize())) 438 bc.args.DestIdx = 0 439 for i := 0; i < b.N; i++ { 440 dest.Append(bc.args) 441 bc.args.DestIdx += coldata.BatchSize() 442 } 443 }) 444 } 445 } 446 } 447 } 448 449 func BenchmarkCopy(b *testing.B) { 450 rng, _ := randutil.NewPseudoRand() 451 sel := rng.Perm(coldata.BatchSize()) 452 453 benchCases := []struct { 454 name string 455 args coldata.CopySliceArgs 456 }{ 457 { 458 name: "CopySimple", 459 args: coldata.CopySliceArgs{}, 460 }, 461 { 462 name: "CopyWithSel", 463 args: coldata.CopySliceArgs{ 464 SliceArgs: coldata.SliceArgs{ 465 Sel: sel, 466 }, 467 }, 468 }, 469 } 470 471 for _, typ := range []*types.T{types.Bytes, types.Decimal, types.Int} { 472 for _, nullProbability := range []float64{0, 0.2} { 473 src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 474 coldatatestutils.RandomVec(coldatatestutils.RandomVecArgs{ 475 Rand: rng, 476 Vec: src, 477 N: coldata.BatchSize(), 478 NullProbability: nullProbability, 479 BytesFixedLength: 8, 480 }) 481 for _, bc := range benchCases { 482 bc.args.Src = src 483 bc.args.SrcEndIdx = coldata.BatchSize() 484 dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) 485 b.Run(fmt.Sprintf("%s/%s/NullProbability=%.1f", typ, bc.name, nullProbability), func(b *testing.B) { 486 b.SetBytes(8 * int64(coldata.BatchSize())) 487 for i := 0; i < b.N; i++ { 488 dest.Copy(bc.args) 489 if typ.Identical(types.Bytes) { 490 // We need to reset flat bytes so that we could copy into it 491 // (otherwise it'll panic on the second copy due to maxSetIndex 492 // being not zero). 493 dest.Bytes().Reset() 494 } 495 } 496 }) 497 } 498 } 499 } 500 }