benchmarks: readability and bugfixes

This commit is contained in:
glebarez
2022-01-11 15:17:31 +01:00
parent 884a3ed642
commit f6f0331b65
5 changed files with 88 additions and 69 deletions

View File

@@ -12,7 +12,7 @@ Additional command line arguments:
| flag | type | default | description | | flag | type | default | description |
| ---- | ---- | ------- | ----------------------------------------------------------------------------------------------- | | ---- | ---- | ------- | ----------------------------------------------------------------------------------------------- |
| -mem | bool | false | if set, use in-memory SQLite | | -mem | bool | false | if set - benchmarks will use in-memory SQLite instance, otherwise: on-disk instance |
| -rep | uint | 1 | run each benchmark multiple times and average the results. this may provide more stable results | | -rep | uint | 1 | run each benchmark multiple times and average the results. this may provide more stable results |

View File

@@ -1,6 +1,11 @@
// Copyright 2021 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package benchmark package benchmark
/* this file contains benchmarks inspired by https://www.sqlite.org/speed.html */ /*
this file contains benchmarks inspired by https://www.sqlite.org/speed.html
*/
import ( import (
"database/sql" "database/sql"
@@ -10,7 +15,7 @@ import (
) )
// corresponds to Test 1 from https://www.sqlite.org/speed.html // corresponds to Test 1 from https://www.sqlite.org/speed.html
func bench_insert(b *testing.B, db *sql.DB) { func benchInsert(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db) createTestTable(db)
@@ -21,7 +26,7 @@ func bench_insert(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 2 from https://www.sqlite.org/speed.html // corresponds to Test 2 from https://www.sqlite.org/speed.html
func bench_insert_in_transaction(b *testing.B, db *sql.DB) { func benchInsertInTransaction(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db) createTestTable(db)
@@ -32,7 +37,7 @@ func bench_insert_in_transaction(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 3 from https://www.sqlite.org/speed.html // corresponds to Test 3 from https://www.sqlite.org/speed.html
func bench_insert_into_indexed(b *testing.B, db *sql.DB) { func benchInsertIntoIndexed(b *testing.B, db *sql.DB) {
// create test table with indexed column // create test table with indexed column
createTestTable(db, `c`) createTestTable(db, `c`)
@@ -42,7 +47,7 @@ func bench_insert_into_indexed(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 4 from https://www.sqlite.org/speed.html // corresponds to Test 4 from https://www.sqlite.org/speed.html
func bench_select_without_index(b *testing.B, db *sql.DB) { func benchSelectWithoutIndex(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db) createTestTable(db)
@@ -66,13 +71,15 @@ func bench_select_without_index(b *testing.B, db *sql.DB) {
// ... // ...
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b := (i * 100) % maxGeneratedNum b := (i * 100) % maxGeneratedNum
stmt.Exec(b, b+1000) if _, err := stmt.Exec(b, b+1000); err != nil {
panic(err)
}
} }
}) })
} }
// corresponds to Test 5 from https://www.sqlite.org/speed.html // corresponds to Test 5 from https://www.sqlite.org/speed.html
func bench_select_on_string_comparison(b *testing.B, db *sql.DB) { func benchSelectOnStringComparison(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db) createTestTable(db)
@@ -105,7 +112,7 @@ func bench_select_on_string_comparison(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 6 from https://www.sqlite.org/speed.html // corresponds to Test 6 from https://www.sqlite.org/speed.html
func bench_create_index(b *testing.B, db *sql.DB) { func benchCreateIndex(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db) createTestTable(db)
@@ -136,7 +143,7 @@ func bench_create_index(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 7 from https://www.sqlite.org/speed.html // corresponds to Test 7 from https://www.sqlite.org/speed.html
func bench_select_with_index(b *testing.B, db *sql.DB) { func benchSelectWithIndex(b *testing.B, db *sql.DB) {
// create test table with indexed field // create test table with indexed field
createTestTable(db, `b`) createTestTable(db, `b`)
@@ -159,14 +166,17 @@ func bench_select_with_index(b *testing.B, db *sql.DB) {
// SELECT count(*), avg(b) FROM t2 WHERE b>=200 AND b<300; // SELECT count(*), avg(b) FROM t2 WHERE b>=200 AND b<300;
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b := (i * 100) % maxGeneratedNum b := (i * 100) % maxGeneratedNum
stmt.Exec(b, b+100) if _, err := stmt.Exec(b, b+100); err != nil {
panic(err)
}
} }
}) })
} }
// corresponds to Test 8 from https://www.sqlite.org/speed.html // corresponds to Test 8 from https://www.sqlite.org/speed.html
func bench_update_without_index(b *testing.B, db *sql.DB) { func benchUpdateWithoutIndex(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db) createTestTable(db)
@@ -189,14 +199,16 @@ func bench_update_without_index(b *testing.B, db *sql.DB) {
// UPDATE t1 SET b=b*2 WHERE a>=20 AND a<30; // UPDATE t1 SET b=b*2 WHERE a>=20 AND a<30;
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
a := (i * 10) % testTableRowCount a := (i * 10) % testTableRowCount
stmt.Exec(a, a+10) if _, err := stmt.Exec(a, a+10); err != nil {
panic(err)
}
} }
}) })
} }
// corresponds to Test 9 from https://www.sqlite.org/speed.html // corresponds to Test 9 from https://www.sqlite.org/speed.html
func bench_update_with_index(b *testing.B, db *sql.DB) { func benchUpdateWithIndex(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db, `a`) createTestTable(db, `a`)
@@ -218,16 +230,18 @@ func bench_update_with_index(b *testing.B, db *sql.DB) {
// UPDATE t2 SET b=121928 WHERE a=2; // UPDATE t2 SET b=121928 WHERE a=2;
// ... // ...
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
stmt.Exec( if _, err := stmt.Exec(
rand.Uint32(), // b = ? rand.Uint32(), // b = ?
i%testTableRowCount+1, // WHERE a=? i%testTableRowCount+1, // WHERE a=?
) ); err != nil {
panic(err)
}
} }
}) })
} }
// corresponds to Test 10 from https://www.sqlite.org/speed.html // corresponds to Test 10 from https://www.sqlite.org/speed.html
func bench_update_text_with_index(b *testing.B, db *sql.DB) { func benchUpdateTextWithIndex(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db, `a`) createTestTable(db, `a`)
@@ -249,16 +263,18 @@ func bench_update_text_with_index(b *testing.B, db *sql.DB) {
// UPDATE t2 SET c='three hundred sixty six thousand five hundred two' WHERE a=2; // UPDATE t2 SET c='three hundred sixty six thousand five hundred two' WHERE a=2;
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
// generate new random number-as-words for c // generate new random number-as-words for c
stmt.Exec( if _, err := stmt.Exec(
pronounceNum(uint32(rand.Int31n(maxGeneratedNum))), // SET c=? pronounceNum(uint32(rand.Int31n(maxGeneratedNum))), // SET c=?
i%testTableRowCount+1, // WHERE a=? i%testTableRowCount+1, // WHERE a=?
) ); err != nil {
panic(err)
}
} }
}) })
} }
// corresponds to Test 11 from https://www.sqlite.org/speed.html // corresponds to Test 11 from https://www.sqlite.org/speed.html
func bench_insert_from_select(b *testing.B, db *sql.DB) { func benchInsertFromSelect(b *testing.B, db *sql.DB) {
// create source table // create source table
createTestTable(db) createTestTable(db)
fillTestTableInTx(db, testTableRowCount) fillTestTableInTx(db, testTableRowCount)
@@ -292,7 +308,7 @@ func bench_insert_from_select(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 12 from https://www.sqlite.org/speed.html // corresponds to Test 12 from https://www.sqlite.org/speed.html
func bench_delete_without_index(b *testing.B, db *sql.DB) { func benchDeleteWithoutIndex(b *testing.B, db *sql.DB) {
// create test table // create test table
createTestTable(db) createTestTable(db)
@@ -319,7 +335,7 @@ func bench_delete_without_index(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 13 from https://www.sqlite.org/speed.html // corresponds to Test 13 from https://www.sqlite.org/speed.html
func bench_delete_with_index(b *testing.B, db *sql.DB) { func benchDeleteWithIndex(b *testing.B, db *sql.DB) {
// create test table with indexed column // create test table with indexed column
createTestTable(db, `a`) createTestTable(db, `a`)
@@ -346,7 +362,7 @@ func bench_delete_with_index(b *testing.B, db *sql.DB) {
} }
// corresponds to Test 16 from https://www.sqlite.org/speed.html // corresponds to Test 16 from https://www.sqlite.org/speed.html
func bench_drop_table(b *testing.B, db *sql.DB) { func benchDropTable(b *testing.B, db *sql.DB) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
createTestTable(db) createTestTable(db)

View File

@@ -27,22 +27,22 @@ var (
// benchmark funcs to execute // benchmark funcs to execute
funcs = []func(*testing.B, *sql.DB){ funcs = []func(*testing.B, *sql.DB){
bench_create_index, benchCreateIndex,
bench_select_on_string_comparison, benchSelectOnStringComparison,
bench_select_with_index, benchSelectWithIndex,
bench_select_without_index, benchSelectWithoutIndex,
bench_insert, benchInsert,
bench_insert_in_transaction, benchInsertInTransaction,
bench_insert_into_indexed, benchInsertIntoIndexed,
bench_insert_from_select, benchInsertFromSelect,
bench_update_text_with_index, benchUpdateTextWithIndex,
bench_update_with_index, benchUpdateWithIndex,
bench_update_without_index, benchUpdateWithoutIndex,
bench_delete_without_index, benchDeleteWithoutIndex,
bench_delete_with_index, benchDeleteWithIndex,
// due to very long run of this benchmark, it is disabled // due to very long run of this benchmark, it is disabled
// bench_drop_table, // benchDropTable,
} }
) )
@@ -53,7 +53,7 @@ func TestMain(m *testing.M) {
os.Exit(m.Run()) os.Exit(m.Run())
} }
func Test_BenchmarkSQLite(t *testing.T) { func TestBenchmarkSQLite(t *testing.T) {
// print info about CPU and OS // print info about CPU and OS
fmt.Println() fmt.Println()
fmt.Printf("goos: %s\n", runtime.GOOS) fmt.Printf("goos: %s\n", runtime.GOOS)
@@ -69,8 +69,8 @@ func Test_BenchmarkSQLite(t *testing.T) {
for _, f := range funcs { for _, f := range funcs {
var ( var (
nsPerOpBase avgVal nsPerOpCGo avgVal
nsPerOp avgVal nsPerOpPureGo avgVal
) )
// run benchmark against different drivers // run benchmark against different drivers
@@ -80,7 +80,7 @@ func Test_BenchmarkSQLite(t *testing.T) {
br := testing.Benchmark(func(b *testing.B) { f(b, db) }) br := testing.Benchmark(func(b *testing.B) { f(b, db) })
// contribue metric to average // contribue metric to average
nsPerOpBase.contribInt(br.NsPerOp()) nsPerOpCGo.contribInt(br.NsPerOp())
// close DB // close DB
if err := db.Close(); err != nil { if err := db.Close(); err != nil {
@@ -92,7 +92,7 @@ func Test_BenchmarkSQLite(t *testing.T) {
br = testing.Benchmark(func(b *testing.B) { f(b, db) }) br = testing.Benchmark(func(b *testing.B) { f(b, db) })
// contribue metric to average // contribue metric to average
nsPerOp.contribInt(br.NsPerOp()) nsPerOpPureGo.contribInt(br.NsPerOp())
// close DB // close DB
if err := db.Close(); err != nil { if err := db.Close(); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -101,10 +101,10 @@ func Test_BenchmarkSQLite(t *testing.T) {
// print result row // print result row
fmt.Printf("%-35s | %5.2fx | CGo: %7.3f ms/op | Pure-Go: %7.3f ms/op\n", fmt.Printf("%-35s | %5.2fx | CGo: %7.3f ms/op | Pure-Go: %7.3f ms/op\n",
getFuncName(f), toSnakeCase(getFuncName(f)),
nsPerOp.val/nsPerOpBase.val, nsPerOpPureGo.val/nsPerOpCGo.val, // factor
nsPerOpBase.val/1e6, nsPerOpCGo.val/1e6, // ms/op
nsPerOp.val/1e6, nsPerOpPureGo.val/1e6, // ms/op
) )
} }
} }

View File

@@ -10,6 +10,7 @@ import (
"math/rand" "math/rand"
"path" "path"
"reflect" "reflect"
"regexp"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
@@ -26,6 +27,17 @@ const (
testTableName = "t1" testTableName = "t1"
) )
var (
matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
)
func toSnakeCase(str string) string {
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
return strings.ToLower(snake)
}
// mustExec executes SQL statements and panic if error occurs // mustExec executes SQL statements and panic if error occurs
func mustExec(db *sql.DB, statements ...string) { func mustExec(db *sql.DB, statements ...string) {
for _, s := range statements { for _, s := range statements {
@@ -133,14 +145,16 @@ func createDB(tb testing.TB, inMemory bool, driverName string) *sql.DB {
} }
db.SetMaxOpenConns(1) db.SetMaxOpenConns(1)
// if !inMemory { // when in on-disk mode - set synchronous = OFF
// // disable sync // this turns off fsync() sys call at every record inserted out of transaction scope
// _, err = db.Exec(`PRAGMA synchronous = OFF`) // thus we don't bother HDD/SSD too often during specific bechmarks
// if err != nil { if !inMemory {
// tb.Fatal(err) // disable sync
// } _, err = db.Exec(`PRAGMA synchronous = OFF`)
// } if err != nil {
tb.Fatal(err)
}
}
return db return db
} }
@@ -193,11 +207,6 @@ func pronounceNum(n uint32) string {
panic("must have returned already") panic("must have returned already")
} }
// fracDiv does a fractional division of 2 integers
func fracDiv(x, y int64) float64 {
return float64(x) / float64(y)
}
// AvgVal provides average value with value contributions on-the-fly // AvgVal provides average value with value contributions on-the-fly
type avgVal struct { type avgVal struct {
// the current average value // the current average value
@@ -208,7 +217,9 @@ type avgVal struct {
} }
func (a *avgVal) contribFloat(v float64) { func (a *avgVal) contribFloat(v float64) {
a.val = (a.val*float64(a.numContributions) + v) / (float64(a.numContributions) + 1.) nContrib := float64(a.numContributions)
a.val = (a.val*nContrib + v) / (nContrib + 1.)
a.numContributions++
} }
func (a *avgVal) contribInt(v int64) { func (a *avgVal) contribInt(v int64) {

View File

@@ -9,15 +9,7 @@ import (
"testing" "testing"
) )
func test_pronounceNum(t *testing.T) { func BenchmarkPronounceNum(b *testing.B) {
// this is only for visual testing
for i := 0; i < 10; i++ {
n := rand.Int31()
t.Logf("%d: %s\n", n, pronounceNum(uint32(n)))
}
}
func Benchmark_pronounceNum(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
n := rand.Int31() n := rand.Int31()
pronounceNum(uint32(n)) pronounceNum(uint32(n))