Add bench tests for knn

This commit is contained in:
Mike Poindexter 2020-04-08 11:36:37 -07:00
parent 2a4272c95f
commit fe6e3863ba
3 changed files with 121 additions and 9 deletions

View File

@ -2,6 +2,7 @@ package tests
import ( import (
"fmt" "fmt"
"math/rand"
"sort" "sort"
"testing" "testing"
) )
@ -429,3 +430,19 @@ func match(expectIn string) func(org, v interface{}) (resp, expect interface{})
return fmt.Sprintf("%v", org), expectIn return fmt.Sprintf("%v", org), expectIn
} }
} }
func subBenchSearch(b *testing.B, mc *mockServer) {
runBenchStep(b, mc, "KNN", keys_KNN_bench)
}
func keys_KNN_bench(mc *mockServer) error {
lat := rand.Float64()*180 - 90
lon := rand.Float64()*360 - 180
_, err := mc.conn.Do("NEARBY",
"mykey",
"LIMIT", 50,
"DISTANCE",
"POINTS",
"POINT", lat, lon)
return err
}

View File

@ -20,15 +20,19 @@ import (
var errTimeout = errors.New("timeout") var errTimeout = errors.New("timeout")
func mockCleanup() { func mockCleanup(silent bool) {
if !silent {
fmt.Printf("Cleanup: may take some time... ") fmt.Printf("Cleanup: may take some time... ")
}
files, _ := ioutil.ReadDir(".") files, _ := ioutil.ReadDir(".")
for _, file := range files { for _, file := range files {
if strings.HasPrefix(file.Name(), "data-mock-") { if strings.HasPrefix(file.Name(), "data-mock-") {
os.RemoveAll(file.Name()) os.RemoveAll(file.Name())
} }
} }
if !silent {
fmt.Printf("OK\n") fmt.Printf("OK\n")
}
} }
type mockServer struct { type mockServer struct {
@ -39,11 +43,13 @@ type mockServer struct {
conn redis.Conn conn redis.Conn
} }
func mockOpenServer() (*mockServer, error) { func mockOpenServer(silent bool) (*mockServer, error) {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
port := rand.Int()%20000 + 20000 port := rand.Int()%20000 + 20000
dir := fmt.Sprintf("data-mock-%d", port) dir := fmt.Sprintf("data-mock-%d", port)
if !silent {
fmt.Printf("Starting test server at port %d\n", port) fmt.Printf("Starting test server at port %d\n", port)
}
logOutput := ioutil.Discard logOutput := ioutil.Discard
if os.Getenv("PRINTLOG") == "1" { if os.Getenv("PRINTLOG") == "1" {
logOutput = os.Stderr logOutput = os.Stderr

View File

@ -2,10 +2,14 @@ package tests
import ( import (
"fmt" "fmt"
"math/rand"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"testing" "testing"
"time"
"github.com/gomodule/redigo/redis"
) )
const ( const (
@ -23,18 +27,18 @@ const (
) )
func TestAll(t *testing.T) { func TestAll(t *testing.T) {
mockCleanup() mockCleanup(false)
defer mockCleanup() defer mockCleanup(false)
ch := make(chan os.Signal) ch := make(chan os.Signal)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM) signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
go func() { go func() {
<-ch <-ch
mockCleanup() mockCleanup(false)
os.Exit(1) os.Exit(1)
}() }()
mc, err := mockOpenServer() mc, err := mockOpenServer(false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -83,3 +87,88 @@ func runStep(t *testing.T, mc *mockServer, name string, step func(mc *mockServer
fmt.Printf("["+green+"ok"+clear+"]: %s\n", name) fmt.Printf("["+green+"ok"+clear+"]: %s\n", name)
}) })
} }
func BenchmarkAll(b *testing.B) {
mockCleanup(true)
defer mockCleanup(true)
ch := make(chan os.Signal)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
go func() {
<-ch
mockCleanup(true)
os.Exit(1)
}()
mc, err := mockOpenServer(true)
if err != nil {
b.Fatal(err)
}
defer mc.Close()
runSubBenchmark(b, "search", mc, subBenchSearch)
}
func loadBenchmarkPoints(b *testing.B, mc *mockServer) (err error) {
const nPoints = 200000
rand.Seed(time.Now().UnixNano())
// add a bunch of points
for i := 0; i < nPoints; i++ {
val := fmt.Sprintf("val:%d", i)
var resp string
var lat, lon, fval float64
fval = rand.Float64()
lat = rand.Float64()*180 - 90
lon = rand.Float64()*360 - 180
resp, err = redis.String(mc.conn.Do("SET",
"mykey", val,
"FIELD", "foo", fval,
"POINT", lat, lon))
if err != nil {
return
}
if resp != "OK" {
err = fmt.Errorf("expected 'OK', got '%s'", resp)
return
}
}
return
}
func runSubBenchmark(b *testing.B, name string, mc *mockServer, bench func(t *testing.B, mc *mockServer)) {
b.Run(name, func(b *testing.B) {
bench(b, mc)
})
}
func runBenchStep(b *testing.B, mc *mockServer, name string, step func(mc *mockServer) error) {
b.Helper()
b.Run(name, func(b *testing.B) {
b.Helper()
if err := func() error {
// reset the current server
mc.ResetConn()
defer mc.ResetConn()
// clear the database so the test is consistent
if err := mc.DoBatch([][]interface{}{
{"OUTPUT", "resp"}, {"OK"},
{"FLUSHDB"}, {"OK"},
}); err != nil {
return err
}
err := loadBenchmarkPoints(b, mc)
if err != nil {
return err
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err := step(mc); err != nil {
return err
}
}
return nil
}(); err != nil {
b.Fatal(err)
}
})
}