@@ -46,10 +46,10 @@ func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt {
4646	return  stmt 
4747}
4848
49- func  initDB (b  * testing.B , useCompression  bool , queries  ... string ) * sql.DB  {
49+ func  initDB (b  * testing.B , compress  bool , queries  ... string ) * sql.DB  {
5050	tb  :=  (* TB )(b )
5151	comprStr  :=  "" 
52- 	if  useCompression  {
52+ 	if  compress  {
5353		comprStr  =  "&compress=1" 
5454	}
5555	db  :=  tb .checkDB (sql .Open (driverNameTest , dsn + comprStr ))
@@ -64,16 +64,15 @@ func initDB(b *testing.B, useCompression bool, queries ...string) *sql.DB {
6464const  concurrencyLevel  =  10 
6565
6666func  BenchmarkQuery (b  * testing.B ) {
67- 	benchmarkQueryHelper (b , false )
67+ 	benchmarkQuery (b , false )
6868}
6969
70- func  BenchmarkQueryCompression (b  * testing.B ) {
71- 	benchmarkQueryHelper (b , true )
70+ func  BenchmarkQueryCompressed (b  * testing.B ) {
71+ 	benchmarkQuery (b , true )
7272}
7373
74- func  benchmarkQueryHelper (b  * testing.B , compr  bool ) {
74+ func  benchmarkQuery (b  * testing.B , compr  bool ) {
7575	tb  :=  (* TB )(b )
76- 	b .StopTimer ()
7776	b .ReportAllocs ()
7877	db  :=  initDB (b , compr ,
7978		"DROP TABLE IF EXISTS foo" ,
@@ -115,8 +114,6 @@ func benchmarkQueryHelper(b *testing.B, compr bool) {
115114
116115func  BenchmarkExec (b  * testing.B ) {
117116	tb  :=  (* TB )(b )
118- 	b .StopTimer ()
119- 	b .ReportAllocs ()
120117	db  :=  tb .checkDB (sql .Open (driverNameTest , dsn ))
121118	db .SetMaxIdleConns (concurrencyLevel )
122119	defer  db .Close ()
@@ -128,7 +125,9 @@ func BenchmarkExec(b *testing.B) {
128125	var  wg  sync.WaitGroup 
129126	wg .Add (concurrencyLevel )
130127	defer  wg .Wait ()
131- 	b .StartTimer ()
128+ 
129+ 	b .ReportAllocs ()
130+ 	b .ResetTimer ()
132131
133132	for  range  concurrencyLevel  {
134133		go  func () {
@@ -158,14 +157,15 @@ func initRoundtripBenchmarks() ([]byte, int, int) {
158157}
159158
160159func  BenchmarkRoundtripTxt (b  * testing.B ) {
161- 	b .StopTimer ()
162160	sample , min , max  :=  initRoundtripBenchmarks ()
163161	sampleString  :=  string (sample )
164- 	b .ReportAllocs ()
165162	tb  :=  (* TB )(b )
166163	db  :=  tb .checkDB (sql .Open (driverNameTest , dsn ))
167164	defer  db .Close ()
168- 	b .StartTimer ()
165+ 
166+ 	b .ReportAllocs ()
167+ 	b .ResetTimer ()
168+ 
169169	var  result  string 
170170	for  i  :=  0 ; i  <  b .N ; i ++  {
171171		length  :=  min  +  i 
@@ -192,15 +192,15 @@ func BenchmarkRoundtripTxt(b *testing.B) {
192192}
193193
194194func  BenchmarkRoundtripBin (b  * testing.B ) {
195- 	b .StopTimer ()
196195	sample , min , max  :=  initRoundtripBenchmarks ()
197- 	b .ReportAllocs ()
198196	tb  :=  (* TB )(b )
199197	db  :=  tb .checkDB (sql .Open (driverNameTest , dsn ))
200198	defer  db .Close ()
201199	stmt  :=  tb .checkStmt (db .Prepare ("SELECT ?" ))
202200	defer  stmt .Close ()
203- 	b .StartTimer ()
201+ 
202+ 	b .ReportAllocs ()
203+ 	b .ResetTimer ()
204204	var  result  sql.RawBytes 
205205	for  i  :=  0 ; i  <  b .N ; i ++  {
206206		length  :=  min  +  i 
@@ -385,10 +385,9 @@ func BenchmarkQueryRawBytes(b *testing.B) {
385385	}
386386}
387387
388- // BenchmarkReceiveMassiveRows measures performance of receiving large number of rows. 
389- func  BenchmarkReceiveMassiveRows (b  * testing.B ) {
388+ func  benchmark10kRows (b  * testing.B , compress  bool ) {
390389	// Setup -- prepare 10000 rows. 
391- 	db  :=  initDB (b , false ,
390+ 	db  :=  initDB (b , compress ,
392391		"DROP TABLE IF EXISTS foo" ,
393392		"CREATE TABLE foo (id INT PRIMARY KEY, val TEXT)" )
394393	defer  db .Close ()
@@ -399,11 +398,14 @@ func BenchmarkReceiveMassiveRows(b *testing.B) {
399398		b .Errorf ("failed to prepare query: %v" , err )
400399		return 
401400	}
401+ 
402+ 	args  :=  make ([]any , 200 )
403+ 	for  i  :=  1 ; i  <  200 ; i += 2  {
404+ 		args [i ] =  sval 
405+ 	}
402406	for  i  :=  0 ; i  <  10000 ; i  +=  100  {
403- 		args  :=  make ([]any , 200 )
404- 		for  j  :=  range  100  {
407+ 		for  j  :=  0 ; j  <  100 ; j ++  {
405408			args [j * 2 ] =  i  +  j 
406- 			args [j * 2 + 1 ] =  sval 
407409		}
408410		_ , err  :=  stmt .Exec (args ... )
409411		if  err  !=  nil  {
@@ -413,30 +415,43 @@ func BenchmarkReceiveMassiveRows(b *testing.B) {
413415	}
414416	stmt .Close ()
415417
416- 	// Use b.Run() to skip expensive setup. 
418+ 	// benchmark function called several times with different b.N. 
419+ 	// it means heavy setup is called multiple times. 
420+ 	// Use b.Run() to run expensive setup only once. 
421+ 	// Go 1.24 introduced b.Loop() fot this purpose. But we keep this 
422+ 	// benchmark compatible with Go 1.20. 
417423	b .Run ("query" , func (b  * testing.B ) {
418424		b .ReportAllocs ()
419- 
420425		for  i  :=  0 ; i  <  b .N ; i ++  {
421426			rows , err  :=  db .Query (`SELECT id, val FROM foo` )
422427			if  err  !=  nil  {
423428				b .Errorf ("failed to select: %v" , err )
424429				return 
425430			}
431+ 			// rows.Scan() escapes arguments. So these variables must be defined 
432+ 			// before loop. 
433+ 			var  i  int 
434+ 			var  s  sql.RawBytes 
426435			for  rows .Next () {
427- 				var  i  int 
428- 				var  s  sql.RawBytes 
429- 				err  =  rows .Scan (& i , & s )
430- 				if  err  !=  nil  {
436+ 				if  err  :=  rows .Scan (& i , & s ); err  !=  nil  {
431437					b .Errorf ("failed to scan: %v" , err )
432- 					_   =   rows .Close ()
438+ 					rows .Close ()
433439					return 
434440				}
435441			}
436442			if  err  =  rows .Err (); err  !=  nil  {
437443				b .Errorf ("failed to read rows: %v" , err )
438444			}
439- 			_   =   rows .Close ()
445+ 			rows .Close ()
440446		}
441447	})
442448}
449+ 
450+ // BenchmarkReceive10kRows measures performance of receiving large number of rows. 
451+ func  BenchmarkReceive10kRows (b  * testing.B ) {
452+ 	benchmark10kRows (b , false )
453+ }
454+ 
455+ func  BenchmarkReceive10kRowsCompressed (b  * testing.B ) {
456+ 	benchmark10kRows (b , true )
457+ }
0 commit comments