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 |
|
| 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 |
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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))
|
||||||
|
Reference in New Issue
Block a user