...

Source file src/github.com/lib/pq/bench_test.go

Documentation: github.com/lib/pq

     1  package pq
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"database/sql"
     8  	"database/sql/driver"
     9  	"io"
    10  	"math/rand"
    11  	"net"
    12  	"runtime"
    13  	"strconv"
    14  	"strings"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/lib/pq/oid"
    20  )
    21  
    22  var (
    23  	selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'"
    24  	selectSeriesQuery = "SELECT generate_series(1, 100)"
    25  )
    26  
    27  func BenchmarkSelectString(b *testing.B) {
    28  	var result string
    29  	benchQuery(b, selectStringQuery, &result)
    30  }
    31  
    32  func BenchmarkSelectSeries(b *testing.B) {
    33  	var result int
    34  	benchQuery(b, selectSeriesQuery, &result)
    35  }
    36  
    37  func benchQuery(b *testing.B, query string, result interface{}) {
    38  	b.StopTimer()
    39  	db := openTestConn(b)
    40  	defer db.Close()
    41  	b.StartTimer()
    42  
    43  	for i := 0; i < b.N; i++ {
    44  		benchQueryLoop(b, db, query, result)
    45  	}
    46  }
    47  
    48  func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) {
    49  	rows, err := db.Query(query)
    50  	if err != nil {
    51  		b.Fatal(err)
    52  	}
    53  	defer rows.Close()
    54  	for rows.Next() {
    55  		err = rows.Scan(result)
    56  		if err != nil {
    57  			b.Fatal("failed to scan", err)
    58  		}
    59  	}
    60  }
    61  
    62  // reading from circularConn yields content[:prefixLen] once, followed by
    63  // content[prefixLen:] over and over again. It never returns EOF.
    64  type circularConn struct {
    65  	content   string
    66  	prefixLen int
    67  	pos       int
    68  	net.Conn  // for all other net.Conn methods that will never be called
    69  }
    70  
    71  func (r *circularConn) Read(b []byte) (n int, err error) {
    72  	n = copy(b, r.content[r.pos:])
    73  	r.pos += n
    74  	if r.pos >= len(r.content) {
    75  		r.pos = r.prefixLen
    76  	}
    77  	return
    78  }
    79  
    80  func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil }
    81  
    82  func (r *circularConn) Close() error { return nil }
    83  
    84  func fakeConn(content string, prefixLen int) *conn {
    85  	c := &circularConn{content: content, prefixLen: prefixLen}
    86  	return &conn{buf: bufio.NewReader(c), c: c}
    87  }
    88  
    89  // This benchmark is meant to be the same as BenchmarkSelectString, but takes
    90  // out some of the factors this package can't control. The numbers are less noisy,
    91  // but also the costs of network communication aren't accurately represented.
    92  func BenchmarkMockSelectString(b *testing.B) {
    93  	b.StopTimer()
    94  	// taken from a recorded run of BenchmarkSelectString
    95  	// See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html
    96  	const response = "1\x00\x00\x00\x04" +
    97  		"t\x00\x00\x00\x06\x00\x00" +
    98  		"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
    99  		"Z\x00\x00\x00\x05I" +
   100  		"2\x00\x00\x00\x04" +
   101  		"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
   102  		"C\x00\x00\x00\rSELECT 1\x00" +
   103  		"Z\x00\x00\x00\x05I" +
   104  		"3\x00\x00\x00\x04" +
   105  		"Z\x00\x00\x00\x05I"
   106  	c := fakeConn(response, 0)
   107  	b.StartTimer()
   108  
   109  	for i := 0; i < b.N; i++ {
   110  		benchMockQuery(b, c, selectStringQuery)
   111  	}
   112  }
   113  
   114  var seriesRowData = func() string {
   115  	var buf bytes.Buffer
   116  	for i := 1; i <= 100; i++ {
   117  		digits := byte(2)
   118  		if i >= 100 {
   119  			digits = 3
   120  		} else if i < 10 {
   121  			digits = 1
   122  		}
   123  		buf.WriteString("D\x00\x00\x00")
   124  		buf.WriteByte(10 + digits)
   125  		buf.WriteString("\x00\x01\x00\x00\x00")
   126  		buf.WriteByte(digits)
   127  		buf.WriteString(strconv.Itoa(i))
   128  	}
   129  	return buf.String()
   130  }()
   131  
   132  func BenchmarkMockSelectSeries(b *testing.B) {
   133  	b.StopTimer()
   134  	var response = "1\x00\x00\x00\x04" +
   135  		"t\x00\x00\x00\x06\x00\x00" +
   136  		"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
   137  		"Z\x00\x00\x00\x05I" +
   138  		"2\x00\x00\x00\x04" +
   139  		seriesRowData +
   140  		"C\x00\x00\x00\x0fSELECT 100\x00" +
   141  		"Z\x00\x00\x00\x05I" +
   142  		"3\x00\x00\x00\x04" +
   143  		"Z\x00\x00\x00\x05I"
   144  	c := fakeConn(response, 0)
   145  	b.StartTimer()
   146  
   147  	for i := 0; i < b.N; i++ {
   148  		benchMockQuery(b, c, selectSeriesQuery)
   149  	}
   150  }
   151  
   152  func benchMockQuery(b *testing.B, c *conn, query string) {
   153  	stmt, err := c.Prepare(query)
   154  	if err != nil {
   155  		b.Fatal(err)
   156  	}
   157  	defer stmt.Close()
   158  	rows, err := stmt.(driver.StmtQueryContext).QueryContext(context.Background(), nil)
   159  	if err != nil {
   160  		b.Fatal(err)
   161  	}
   162  	defer rows.Close()
   163  	var dest [1]driver.Value
   164  	for {
   165  		if err := rows.Next(dest[:]); err != nil {
   166  			if err == io.EOF {
   167  				break
   168  			}
   169  			b.Fatal(err)
   170  		}
   171  	}
   172  }
   173  
   174  func BenchmarkPreparedSelectString(b *testing.B) {
   175  	var result string
   176  	benchPreparedQuery(b, selectStringQuery, &result)
   177  }
   178  
   179  func BenchmarkPreparedSelectSeries(b *testing.B) {
   180  	var result int
   181  	benchPreparedQuery(b, selectSeriesQuery, &result)
   182  }
   183  
   184  func benchPreparedQuery(b *testing.B, query string, result interface{}) {
   185  	b.StopTimer()
   186  	db := openTestConn(b)
   187  	defer db.Close()
   188  	stmt, err := db.Prepare(query)
   189  	if err != nil {
   190  		b.Fatal(err)
   191  	}
   192  	defer stmt.Close()
   193  	b.StartTimer()
   194  
   195  	for i := 0; i < b.N; i++ {
   196  		benchPreparedQueryLoop(b, db, stmt, result)
   197  	}
   198  }
   199  
   200  func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) {
   201  	rows, err := stmt.Query()
   202  	if err != nil {
   203  		b.Fatal(err)
   204  	}
   205  	if !rows.Next() {
   206  		rows.Close()
   207  		b.Fatal("no rows")
   208  	}
   209  	defer rows.Close()
   210  	for rows.Next() {
   211  		err = rows.Scan(&result)
   212  		if err != nil {
   213  			b.Fatal("failed to scan")
   214  		}
   215  	}
   216  }
   217  
   218  // See the comment for BenchmarkMockSelectString.
   219  func BenchmarkMockPreparedSelectString(b *testing.B) {
   220  	b.StopTimer()
   221  	const parseResponse = "1\x00\x00\x00\x04" +
   222  		"t\x00\x00\x00\x06\x00\x00" +
   223  		"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
   224  		"Z\x00\x00\x00\x05I"
   225  	const responses = parseResponse +
   226  		"2\x00\x00\x00\x04" +
   227  		"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
   228  		"C\x00\x00\x00\rSELECT 1\x00" +
   229  		"Z\x00\x00\x00\x05I"
   230  	c := fakeConn(responses, len(parseResponse))
   231  
   232  	stmt, err := c.Prepare(selectStringQuery)
   233  	if err != nil {
   234  		b.Fatal(err)
   235  	}
   236  	b.StartTimer()
   237  
   238  	for i := 0; i < b.N; i++ {
   239  		benchPreparedMockQuery(b, c, stmt)
   240  	}
   241  }
   242  
   243  func BenchmarkMockPreparedSelectSeries(b *testing.B) {
   244  	b.StopTimer()
   245  	const parseResponse = "1\x00\x00\x00\x04" +
   246  		"t\x00\x00\x00\x06\x00\x00" +
   247  		"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
   248  		"Z\x00\x00\x00\x05I"
   249  	var responses = parseResponse +
   250  		"2\x00\x00\x00\x04" +
   251  		seriesRowData +
   252  		"C\x00\x00\x00\x0fSELECT 100\x00" +
   253  		"Z\x00\x00\x00\x05I"
   254  	c := fakeConn(responses, len(parseResponse))
   255  
   256  	stmt, err := c.Prepare(selectSeriesQuery)
   257  	if err != nil {
   258  		b.Fatal(err)
   259  	}
   260  	b.StartTimer()
   261  
   262  	for i := 0; i < b.N; i++ {
   263  		benchPreparedMockQuery(b, c, stmt)
   264  	}
   265  }
   266  
   267  func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) {
   268  	rows, err := stmt.(driver.StmtQueryContext).QueryContext(context.Background(), nil)
   269  	if err != nil {
   270  		b.Fatal(err)
   271  	}
   272  	defer rows.Close()
   273  	var dest [1]driver.Value
   274  	for {
   275  		if err := rows.Next(dest[:]); err != nil {
   276  			if err == io.EOF {
   277  				break
   278  			}
   279  			b.Fatal(err)
   280  		}
   281  	}
   282  }
   283  
   284  func BenchmarkEncodeInt64(b *testing.B) {
   285  	for i := 0; i < b.N; i++ {
   286  		encode(&parameterStatus{}, int64(1234), oid.T_int8)
   287  	}
   288  }
   289  
   290  func BenchmarkEncodeFloat64(b *testing.B) {
   291  	for i := 0; i < b.N; i++ {
   292  		encode(&parameterStatus{}, 3.14159, oid.T_float8)
   293  	}
   294  }
   295  
   296  var testByteString = []byte("abcdefghijklmnopqrstuvwxyz")
   297  
   298  func BenchmarkEncodeByteaHex(b *testing.B) {
   299  	for i := 0; i < b.N; i++ {
   300  		encode(&parameterStatus{serverVersion: 90000}, testByteString, oid.T_bytea)
   301  	}
   302  }
   303  func BenchmarkEncodeByteaEscape(b *testing.B) {
   304  	for i := 0; i < b.N; i++ {
   305  		encode(&parameterStatus{serverVersion: 84000}, testByteString, oid.T_bytea)
   306  	}
   307  }
   308  
   309  func BenchmarkEncodeBool(b *testing.B) {
   310  	for i := 0; i < b.N; i++ {
   311  		encode(&parameterStatus{}, true, oid.T_bool)
   312  	}
   313  }
   314  
   315  var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local)
   316  
   317  func BenchmarkEncodeTimestamptz(b *testing.B) {
   318  	for i := 0; i < b.N; i++ {
   319  		encode(&parameterStatus{}, testTimestamptz, oid.T_timestamptz)
   320  	}
   321  }
   322  
   323  var testIntBytes = []byte("1234")
   324  
   325  func BenchmarkDecodeInt64(b *testing.B) {
   326  	for i := 0; i < b.N; i++ {
   327  		decode(&parameterStatus{}, testIntBytes, oid.T_int8, formatText)
   328  	}
   329  }
   330  
   331  var testFloatBytes = []byte("3.14159")
   332  
   333  func BenchmarkDecodeFloat64(b *testing.B) {
   334  	for i := 0; i < b.N; i++ {
   335  		decode(&parameterStatus{}, testFloatBytes, oid.T_float8, formatText)
   336  	}
   337  }
   338  
   339  var testBoolBytes = []byte{'t'}
   340  
   341  func BenchmarkDecodeBool(b *testing.B) {
   342  	for i := 0; i < b.N; i++ {
   343  		decode(&parameterStatus{}, testBoolBytes, oid.T_bool, formatText)
   344  	}
   345  }
   346  
   347  func TestDecodeBool(t *testing.T) {
   348  	db := openTestConn(t)
   349  	rows, err := db.Query("select true")
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	rows.Close()
   354  }
   355  
   356  var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07")
   357  
   358  func BenchmarkDecodeTimestamptz(b *testing.B) {
   359  	for i := 0; i < b.N; i++ {
   360  		decode(&parameterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
   361  	}
   362  }
   363  
   364  func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) {
   365  	oldProcs := runtime.GOMAXPROCS(0)
   366  	defer runtime.GOMAXPROCS(oldProcs)
   367  	runtime.GOMAXPROCS(runtime.NumCPU())
   368  	globalLocationCache = newLocationCache()
   369  
   370  	f := func(wg *sync.WaitGroup, loops int) {
   371  		defer wg.Done()
   372  		for i := 0; i < loops; i++ {
   373  			decode(&parameterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
   374  		}
   375  	}
   376  
   377  	wg := &sync.WaitGroup{}
   378  	b.ResetTimer()
   379  	for j := 0; j < 10; j++ {
   380  		wg.Add(1)
   381  		go f(wg, b.N/10)
   382  	}
   383  	wg.Wait()
   384  }
   385  
   386  func BenchmarkLocationCache(b *testing.B) {
   387  	globalLocationCache = newLocationCache()
   388  	for i := 0; i < b.N; i++ {
   389  		globalLocationCache.getLocation(rand.Intn(10000))
   390  	}
   391  }
   392  
   393  func BenchmarkLocationCacheMultiThread(b *testing.B) {
   394  	oldProcs := runtime.GOMAXPROCS(0)
   395  	defer runtime.GOMAXPROCS(oldProcs)
   396  	runtime.GOMAXPROCS(runtime.NumCPU())
   397  	globalLocationCache = newLocationCache()
   398  
   399  	f := func(wg *sync.WaitGroup, loops int) {
   400  		defer wg.Done()
   401  		for i := 0; i < loops; i++ {
   402  			globalLocationCache.getLocation(rand.Intn(10000))
   403  		}
   404  	}
   405  
   406  	wg := &sync.WaitGroup{}
   407  	b.ResetTimer()
   408  	for j := 0; j < 10; j++ {
   409  		wg.Add(1)
   410  		go f(wg, b.N/10)
   411  	}
   412  	wg.Wait()
   413  }
   414  
   415  // Stress test the performance of parsing results from the wire.
   416  func BenchmarkResultParsing(b *testing.B) {
   417  	b.StopTimer()
   418  
   419  	db := openTestConn(b)
   420  	defer db.Close()
   421  	_, err := db.Exec("BEGIN")
   422  	if err != nil {
   423  		b.Fatal(err)
   424  	}
   425  
   426  	b.StartTimer()
   427  	for i := 0; i < b.N; i++ {
   428  		res, err := db.Query("SELECT generate_series(1, 50000)")
   429  		if err != nil {
   430  			b.Fatal(err)
   431  		}
   432  		res.Close()
   433  	}
   434  }
   435  

View as plain text