| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- // +build go1.1
- package pq
- import (
- "bufio"
- "bytes"
- "database/sql"
- "database/sql/driver"
- "github.com/lib/pq/oid"
- "io"
- "math/rand"
- "net"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "testing"
- "time"
- )
- var (
- selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'"
- selectSeriesQuery = "SELECT generate_series(1, 100)"
- )
- func BenchmarkSelectString(b *testing.B) {
- var result string
- benchQuery(b, selectStringQuery, &result)
- }
- func BenchmarkSelectSeries(b *testing.B) {
- var result int
- benchQuery(b, selectSeriesQuery, &result)
- }
- func benchQuery(b *testing.B, query string, result interface{}) {
- b.StopTimer()
- db := openTestConn(b)
- defer db.Close()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- benchQueryLoop(b, db, query, result)
- }
- }
- func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) {
- rows, err := db.Query(query)
- if err != nil {
- b.Fatal(err)
- }
- defer rows.Close()
- for rows.Next() {
- err = rows.Scan(result)
- if err != nil {
- b.Fatal("failed to scan", err)
- }
- }
- }
- // reading from circularConn yields content[:prefixLen] once, followed by
- // content[prefixLen:] over and over again. It never returns EOF.
- type circularConn struct {
- content string
- prefixLen int
- pos int
- net.Conn // for all other net.Conn methods that will never be called
- }
- func (r *circularConn) Read(b []byte) (n int, err error) {
- n = copy(b, r.content[r.pos:])
- r.pos += n
- if r.pos >= len(r.content) {
- r.pos = r.prefixLen
- }
- return
- }
- func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil }
- func (r *circularConn) Close() error { return nil }
- func fakeConn(content string, prefixLen int) *conn {
- c := &circularConn{content: content, prefixLen: prefixLen}
- return &conn{buf: bufio.NewReader(c), c: c}
- }
- // This benchmark is meant to be the same as BenchmarkSelectString, but takes
- // out some of the factors this package can't control. The numbers are less noisy,
- // but also the costs of network communication aren't accurately represented.
- func BenchmarkMockSelectString(b *testing.B) {
- b.StopTimer()
- // taken from a recorded run of BenchmarkSelectString
- // See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html
- const response = "1\x00\x00\x00\x04" +
- "t\x00\x00\x00\x06\x00\x00" +
- "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" +
- "Z\x00\x00\x00\x05I" +
- "2\x00\x00\x00\x04" +
- "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
- "C\x00\x00\x00\rSELECT 1\x00" +
- "Z\x00\x00\x00\x05I" +
- "3\x00\x00\x00\x04" +
- "Z\x00\x00\x00\x05I"
- c := fakeConn(response, 0)
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- benchMockQuery(b, c, selectStringQuery)
- }
- }
- var seriesRowData = func() string {
- var buf bytes.Buffer
- for i := 1; i <= 100; i++ {
- digits := byte(2)
- if i >= 100 {
- digits = 3
- } else if i < 10 {
- digits = 1
- }
- buf.WriteString("D\x00\x00\x00")
- buf.WriteByte(10 + digits)
- buf.WriteString("\x00\x01\x00\x00\x00")
- buf.WriteByte(digits)
- buf.WriteString(strconv.Itoa(i))
- }
- return buf.String()
- }()
- func BenchmarkMockSelectSeries(b *testing.B) {
- b.StopTimer()
- var response = "1\x00\x00\x00\x04" +
- "t\x00\x00\x00\x06\x00\x00" +
- "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" +
- "Z\x00\x00\x00\x05I" +
- "2\x00\x00\x00\x04" +
- seriesRowData +
- "C\x00\x00\x00\x0fSELECT 100\x00" +
- "Z\x00\x00\x00\x05I" +
- "3\x00\x00\x00\x04" +
- "Z\x00\x00\x00\x05I"
- c := fakeConn(response, 0)
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- benchMockQuery(b, c, selectSeriesQuery)
- }
- }
- func benchMockQuery(b *testing.B, c *conn, query string) {
- stmt, err := c.Prepare(query)
- if err != nil {
- b.Fatal(err)
- }
- defer stmt.Close()
- rows, err := stmt.Query(nil)
- if err != nil {
- b.Fatal(err)
- }
- defer rows.Close()
- var dest [1]driver.Value
- for {
- if err := rows.Next(dest[:]); err != nil {
- if err == io.EOF {
- break
- }
- b.Fatal(err)
- }
- }
- }
- func BenchmarkPreparedSelectString(b *testing.B) {
- var result string
- benchPreparedQuery(b, selectStringQuery, &result)
- }
- func BenchmarkPreparedSelectSeries(b *testing.B) {
- var result int
- benchPreparedQuery(b, selectSeriesQuery, &result)
- }
- func benchPreparedQuery(b *testing.B, query string, result interface{}) {
- b.StopTimer()
- db := openTestConn(b)
- defer db.Close()
- stmt, err := db.Prepare(query)
- if err != nil {
- b.Fatal(err)
- }
- defer stmt.Close()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- benchPreparedQueryLoop(b, db, stmt, result)
- }
- }
- func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) {
- rows, err := stmt.Query()
- if err != nil {
- b.Fatal(err)
- }
- if !rows.Next() {
- rows.Close()
- b.Fatal("no rows")
- }
- defer rows.Close()
- for rows.Next() {
- err = rows.Scan(&result)
- if err != nil {
- b.Fatal("failed to scan")
- }
- }
- }
- // See the comment for BenchmarkMockSelectString.
- func BenchmarkMockPreparedSelectString(b *testing.B) {
- b.StopTimer()
- const parseResponse = "1\x00\x00\x00\x04" +
- "t\x00\x00\x00\x06\x00\x00" +
- "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" +
- "Z\x00\x00\x00\x05I"
- const responses = parseResponse +
- "2\x00\x00\x00\x04" +
- "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
- "C\x00\x00\x00\rSELECT 1\x00" +
- "Z\x00\x00\x00\x05I"
- c := fakeConn(responses, len(parseResponse))
- stmt, err := c.Prepare(selectStringQuery)
- if err != nil {
- b.Fatal(err)
- }
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- benchPreparedMockQuery(b, c, stmt)
- }
- }
- func BenchmarkMockPreparedSelectSeries(b *testing.B) {
- b.StopTimer()
- const parseResponse = "1\x00\x00\x00\x04" +
- "t\x00\x00\x00\x06\x00\x00" +
- "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" +
- "Z\x00\x00\x00\x05I"
- var responses = parseResponse +
- "2\x00\x00\x00\x04" +
- seriesRowData +
- "C\x00\x00\x00\x0fSELECT 100\x00" +
- "Z\x00\x00\x00\x05I"
- c := fakeConn(responses, len(parseResponse))
- stmt, err := c.Prepare(selectSeriesQuery)
- if err != nil {
- b.Fatal(err)
- }
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- benchPreparedMockQuery(b, c, stmt)
- }
- }
- func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) {
- rows, err := stmt.Query(nil)
- if err != nil {
- b.Fatal(err)
- }
- defer rows.Close()
- var dest [1]driver.Value
- for {
- if err := rows.Next(dest[:]); err != nil {
- if err == io.EOF {
- break
- }
- b.Fatal(err)
- }
- }
- }
- func BenchmarkEncodeInt64(b *testing.B) {
- for i := 0; i < b.N; i++ {
- encode(¶meterStatus{}, int64(1234), oid.T_int8)
- }
- }
- func BenchmarkEncodeFloat64(b *testing.B) {
- for i := 0; i < b.N; i++ {
- encode(¶meterStatus{}, 3.14159, oid.T_float8)
- }
- }
- var testByteString = []byte("abcdefghijklmnopqrstuvwxyz")
- func BenchmarkEncodeByteaHex(b *testing.B) {
- for i := 0; i < b.N; i++ {
- encode(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea)
- }
- }
- func BenchmarkEncodeByteaEscape(b *testing.B) {
- for i := 0; i < b.N; i++ {
- encode(¶meterStatus{serverVersion: 84000}, testByteString, oid.T_bytea)
- }
- }
- func BenchmarkEncodeBool(b *testing.B) {
- for i := 0; i < b.N; i++ {
- encode(¶meterStatus{}, true, oid.T_bool)
- }
- }
- var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local)
- func BenchmarkEncodeTimestamptz(b *testing.B) {
- for i := 0; i < b.N; i++ {
- encode(¶meterStatus{}, testTimestamptz, oid.T_timestamptz)
- }
- }
- var testIntBytes = []byte("1234")
- func BenchmarkDecodeInt64(b *testing.B) {
- for i := 0; i < b.N; i++ {
- decode(¶meterStatus{}, testIntBytes, oid.T_int8)
- }
- }
- var testFloatBytes = []byte("3.14159")
- func BenchmarkDecodeFloat64(b *testing.B) {
- for i := 0; i < b.N; i++ {
- decode(¶meterStatus{}, testFloatBytes, oid.T_float8)
- }
- }
- var testBoolBytes = []byte{'t'}
- func BenchmarkDecodeBool(b *testing.B) {
- for i := 0; i < b.N; i++ {
- decode(¶meterStatus{}, testBoolBytes, oid.T_bool)
- }
- }
- func TestDecodeBool(t *testing.T) {
- db := openTestConn(t)
- rows, err := db.Query("select true")
- if err != nil {
- t.Fatal(err)
- }
- rows.Close()
- }
- var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07")
- func BenchmarkDecodeTimestamptz(b *testing.B) {
- for i := 0; i < b.N; i++ {
- decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz)
- }
- }
- func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) {
- oldProcs := runtime.GOMAXPROCS(0)
- defer runtime.GOMAXPROCS(oldProcs)
- runtime.GOMAXPROCS(runtime.NumCPU())
- globalLocationCache = newLocationCache()
- f := func(wg *sync.WaitGroup, loops int) {
- defer wg.Done()
- for i := 0; i < loops; i++ {
- decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz)
- }
- }
- wg := &sync.WaitGroup{}
- b.ResetTimer()
- for j := 0; j < 10; j++ {
- wg.Add(1)
- go f(wg, b.N/10)
- }
- wg.Wait()
- }
- func BenchmarkLocationCache(b *testing.B) {
- globalLocationCache = newLocationCache()
- for i := 0; i < b.N; i++ {
- globalLocationCache.getLocation(rand.Intn(10000))
- }
- }
- func BenchmarkLocationCacheMultiThread(b *testing.B) {
- oldProcs := runtime.GOMAXPROCS(0)
- defer runtime.GOMAXPROCS(oldProcs)
- runtime.GOMAXPROCS(runtime.NumCPU())
- globalLocationCache = newLocationCache()
- f := func(wg *sync.WaitGroup, loops int) {
- defer wg.Done()
- for i := 0; i < loops; i++ {
- globalLocationCache.getLocation(rand.Intn(10000))
- }
- }
- wg := &sync.WaitGroup{}
- b.ResetTimer()
- for j := 0; j < 10; j++ {
- wg.Add(1)
- go f(wg, b.N/10)
- }
- wg.Wait()
- }
- // Stress test the performance of parsing results from the wire.
- func BenchmarkResultParsing(b *testing.B) {
- b.StopTimer()
- db := openTestConn(b)
- defer db.Close()
- _, err := db.Exec("BEGIN")
- if err != nil {
- b.Fatal(err)
- }
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- res, err := db.Query("SELECT generate_series(1, 50000)")
- if err != nil {
- b.Fatal(err)
- }
- res.Close()
- }
- }
|