github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/database/mysql/benchmark_test.go (about)

     1  // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
     2  //
     3  // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
     4  //
     5  // This Source Code Form is subject to the terms of the Mozilla Public
     6  // License, v. 2.0. If a copy of the MPL was not distributed with this file,
     7  // You can obtain one at http://mozilla.org/MPL/2.0/.
     8  
     9  package mysql
    10  
    11  import (
    12  	"bytes"
    13  	"database/sql"
    14  	"strings"
    15  	"sync"
    16  	"sync/atomic"
    17  	"testing"
    18  )
    19  
    20  type TB testing.B
    21  
    22  func (tb *TB) check(err error) {
    23  	if err != nil {
    24  		tb.Fatal(err)
    25  	}
    26  }
    27  
    28  func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB {
    29  	tb.check(err)
    30  	return db
    31  }
    32  
    33  func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows {
    34  	tb.check(err)
    35  	return rows
    36  }
    37  
    38  func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt {
    39  	tb.check(err)
    40  	return stmt
    41  }
    42  
    43  func initDB(b *testing.B, queries ...string) *sql.DB {
    44  	tb := (*TB)(b)
    45  	db := tb.checkDB(sql.Open("mysql", dsn))
    46  	for _, query := range queries {
    47  		if _, err := db.Exec(query); err != nil {
    48  			b.Fatalf("Error on %q: %v", query, err)
    49  		}
    50  	}
    51  	return db
    52  }
    53  
    54  const concurrencyLevel = 10
    55  
    56  func BenchmarkQuery(b *testing.B) {
    57  	tb := (*TB)(b)
    58  	b.StopTimer()
    59  	b.ReportAllocs()
    60  	db := initDB(b,
    61  		"DROP TABLE IF EXISTS foo",
    62  		"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
    63  		`INSERT INTO foo VALUES (1, "one")`,
    64  		`INSERT INTO foo VALUES (2, "two")`,
    65  	)
    66  	db.SetMaxIdleConns(concurrencyLevel)
    67  	defer db.Close()
    68  
    69  	stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?"))
    70  	defer stmt.Close()
    71  
    72  	remain := int64(b.N)
    73  	var wg sync.WaitGroup
    74  	wg.Add(concurrencyLevel)
    75  	defer wg.Wait()
    76  	b.StartTimer()
    77  
    78  	for i := 0; i < concurrencyLevel; i++ {
    79  		go func() {
    80  			for {
    81  				if atomic.AddInt64(&remain, -1) < 0 {
    82  					wg.Done()
    83  					return
    84  				}
    85  
    86  				var got string
    87  				tb.check(stmt.QueryRow(1).Scan(&got))
    88  				if got != "one" {
    89  					b.Errorf("query = %q; want one", got)
    90  					wg.Done()
    91  					return
    92  				}
    93  			}
    94  		}()
    95  	}
    96  }
    97  
    98  func BenchmarkExec(b *testing.B) {
    99  	tb := (*TB)(b)
   100  	b.StopTimer()
   101  	b.ReportAllocs()
   102  	db := tb.checkDB(sql.Open("mysql", dsn))
   103  	db.SetMaxIdleConns(concurrencyLevel)
   104  	defer db.Close()
   105  
   106  	stmt := tb.checkStmt(db.Prepare("DO 1"))
   107  	defer stmt.Close()
   108  
   109  	remain := int64(b.N)
   110  	var wg sync.WaitGroup
   111  	wg.Add(concurrencyLevel)
   112  	defer wg.Wait()
   113  	b.StartTimer()
   114  
   115  	for i := 0; i < concurrencyLevel; i++ {
   116  		go func() {
   117  			for {
   118  				if atomic.AddInt64(&remain, -1) < 0 {
   119  					wg.Done()
   120  					return
   121  				}
   122  
   123  				if _, err := stmt.Exec(); err != nil {
   124  					b.Fatal(err.Error())
   125  				}
   126  			}
   127  		}()
   128  	}
   129  }
   130  
   131  // data, but no db writes
   132  var roundtripSample []byte
   133  
   134  func initRoundtripBenchmarks() ([]byte, int, int) {
   135  	if roundtripSample == nil {
   136  		roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024))
   137  	}
   138  	return roundtripSample, 16, len(roundtripSample)
   139  }
   140  
   141  func BenchmarkRoundtripTxt(b *testing.B) {
   142  	b.StopTimer()
   143  	sample, min, max := initRoundtripBenchmarks()
   144  	sampleString := string(sample)
   145  	b.ReportAllocs()
   146  	tb := (*TB)(b)
   147  	db := tb.checkDB(sql.Open("mysql", dsn))
   148  	defer db.Close()
   149  	b.StartTimer()
   150  	var result string
   151  	for i := 0; i < b.N; i++ {
   152  		length := min + i
   153  		if length > max {
   154  			length = max
   155  		}
   156  		test := sampleString[0:length]
   157  		rows := tb.checkRows(db.Query(`SELECT "` + test + `"`))
   158  		if !rows.Next() {
   159  			rows.Close()
   160  			b.Fatalf("crashed")
   161  		}
   162  		err := rows.Scan(&result)
   163  		if err != nil {
   164  			rows.Close()
   165  			b.Fatalf("crashed")
   166  		}
   167  		if result != test {
   168  			rows.Close()
   169  			b.Errorf("mismatch")
   170  		}
   171  		rows.Close()
   172  	}
   173  }
   174  
   175  func BenchmarkRoundtripBin(b *testing.B) {
   176  	b.StopTimer()
   177  	sample, min, max := initRoundtripBenchmarks()
   178  	b.ReportAllocs()
   179  	tb := (*TB)(b)
   180  	db := tb.checkDB(sql.Open("mysql", dsn))
   181  	defer db.Close()
   182  	stmt := tb.checkStmt(db.Prepare("SELECT ?"))
   183  	defer stmt.Close()
   184  	b.StartTimer()
   185  	var result sql.RawBytes
   186  	for i := 0; i < b.N; i++ {
   187  		length := min + i
   188  		if length > max {
   189  			length = max
   190  		}
   191  		test := sample[0:length]
   192  		rows := tb.checkRows(stmt.Query(test))
   193  		if !rows.Next() {
   194  			rows.Close()
   195  			b.Fatalf("crashed")
   196  		}
   197  		err := rows.Scan(&result)
   198  		if err != nil {
   199  			rows.Close()
   200  			b.Fatalf("crashed")
   201  		}
   202  		if !bytes.Equal(result, test) {
   203  			rows.Close()
   204  			b.Errorf("mismatch")
   205  		}
   206  		rows.Close()
   207  	}
   208  }