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(¶meterStatus{}, int64(1234), oid.T_int8) 288 } 289 } 290 291 func BenchmarkEncodeFloat64(b *testing.B) { 292 for i := 0; i < b.N; i++ { 293 encode(¶meterStatus{}, 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(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea) 302 } 303 } 304 func BenchmarkEncodeByteaEscape(b *testing.B) { 305 for i := 0; i < b.N; i++ { 306 encode(¶meterStatus{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(¶meterStatus{}, 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(¶meterStatus{}, 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(¶meterStatus{}, 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(¶meterStatus{}, 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(¶meterStatus{}, 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(¶meterStatus{}, 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(¶meterStatus{}, 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 }