|
|
@@ -0,0 +1,173 @@
|
|
|
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
|
+//
|
|
|
+// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
|
+//
|
|
|
+// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
+// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
+
|
|
|
+package mysql
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "database/sql"
|
|
|
+ "strings"
|
|
|
+ "sync"
|
|
|
+ "sync/atomic"
|
|
|
+ "testing"
|
|
|
+)
|
|
|
+
|
|
|
+type TB testing.B
|
|
|
+
|
|
|
+func (tb *TB) check(err error) {
|
|
|
+ if err != nil {
|
|
|
+ tb.Fatal(err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB {
|
|
|
+ tb.check(err)
|
|
|
+ return db
|
|
|
+}
|
|
|
+
|
|
|
+func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows {
|
|
|
+ tb.check(err)
|
|
|
+ return rows
|
|
|
+}
|
|
|
+
|
|
|
+func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt {
|
|
|
+ tb.check(err)
|
|
|
+ return stmt
|
|
|
+}
|
|
|
+
|
|
|
+func initDB(b *testing.B, queries ...string) *sql.DB {
|
|
|
+ tb := (*TB)(b)
|
|
|
+ db := tb.checkDB(sql.Open("mysql", dsn))
|
|
|
+ for _, query := range queries {
|
|
|
+ if _, err := db.Exec(query); err != nil {
|
|
|
+ b.Fatalf("Error on %q: %v", query, err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return db
|
|
|
+}
|
|
|
+
|
|
|
+// by Brad Fitzpatrick
|
|
|
+const concurrencyLevel = 10
|
|
|
+
|
|
|
+func BenchmarkQuery(b *testing.B) {
|
|
|
+ tb := (*TB)(b)
|
|
|
+ b.StopTimer()
|
|
|
+ b.ReportAllocs()
|
|
|
+ db := initDB(b,
|
|
|
+ "DROP TABLE IF EXISTS foo",
|
|
|
+ "CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
|
|
+ `INSERT INTO foo VALUES (1, "one")`,
|
|
|
+ `INSERT INTO foo VALUES (2, "two")`,
|
|
|
+ )
|
|
|
+ db.SetMaxIdleConns(concurrencyLevel)
|
|
|
+ defer db.Close()
|
|
|
+
|
|
|
+ stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?"))
|
|
|
+ defer stmt.Close()
|
|
|
+ b.StartTimer()
|
|
|
+
|
|
|
+ remain := int64(b.N)
|
|
|
+ var wg sync.WaitGroup
|
|
|
+ wg.Add(concurrencyLevel)
|
|
|
+ defer wg.Wait()
|
|
|
+ for i := 0; i < concurrencyLevel; i++ {
|
|
|
+ go func() {
|
|
|
+ defer wg.Done()
|
|
|
+ for {
|
|
|
+ if atomic.AddInt64(&remain, -1) < 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var got string
|
|
|
+ tb.check(stmt.QueryRow(1).Scan(&got))
|
|
|
+ if got != "one" {
|
|
|
+ b.Errorf("query = %q; want one", got)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// data, but no db writes
|
|
|
+var roundtripSample []byte
|
|
|
+
|
|
|
+func initRoundtripBenchmarks() ([]byte, int, int) {
|
|
|
+ if roundtripSample == nil {
|
|
|
+ roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024))
|
|
|
+ }
|
|
|
+ return roundtripSample, 16, len(roundtripSample)
|
|
|
+}
|
|
|
+
|
|
|
+func BenchmarkRoundtripTxt(b *testing.B) {
|
|
|
+ b.StopTimer()
|
|
|
+ sample, min, max := initRoundtripBenchmarks()
|
|
|
+ sampleString := string(sample)
|
|
|
+ b.ReportAllocs()
|
|
|
+ tb := (*TB)(b)
|
|
|
+ db := tb.checkDB(sql.Open("mysql", dsn))
|
|
|
+ defer db.Close()
|
|
|
+ b.StartTimer()
|
|
|
+ var result string
|
|
|
+ for i := 0; i < b.N; i++ {
|
|
|
+ length := min + i
|
|
|
+ if length > max {
|
|
|
+ length = max
|
|
|
+ }
|
|
|
+ test := sampleString[0:length]
|
|
|
+ rows := tb.checkRows(db.Query(`SELECT "` + test + `"`))
|
|
|
+ if !rows.Next() {
|
|
|
+ rows.Close()
|
|
|
+ b.Fatalf("crashed")
|
|
|
+ }
|
|
|
+ err := rows.Scan(&result)
|
|
|
+ if err != nil {
|
|
|
+ rows.Close()
|
|
|
+ b.Fatalf("crashed")
|
|
|
+ }
|
|
|
+ if result != test {
|
|
|
+ rows.Close()
|
|
|
+ b.Errorf("mismatch")
|
|
|
+ }
|
|
|
+ rows.Close()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func BenchmarkRoundtripBin(b *testing.B) {
|
|
|
+ b.StopTimer()
|
|
|
+ sample, min, max := initRoundtripBenchmarks()
|
|
|
+ b.ReportAllocs()
|
|
|
+ tb := (*TB)(b)
|
|
|
+ db := tb.checkDB(sql.Open("mysql", dsn))
|
|
|
+ defer db.Close()
|
|
|
+ stmt := tb.checkStmt(db.Prepare("SELECT ?"))
|
|
|
+ defer stmt.Close()
|
|
|
+ b.StartTimer()
|
|
|
+ var result sql.RawBytes
|
|
|
+ for i := 0; i < b.N; i++ {
|
|
|
+ length := min + i
|
|
|
+ if length > max {
|
|
|
+ length = max
|
|
|
+ }
|
|
|
+ test := sample[0:length]
|
|
|
+ rows := tb.checkRows(stmt.Query(test))
|
|
|
+ if !rows.Next() {
|
|
|
+ rows.Close()
|
|
|
+ b.Fatalf("crashed")
|
|
|
+ }
|
|
|
+ err := rows.Scan(&result)
|
|
|
+ if err != nil {
|
|
|
+ rows.Close()
|
|
|
+ b.Fatalf("crashed")
|
|
|
+ }
|
|
|
+ if !bytes.Equal(result, test) {
|
|
|
+ rows.Close()
|
|
|
+ b.Errorf("mismatch")
|
|
|
+ }
|
|
|
+ rows.Close()
|
|
|
+ }
|
|
|
+}
|