From 81b5da2d0f42a7315e417fe1143866a7cd43075a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net> Date: Wed, 15 Jul 2015 20:34:18 -0500 Subject: [PATCH] SQLite: Adding benchmarks. --- sqlite/benchmark_test.go | 665 +++++++++++++++++++++++++++++++++++++++ sqlite/database_test.go | 210 ------------- 2 files changed, 665 insertions(+), 210 deletions(-) create mode 100644 sqlite/benchmark_test.go diff --git a/sqlite/benchmark_test.go b/sqlite/benchmark_test.go new file mode 100644 index 00000000..27447312 --- /dev/null +++ b/sqlite/benchmark_test.go @@ -0,0 +1,665 @@ +package sqlite + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/jmoiron/sqlx" + "upper.io/db" +) + +const ( + testRows = 1000 +) + +func updatedArtistN(i int) string { + return fmt.Sprintf("Updated Artist %d", i%testRows) +} + +func artistN(i int) string { + return fmt.Sprintf("Artist %d", i%testRows) +} + +func connectAndAddFakeRows() (db.Database, error) { + var err error + var sess db.Database + + if sess, err = db.Open(Adapter, settings); err != nil { + return nil, err + } + + driver := sess.Driver().(*sqlx.DB) + + if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { + return nil, err + } + + for i := 0; i < testRows; i++ { + if _, err = driver.Exec(`INSERT INTO "artist" ("name") VALUES(?)`, artistN(i)); err != nil { + return nil, err + } + } + + return sess, nil +} + +// BenchmarkSQLAppend benchmarks raw INSERT SQL queries without using prepared +// statements nor arguments. +func BenchmarkSQLAppend(b *testing.B) { + var err error + var sess db.Database + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = driver.Exec(`INSERT INTO "artist" ("name") VALUES('Hayao Miyazaki')`); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLAppendWithArgs benchmarks raw SQL queries with arguments but +// without using prepared statements. The SQL query looks like the one that is +// generated by upper.io/db. +func BenchmarkSQLAppendWithArgs(b *testing.B) { + var err error + var sess db.Database + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { + b.Fatal(err) + } + + args := []interface{}{ + "Hayao Miyazaki", + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = driver.Exec(`INSERT INTO "artist" ("name") VALUES(?)`, args...); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLPreparedAppend benchmarks raw INSERT SQL queries using prepared +// statements but no arguments. +func BenchmarkSQLPreparedAppend(b *testing.B) { + var err error + var sess db.Database + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { + b.Fatal(err) + } + + stmt, err := driver.Prepare(`INSERT INTO "artist" ("name") VALUES('Hayao Miyazaki')`) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = stmt.Exec(); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLAppendWithArgs benchmarks raw INSERT SQL queries with arguments +// using prepared statements. The SQL query looks like the one that is +// generated by upper.io/db. +func BenchmarkSQLPreparedAppendWithArgs(b *testing.B) { + var err error + var sess db.Database + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { + b.Fatal(err) + } + + stmt, err := driver.Prepare(`INSERT INTO "artist" ("name") VALUES(?)`) + + if err != nil { + b.Fatal(err) + } + + args := []interface{}{ + "Hayao Miyazaki", + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = stmt.Exec(args...); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLAppendWithVariableArgs benchmarks raw INSERT SQL queries with +// arguments using prepared statements. The SQL query looks like the one that +// is generated by upper.io/db. +func BenchmarkSQLPreparedAppendWithVariableArgs(b *testing.B) { + var err error + var sess db.Database + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { + b.Fatal(err) + } + + stmt, err := driver.Prepare(`INSERT INTO "artist" ("name") VALUES(?)`) + + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + args := []interface{}{ + fmt.Sprintf("Hayao Miyazaki %d", rand.Int()), + } + if _, err = stmt.Exec(args...); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLPreparedAppendTransactionWithArgs benchmarks raw INSERT queries +// within a transaction block with arguments and prepared statements. SQL +// queries look like those generated by upper.io/db. +func BenchmarkSQLPreparedAppendTransactionWithArgs(b *testing.B) { + var err error + var sess db.Database + var tx *sqlx.Tx + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + if tx, err = driver.Beginx(); err != nil { + b.Fatal(err) + } + + if _, err = tx.Exec(`DELETE FROM "artist"`); err != nil { + b.Fatal(err) + } + + stmt, err := tx.Preparex(`INSERT INTO "artist" ("name") VALUES(?)`) + if err != nil { + b.Fatal(err) + } + + args := []interface{}{ + "Hayao Miyazaki", + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = stmt.Exec(args...); err != nil { + b.Fatal(err) + } + } + + if err = tx.Commit(); err != nil { + b.Fatal(err) + } +} + +// BenchmarkUpperAppend benchmarks an insertion by upper.io/db. +func BenchmarkUpperAppend(b *testing.B) { + + sess, err := db.Open(Adapter, settings) + if err != nil { + b.Fatal(err) + } + + defer sess.Close() + + artist, err := sess.Collection("artist") + if err != nil { + b.Fatal(err) + } + + artist.Truncate() + + item := struct { + Name string `db:"name"` + }{"Hayao Miyazaki"} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = artist.Append(item); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkUpperAppendVariableArgs benchmarks an insertion by upper.io/db +// with variable parameters. +func BenchmarkUpperAppendVariableArgs(b *testing.B) { + + sess, err := db.Open(Adapter, settings) + if err != nil { + b.Fatal(err) + } + + defer sess.Close() + + artist, err := sess.Collection("artist") + if err != nil { + b.Fatal(err) + } + + artist.Truncate() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + item := struct { + Name string `db:"name"` + }{fmt.Sprintf("Hayao Miyazaki %d", rand.Int())} + if _, err = artist.Append(item); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkUpperAppendTransaction benchmarks insertion queries by upper.io/db +// within a transaction operation. +func BenchmarkUpperAppendTransaction(b *testing.B) { + var sess db.Database + var err error + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + var tx db.Tx + if tx, err = sess.Transaction(); err != nil { + b.Fatal(err) + } + + var artist db.Collection + if artist, err = tx.Collection("artist"); err != nil { + b.Fatal(err) + } + + if err = artist.Truncate(); err != nil { + b.Fatal(err) + } + + item := struct { + Name string `db:"name"` + }{"Hayao Miyazaki"} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = artist.Append(item); err != nil { + b.Fatal(err) + } + } + + if err = tx.Commit(); err != nil { + b.Fatal(err) + } +} + +// BenchmarkUpperAppendTransactionWithMap benchmarks insertion queries by +// upper.io/db within a transaction operation using a map instead of a struct. +func BenchmarkUpperAppendTransactionWithMap(b *testing.B) { + var sess db.Database + var err error + + if sess, err = db.Open(Adapter, settings); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + var tx db.Tx + if tx, err = sess.Transaction(); err != nil { + b.Fatal(err) + } + + var artist db.Collection + if artist, err = tx.Collection("artist"); err != nil { + b.Fatal(err) + } + + if err = artist.Truncate(); err != nil { + b.Fatal(err) + } + + item := map[string]string{ + "name": "Hayao Miyazaki", + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = artist.Append(item); err != nil { + b.Fatal(err) + } + } + + if err = tx.Commit(); err != nil { + b.Fatal(err) + } +} + +// BenchmarkSQLSelect benchmarks SQL SELECT queries. +func BenchmarkSQLSelect(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + var res *sqlx.Rows + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if res, err = driver.Queryx(`SELECT * FROM "artist" WHERE "name" = ?`, artistN(i)); err != nil { + b.Fatal(err) + } + res.Close() + } +} + +// BenchmarkSQLPreparedSelect benchmarks SQL select queries using prepared +// statements. +func BenchmarkSQLPreparedSelect(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + stmt, err := driver.Preparex(`SELECT * FROM "artist" WHERE "name" = ?`) + if err != nil { + b.Fatal(err) + } + + var res *sqlx.Rows + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if res, err = stmt.Queryx(artistN(i)); err != nil { + b.Fatal(err) + } + res.Close() + } +} + +// BenchmarkUpperFind benchmarks upper.io/db's One method. +func BenchmarkUpperFind(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + artist, err := sess.Collection("artist") + if err != nil { + b.Fatal(err) + } + + type artistType struct { + Name string `db:"name"` + } + + var item artistType + + b.ResetTimer() + for i := 0; i < b.N; i++ { + res := artist.Find(db.Cond{"name": artistN(i)}) + if err = res.One(&item); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkUpperFindAll benchmarks upper.io/db's All method. +func BenchmarkUpperFindAll(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + artist, err := sess.Collection("artist") + if err != nil { + b.Fatal(err) + } + + type artistType struct { + Name string `db:"name"` + } + + var items []artistType + + b.ResetTimer() + for i := 0; i < b.N; i++ { + res := artist.Find(db.Or{ + db.Cond{"name": artistN(i)}, + db.Cond{"name": artistN(i + 1)}, + db.Cond{"name": artistN(i + 2)}, + }) + if err = res.All(&items); err != nil { + b.Fatal(err) + } + if len(items) != 3 { + b.Fatal("Expecting 3 results.") + } + } +} + +// BenchmarkSQLUpdate benchmarks SQL UPDATE queries. +func BenchmarkSQLUpdate(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = driver.Exec(`UPDATE "artist" SET "name" = ? WHERE "name" = ?`, updatedArtistN(i), artistN(i)); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLPreparedUpdate benchmarks SQL UPDATE queries. +func BenchmarkSQLPreparedUpdate(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + stmt, err := driver.Prepare(`UPDATE "artist" SET "name" = ? WHERE "name" = ?`) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = stmt.Exec(updatedArtistN(i), artistN(i)); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkUpperUpdate benchmarks upper.io/db's Update method. +func BenchmarkUpperUpdate(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + artist, err := sess.Collection("artist") + if err != nil { + b.Fatal(err) + } + + type artistType struct { + Name string `db:"name"` + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newValue := artistType{ + Name: updatedArtistN(i), + } + res := artist.Find(db.Cond{"name": artistN(i)}) + if err = res.Update(newValue); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLDelete benchmarks SQL DELETE queries. +func BenchmarkSQLDelete(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = driver.Exec(`DELETE FROM "artist" WHERE "name" = ?`, artistN(i)); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkSQLPreparedDelete benchmarks SQL DELETE queries. +func BenchmarkSQLPreparedDelete(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + defer sess.Close() + + driver := sess.Driver().(*sqlx.DB) + + stmt, err := driver.Prepare(`DELETE FROM "artist" WHERE "name" = ?`) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err = stmt.Exec(artistN(i)); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkUpperRemove benchmarks +func BenchmarkUpperRemove(b *testing.B) { + var err error + var sess db.Database + + if sess, err = connectAndAddFakeRows(); err != nil { + b.Fatal(err) + } + + defer sess.Close() + + artist, err := sess.Collection("artist") + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + res := artist.Find(db.Cond{"name": artistN(i)}) + if err = res.Remove(); err != nil { + b.Fatal(err) + } + } +} diff --git a/sqlite/database_test.go b/sqlite/database_test.go index bae89a2e..4edf13cb 100644 --- a/sqlite/database_test.go +++ b/sqlite/database_test.go @@ -1369,213 +1369,3 @@ func TestDataTypes(t *testing.T) { t.Fatalf("Struct is different.") } } - -// Benchmarking raw database/sql. -func BenchmarkAppendRawSQL(b *testing.B) { - var err error - var sess db.Database - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - driver := sess.Driver().(*sqlx.DB) - - if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { - b.Fatal(err) - } - - stmt, err := driver.Prepare(`INSERT INTO "artist" ("name") VALUES('Hayao Miyazaki')`) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err = stmt.Exec(); err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkAppendRawSQLWithArgs(b *testing.B) { - var err error - var sess db.Database - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - driver := sess.Driver().(*sqlx.DB) - - if _, err = driver.Exec(`DELETE FROM "artist"`); err != nil { - b.Fatal(err) - } - - stmt, err := driver.Prepare(`INSERT INTO "artist" ("name") VALUES(?) RETURNING "id"`) - if err != nil { - b.Fatal(err) - } - - args := []interface{}{ - "Hayao Miyazaki", - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err = stmt.Query(args...); err != nil { - b.Fatal(err) - } - } -} - -// Benchmarking Append(). -// -// Contributed by wei2912 -// See: https://github.com/gosexy/db/issues/20#issuecomment-20097801 -func BenchmarkAppendUpper(b *testing.B) { - sess, err := db.Open(Adapter, settings) - - if err != nil { - b.Fatal(err) - } - - defer sess.Close() - - artist, err := sess.Collection("artist") - artist.Truncate() - - item := struct { - Name string `db:"name"` - }{"Hayao Miyazaki"} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err = artist.Append(item); err != nil { - b.Fatal(err) - } - } -} - -// Benchmarking raw database/sql. -func BenchmarkAppendTxRawSQL(b *testing.B) { - var err error - var sess db.Database - var tx *sql.Tx - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - driver := sess.Driver().(*sqlx.DB) - - if tx, err = driver.Begin(); err != nil { - b.Fatal(err) - } - - if _, err = tx.Exec(`DELETE FROM "artist"`); err != nil { - b.Fatal(err) - } - - stmt, err := tx.Prepare(`INSERT INTO "artist" ("name") VALUES('Hayao Miyazaki')`) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err = stmt.Exec(); err != nil { - b.Fatal(err) - } - } - - if err = tx.Commit(); err != nil { - b.Fatal(err) - } -} - -// Benchmarking Append() with transactions. -func BenchmarkAppendTxUpper(b *testing.B) { - var sess db.Database - var err error - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - var tx db.Tx - if tx, err = sess.Transaction(); err != nil { - b.Fatal(err) - } - - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } - - if err = artist.Truncate(); err != nil { - b.Fatal(err) - } - - item := struct { - Name string `db:"name"` - }{"Hayao Miyazaki"} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err = artist.Append(item); err != nil { - b.Fatal(err) - } - } - - if err = tx.Commit(); err != nil { - b.Fatal(err) - } -} - -// Benchmarking Append() with map. -func BenchmarkAppendTxUpperMap(b *testing.B) { - var sess db.Database - var err error - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - var tx db.Tx - if tx, err = sess.Transaction(); err != nil { - b.Fatal(err) - } - - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } - - if err = artist.Truncate(); err != nil { - b.Fatal(err) - } - - item := map[string]string{"name": "Hayao Miyazaki"} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err = artist.Append(item); err != nil { - b.Fatal(err) - } - } - - if err = tx.Commit(); err != nil { - b.Fatal(err) - } -} -- GitLab