github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/lib/pq/bench_test.go (about)

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