bench_test.go 10 KB

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