mirror of
https://github.com/glebarez/go-sqlite.git
synced 2025-10-05 07:46:50 +08:00
benchmarks: readability and bugfixes
This commit is contained in:
@@ -12,7 +12,7 @@ Additional command line arguments:
|
||||
|
||||
| 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 |
|
||||
|
||||
|
||||
|
@@ -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
|
||||
|
||||
/* 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 (
|
||||
"database/sql"
|
||||
@@ -10,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
// 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
|
||||
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
|
||||
func bench_insert_in_transaction(b *testing.B, db *sql.DB) {
|
||||
func benchInsertInTransaction(b *testing.B, db *sql.DB) {
|
||||
// create test table
|
||||
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
|
||||
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
|
||||
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
|
||||
func bench_select_without_index(b *testing.B, db *sql.DB) {
|
||||
func benchSelectWithoutIndex(b *testing.B, db *sql.DB) {
|
||||
// create test table
|
||||
createTestTable(db)
|
||||
|
||||
@@ -66,13 +71,15 @@ func bench_select_without_index(b *testing.B, db *sql.DB) {
|
||||
// ...
|
||||
for i := 0; i < b.N; i++ {
|
||||
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
|
||||
func bench_select_on_string_comparison(b *testing.B, db *sql.DB) {
|
||||
func benchSelectOnStringComparison(b *testing.B, db *sql.DB) {
|
||||
// create test table
|
||||
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
|
||||
func bench_create_index(b *testing.B, db *sql.DB) {
|
||||
func benchCreateIndex(b *testing.B, db *sql.DB) {
|
||||
// create test table
|
||||
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
|
||||
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
|
||||
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;
|
||||
for i := 0; i < b.N; i++ {
|
||||
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
|
||||
func bench_update_without_index(b *testing.B, db *sql.DB) {
|
||||
func benchUpdateWithoutIndex(b *testing.B, db *sql.DB) {
|
||||
// create test table
|
||||
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;
|
||||
for i := 0; i < b.N; i++ {
|
||||
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
|
||||
func bench_update_with_index(b *testing.B, db *sql.DB) {
|
||||
func benchUpdateWithIndex(b *testing.B, db *sql.DB) {
|
||||
// create test table
|
||||
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;
|
||||
// ...
|
||||
for i := 0; i < b.N; i++ {
|
||||
stmt.Exec(
|
||||
if _, err := stmt.Exec(
|
||||
rand.Uint32(), // b = ?
|
||||
i%testTableRowCount+1, // WHERE a=?
|
||||
)
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
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;
|
||||
for i := 0; i < b.N; i++ {
|
||||
// generate new random number-as-words for c
|
||||
stmt.Exec(
|
||||
if _, err := stmt.Exec(
|
||||
pronounceNum(uint32(rand.Int31n(maxGeneratedNum))), // SET c=?
|
||||
i%testTableRowCount+1, // WHERE a=?
|
||||
)
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
createTestTable(db)
|
||||
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
|
||||
func bench_delete_without_index(b *testing.B, db *sql.DB) {
|
||||
func benchDeleteWithoutIndex(b *testing.B, db *sql.DB) {
|
||||
// create test table
|
||||
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
|
||||
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
|
||||
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
|
||||
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++ {
|
||||
b.StopTimer()
|
||||
createTestTable(db)
|
||||
|
@@ -27,22 +27,22 @@ var (
|
||||
|
||||
// benchmark funcs to execute
|
||||
funcs = []func(*testing.B, *sql.DB){
|
||||
bench_create_index,
|
||||
bench_select_on_string_comparison,
|
||||
bench_select_with_index,
|
||||
bench_select_without_index,
|
||||
bench_insert,
|
||||
bench_insert_in_transaction,
|
||||
bench_insert_into_indexed,
|
||||
bench_insert_from_select,
|
||||
bench_update_text_with_index,
|
||||
bench_update_with_index,
|
||||
bench_update_without_index,
|
||||
bench_delete_without_index,
|
||||
bench_delete_with_index,
|
||||
benchCreateIndex,
|
||||
benchSelectOnStringComparison,
|
||||
benchSelectWithIndex,
|
||||
benchSelectWithoutIndex,
|
||||
benchInsert,
|
||||
benchInsertInTransaction,
|
||||
benchInsertIntoIndexed,
|
||||
benchInsertFromSelect,
|
||||
benchUpdateTextWithIndex,
|
||||
benchUpdateWithIndex,
|
||||
benchUpdateWithoutIndex,
|
||||
benchDeleteWithoutIndex,
|
||||
benchDeleteWithIndex,
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
func Test_BenchmarkSQLite(t *testing.T) {
|
||||
func TestBenchmarkSQLite(t *testing.T) {
|
||||
// print info about CPU and OS
|
||||
fmt.Println()
|
||||
fmt.Printf("goos: %s\n", runtime.GOOS)
|
||||
@@ -69,8 +69,8 @@ func Test_BenchmarkSQLite(t *testing.T) {
|
||||
for _, f := range funcs {
|
||||
|
||||
var (
|
||||
nsPerOpBase avgVal
|
||||
nsPerOp avgVal
|
||||
nsPerOpCGo avgVal
|
||||
nsPerOpPureGo avgVal
|
||||
)
|
||||
|
||||
// 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) })
|
||||
|
||||
// contribue metric to average
|
||||
nsPerOpBase.contribInt(br.NsPerOp())
|
||||
nsPerOpCGo.contribInt(br.NsPerOp())
|
||||
|
||||
// close DB
|
||||
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) })
|
||||
|
||||
// contribue metric to average
|
||||
nsPerOp.contribInt(br.NsPerOp())
|
||||
nsPerOpPureGo.contribInt(br.NsPerOp())
|
||||
// close DB
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -101,10 +101,10 @@ func Test_BenchmarkSQLite(t *testing.T) {
|
||||
|
||||
// print result row
|
||||
fmt.Printf("%-35s | %5.2fx | CGo: %7.3f ms/op | Pure-Go: %7.3f ms/op\n",
|
||||
getFuncName(f),
|
||||
nsPerOp.val/nsPerOpBase.val,
|
||||
nsPerOpBase.val/1e6,
|
||||
nsPerOp.val/1e6,
|
||||
toSnakeCase(getFuncName(f)),
|
||||
nsPerOpPureGo.val/nsPerOpCGo.val, // factor
|
||||
nsPerOpCGo.val/1e6, // ms/op
|
||||
nsPerOpPureGo.val/1e6, // ms/op
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"math/rand"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -26,6 +27,17 @@ const (
|
||||
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
|
||||
func mustExec(db *sql.DB, statements ...string) {
|
||||
for _, s := range statements {
|
||||
@@ -133,14 +145,16 @@ func createDB(tb testing.TB, inMemory bool, driverName string) *sql.DB {
|
||||
}
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
// if !inMemory {
|
||||
// // disable sync
|
||||
// _, err = db.Exec(`PRAGMA synchronous = OFF`)
|
||||
// if err != nil {
|
||||
// tb.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// when in on-disk mode - set synchronous = OFF
|
||||
// this turns off fsync() sys call at every record inserted out of transaction scope
|
||||
// thus we don't bother HDD/SSD too often during specific bechmarks
|
||||
if !inMemory {
|
||||
// disable sync
|
||||
_, err = db.Exec(`PRAGMA synchronous = OFF`)
|
||||
if err != nil {
|
||||
tb.Fatal(err)
|
||||
}
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -193,11 +207,6 @@ func pronounceNum(n uint32) string {
|
||||
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
|
||||
type avgVal struct {
|
||||
// the current average value
|
||||
@@ -208,7 +217,9 @@ type avgVal struct {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@@ -9,15 +9,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func test_pronounceNum(t *testing.T) {
|
||||
// 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) {
|
||||
func BenchmarkPronounceNum(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
n := rand.Int31()
|
||||
pronounceNum(uint32(n))
|
||||
|
Reference in New Issue
Block a user