| 385 | } |
| 386 | |
| 387 | func benchmark10kRows(b *testing.B, compress bool) { |
| 388 | // Setup -- prepare 10000 rows. |
| 389 | db := initDB(b, compress, |
| 390 | "DROP TABLE IF EXISTS foo", |
| 391 | "CREATE TABLE foo (id INT PRIMARY KEY, val TEXT)") |
| 392 | defer db.Close() |
| 393 | |
| 394 | sval := strings.Repeat("x", 50) |
| 395 | stmt, err := db.Prepare(`INSERT INTO foo (id, val) VALUES (?, ?)` + strings.Repeat(",(?,?)", 99)) |
| 396 | if err != nil { |
| 397 | b.Errorf("failed to prepare query: %v", err) |
| 398 | return |
| 399 | } |
| 400 | |
| 401 | args := make([]any, 200) |
| 402 | for i := 1; i < 200; i += 2 { |
| 403 | args[i] = sval |
| 404 | } |
| 405 | for i := 0; i < 10000; i += 100 { |
| 406 | for j := range 100 { |
| 407 | args[j*2] = i + j |
| 408 | } |
| 409 | _, err := stmt.Exec(args...) |
| 410 | if err != nil { |
| 411 | b.Error(err) |
| 412 | return |
| 413 | } |
| 414 | } |
| 415 | stmt.Close() |
| 416 | |
| 417 | // benchmark function called several times with different b.N. |
| 418 | // it means heavy setup is called multiple times. |
| 419 | // Use b.Run() to run expensive setup only once. |
| 420 | // Go 1.24 introduced b.Loop() for this purpose. But we keep this |
| 421 | // benchmark compatible with Go 1.20. |
| 422 | b.Run("query", func(b *testing.B) { |
| 423 | b.ReportAllocs() |
| 424 | for i := 0; i < b.N; i++ { |
| 425 | rows, err := db.Query(`SELECT id, val FROM foo`) |
| 426 | if err != nil { |
| 427 | b.Errorf("failed to select: %v", err) |
| 428 | return |
| 429 | } |
| 430 | // rows.Scan() escapes arguments. So these variables must be defined |
| 431 | // before loop. |
| 432 | var i int |
| 433 | var s sql.RawBytes |
| 434 | for rows.Next() { |
| 435 | if err := rows.Scan(&i, &s); err != nil { |
| 436 | b.Errorf("failed to scan: %v", err) |
| 437 | rows.Close() |
| 438 | return |
| 439 | } |
| 440 | } |
| 441 | if err = rows.Err(); err != nil { |
| 442 | b.Errorf("failed to read rows: %v", err) |
| 443 | } |
| 444 | rows.Close() |