diff --git a/util/chunk/column.go b/util/chunk/column.go index 476e4bb332593..a1bb3e7bfabd7 100644 --- a/util/chunk/column.go +++ b/util/chunk/column.go @@ -628,3 +628,17 @@ func (c *Column) CopyReconstruct(sel []int, dst *Column) *Column { } return dst } + +// MergeNulls merges these columns' null bitmaps. +// For a row, if any column of it is null, the result is null. +// It works like: if col1.IsNull || col2.IsNull || col3.IsNull. +// The user should ensure that all these columns have the same length, and +// data stored in these columns are fixed-length type. +func (c *Column) MergeNulls(cols ...*Column) { + for _, col := range cols { + for i := range c.nullBitmap { + // bit 0 is null, 1 is not null, so do AND operations here. + c.nullBitmap[i] &= col.nullBitmap[i] + } + } +} diff --git a/util/chunk/column_test.go b/util/chunk/column_test.go index 75795555a5a62..867b5fe4e26a7 100644 --- a/util/chunk/column_test.go +++ b/util/chunk/column_test.go @@ -885,3 +885,53 @@ func BenchmarkTimeVec(b *testing.B) { } } } + +func genNullCols(n int) []*Column { + cols := make([]*Column, n) + for i := range cols { + cols[i] = NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) + cols[i].ResizeInt64(1024, false) + for j := 0; j < 1024; j++ { + if rand.Intn(10) < 5 { + cols[i].SetNull(j, true) + } + } + } + return cols +} + +func (s *testChunkSuite) TestVectorizedNulls(c *check.C) { + for i := 0; i < 256; i++ { + cols := genNullCols(4) + lCol, rCol := cols[0], cols[1] + vecResult, rowResult := cols[2], cols[3] + vecResult.SetNulls(0, 1024, false) + rowResult.SetNulls(0, 1024, false) + vecResult.MergeNulls(lCol, rCol) + for i := 0; i < 1024; i++ { + rowResult.SetNull(i, lCol.IsNull(i) || rCol.IsNull(i)) + } + + for i := 0; i < 1024; i++ { + c.Assert(rowResult.IsNull(i), check.Equals, vecResult.IsNull(i)) + } + } +} + +func BenchmarkMergeNullsVectorized(b *testing.B) { + cols := genNullCols(3) + b.ResetTimer() + for i := 0; i < b.N; i++ { + cols[0].MergeNulls(cols[1:]...) + } +} + +func BenchmarkMergeNullsNonVectorized(b *testing.B) { + cols := genNullCols(3) + b.ResetTimer() + for i := 0; i < b.N; i++ { + for i := 0; i < 1024; i++ { + cols[0].SetNull(i, cols[1].IsNull(i) || cols[2].IsNull(i)) + } + } +}