Skip to content

Commit

Permalink
executor: add vectorized access methods for time.Duration stored in…
Browse files Browse the repository at this point in the history
… `Column`(pingcap#11677)
  • Loading branch information
qw4990 authored Aug 8, 2019
1 parent 554594b commit 6098ed6
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 5 deletions.
24 changes: 19 additions & 5 deletions util/chunk/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,12 @@ func (c *Column) AppendEnum(enum types.Enum) {
}

const (
sizeInt64 = int(unsafe.Sizeof(int64(0)))
sizeUint64 = int(unsafe.Sizeof(uint64(0)))
sizeFloat32 = int(unsafe.Sizeof(float32(0)))
sizeFloat64 = int(unsafe.Sizeof(float64(0)))
sizeMyDecimal = int(unsafe.Sizeof(types.MyDecimal{}))
sizeInt64 = int(unsafe.Sizeof(int64(0)))
sizeUint64 = int(unsafe.Sizeof(uint64(0)))
sizeFloat32 = int(unsafe.Sizeof(float32(0)))
sizeFloat64 = int(unsafe.Sizeof(float64(0)))
sizeMyDecimal = int(unsafe.Sizeof(types.MyDecimal{}))
sizeGoDuration = int(unsafe.Sizeof(time.Duration(0)))
)

// preAlloc allocates space for a fixed-length-type slice and resets all slots to null.
Expand Down Expand Up @@ -337,6 +338,11 @@ func (c *Column) PreAllocDecimal(length int) {
c.preAlloc(length, sizeMyDecimal)
}

// PreAllocDuration allocates space for a duration slice and resets all slots to null.
func (c *Column) PreAllocDuration(length int) {
c.preAlloc(length, sizeGoDuration)
}

func (c *Column) castSliceHeader(header *reflect.SliceHeader, typeSize int) {
header.Data = uintptr(unsafe.Pointer(&c.data[0]))
header.Len = c.length
Expand Down Expand Up @@ -371,6 +377,14 @@ func (c *Column) Float64s() []float64 {
return res
}

// GoDurations returns a Golang time.Duration slice stored in this Column.
// Different from the Row.GetDuration method, the argument Fsp is ignored, so the user should handle it outside.
func (c *Column) GoDurations() []time.Duration {
var res []time.Duration
c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeGoDuration)
return res
}

// Decimals returns a MyDecimal slice stored in this Column.
func (c *Column) Decimals() []types.MyDecimal {
var res []types.MyDecimal
Expand Down
79 changes: 79 additions & 0 deletions util/chunk/column_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package chunk
import (
"fmt"
"math/rand"
"testing"
"time"

"github.com/pingcap/check"
Expand Down Expand Up @@ -257,6 +258,30 @@ func (s *testChunkSuite) TestF32Column(c *check.C) {
}
}

func (s *testChunkSuite) TestDurationSliceColumn(c *check.C) {
chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024)
col := chk.Column(0)
for i := 0; i < 1024; i++ {
col.AppendDuration(types.Duration{Duration: time.Duration(i)})
}

ds := col.GoDurations()
for i := 0; i < 1024; i++ {
c.Assert(ds[i], check.Equals, time.Duration(i))
d := types.Duration{Duration: ds[i]}
d, _ = d.Add(d)
ds[i] = d.Duration
}

it := NewIterator4Chunk(chk)
var i int64
for row := it.Begin(); row != it.End(); row = it.Next() {
c.Assert(row.GetDuration(0, 0).Duration, check.Equals, time.Duration(i)*2)
c.Assert(col.GetDuration(int(i), 0).Duration, check.Equals, time.Duration(i)*2)
i++
}
}

func (s *testChunkSuite) TestMyDecimal(c *check.C) {
chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeNewDecimal)}, 1024)
col := chk.Column(0)
Expand Down Expand Up @@ -681,3 +706,57 @@ func (s *testChunkSuite) TestSetNulls(c *check.C) {
}
}
}

func BenchmarkDurationRow(b *testing.B) {
chk1 := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024)
col1 := chk1.Column(0)
for i := 0; i < 1024; i++ {
col1.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)})
}
chk2 := chk1.CopyConstruct()
result := chk1.CopyConstruct()

b.ResetTimer()
for k := 0; k < b.N; k++ {
result.Reset()
it1 := NewIterator4Chunk(chk1)
it2 := NewIterator4Chunk(chk2)
for r1, r2 := it1.Begin(), it2.Begin(); r1 != it1.End() && r2 != it2.End(); r1, r2 = it1.Next(), it2.Next() {
d1 := r1.GetDuration(0, 0)
d2 := r2.GetDuration(0, 0)
r, err := d1.Add(d2)
if err != nil {
b.Fatal(err)
}
result.AppendDuration(0, r)
}
}
}

func BenchmarkDurationVec(b *testing.B) {
chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024)
col1 := chk.Column(0)
for i := 0; i < 1024; i++ {
col1.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)})
}
col2 := col1.CopyConstruct(nil)
result := col1.CopyConstruct(nil)

ds1 := col1.GoDurations()
ds2 := col2.GoDurations()
rs := result.GoDurations()

b.ResetTimer()
for k := 0; k < b.N; k++ {
result.PreAllocDuration(1024)
for i := 0; i < 1024; i++ {
d1 := types.Duration{Duration: ds1[i]}
d2 := types.Duration{Duration: ds2[i]}
r, err := d1.Add(d2)
if err != nil {
b.Fatal(err)
}
rs[i] = r.Duration
}
}
}

0 comments on commit 6098ed6

Please sign in to comment.