github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/bench/bench_test.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package bench 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "math/rand" 18 "reflect" 19 "runtime" 20 "strconv" 21 "strings" 22 "sync/atomic" 23 "testing" 24 25 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 26 "github.com/cockroachdb/cockroach/pkg/util/log" 27 "github.com/cockroachdb/cockroach/pkg/util/randutil" 28 _ "github.com/go-sql-driver/mysql" 29 _ "github.com/lib/pq" 30 ) 31 32 func runBenchmarkSelect1(b *testing.B, db *sqlutils.SQLRunner) { 33 b.ResetTimer() 34 for i := 0; i < b.N; i++ { 35 rows := db.Query(b, `SELECT 1`) 36 rows.Close() 37 } 38 b.StopTimer() 39 } 40 41 func BenchmarkSelect1(b *testing.B) { 42 defer log.Scope(b).Close(b) 43 ForEachDB(b, runBenchmarkSelect1) 44 } 45 46 func runBenchmarkSelectWithTargetsAndFilter( 47 b *testing.B, db *sqlutils.SQLRunner, targets, filter string, args ...interface{}, 48 ) { 49 defer func() { 50 db.Exec(b, `DROP TABLE IF EXISTS bench.select`) 51 }() 52 53 db.Exec(b, `CREATE TABLE bench.select (k INT PRIMARY KEY, a INT, b INT, c INT, d INT)`) 54 55 var buf bytes.Buffer 56 buf.WriteString(`INSERT INTO bench.select VALUES `) 57 58 // We insert all combinations of values between 1 and num for columns a, b, c. The intention is 59 // to benchmark the expression parsing and query setup so we don't want to have many rows to go 60 // through. 61 const num = 3 62 row := 0 63 for i := 1; i <= num; i++ { 64 for j := 1; j <= num; j++ { 65 for k := 1; k <= num; k++ { 66 if row > 0 { 67 buf.WriteString(", ") 68 } 69 row++ 70 fmt.Fprintf(&buf, "(%d, %d, %d, %d)", row, i, j, k) 71 } 72 } 73 } 74 db.Exec(b, buf.String()) 75 76 b.ResetTimer() 77 for i := 0; i < b.N; i++ { 78 rows := db.Query(b, fmt.Sprintf(`SELECT %s FROM bench.select WHERE %s`, targets, filter), args...) 79 rows.Close() 80 } 81 b.StopTimer() 82 } 83 84 // BenchmarkSelect2 runs a SELECT query with non-trivial expressions. The main 85 // purpose is to detect major regressions in query expression processing. 86 func BenchmarkSelect2(b *testing.B) { 87 defer log.Scope(b).Close(b) 88 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 89 targets := `a, b, c, a+b, a+1, (a+2)*(b+3)*(c+4)` 90 filter := `(a = 1) OR ((a = 2) and (b = c)) OR (a + b = 3) OR (2*a + 4*b = 4*c)` 91 runBenchmarkSelectWithTargetsAndFilter(b, db, targets, filter) 92 }) 93 } 94 95 // BenchmarkSelect3 runs a SELECT query with non-trivial expressions. The main 96 // purpose is to quantify regressions in numeric type processing. 97 func BenchmarkSelect3(b *testing.B) { 98 defer log.Scope(b).Close(b) 99 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 100 targets := `a/b, b/c, c != 3.3 + $1, a = 2.0, c * 9.0` 101 filter := `a > 1 AND b < 4.5` 102 args := []interface{}{1.0} 103 runBenchmarkSelectWithTargetsAndFilter(b, db, targets, filter, args...) 104 }) 105 } 106 107 func BenchmarkCount(b *testing.B) { 108 if testing.Short() { 109 b.Skip("short flag") 110 } 111 defer log.Scope(b).Close(b) 112 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 113 defer func() { 114 db.Exec(b, `DROP TABLE IF EXISTS bench.count`) 115 }() 116 117 db.Exec(b, `CREATE TABLE bench.count (k INT PRIMARY KEY, v TEXT)`) 118 119 var buf bytes.Buffer 120 val := 0 121 for i := 0; i < 100; i++ { 122 buf.Reset() 123 buf.WriteString(`INSERT INTO bench.count VALUES `) 124 for j := 0; j < 1000; j++ { 125 if j > 0 { 126 buf.WriteString(", ") 127 } 128 fmt.Fprintf(&buf, "(%d, '%s')", val, strconv.Itoa(val)) 129 val++ 130 } 131 db.Exec(b, buf.String()) 132 } 133 134 b.ResetTimer() 135 136 for i := 0; i < b.N; i++ { 137 db.Exec(b, "SELECT count(*) FROM bench.count") 138 } 139 b.StopTimer() 140 }) 141 } 142 143 func BenchmarkCountTwoCF(b *testing.B) { 144 if testing.Short() { 145 b.Skip("short flag") 146 } 147 defer log.Scope(b).Close(b) 148 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 149 defer func() { 150 db.Exec(b, `DROP TABLE IF EXISTS bench.count`) 151 }() 152 153 db.Exec(b, `CREATE TABLE bench.count (k INT PRIMARY KEY, v TEXT, FAMILY (k), FAMILY (v))`) 154 155 var buf bytes.Buffer 156 val := 0 157 for i := 0; i < 100; i++ { 158 buf.Reset() 159 buf.WriteString(`INSERT INTO bench.count VALUES `) 160 for j := 0; j < 1000; j++ { 161 if j > 0 { 162 buf.WriteString(", ") 163 } 164 fmt.Fprintf(&buf, "(%d, '%s')", val, strconv.Itoa(val)) 165 val++ 166 } 167 db.Exec(b, buf.String()) 168 } 169 170 b.ResetTimer() 171 172 for i := 0; i < b.N; i++ { 173 db.Exec(b, "SELECT count(*) FROM bench.count") 174 } 175 b.StopTimer() 176 }) 177 } 178 179 func BenchmarkSort(b *testing.B) { 180 if testing.Short() { 181 b.Skip("short flag") 182 } 183 defer log.Scope(b).Close(b) 184 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 185 defer func() { 186 db.Exec(b, `DROP TABLE IF EXISTS bench.sort`) 187 }() 188 189 db.Exec(b, `CREATE TABLE bench.sort (k INT PRIMARY KEY, v INT)`) 190 191 var buf bytes.Buffer 192 val := 0 193 for i := 0; i < 100; i++ { 194 buf.Reset() 195 buf.WriteString(`INSERT INTO bench.sort VALUES `) 196 for j := 0; j < 1000; j++ { 197 if j > 0 { 198 buf.WriteString(", ") 199 } 200 fmt.Fprintf(&buf, "(%d, %d)", val, -val) 201 val++ 202 } 203 db.Exec(b, buf.String()) 204 } 205 206 b.ResetTimer() 207 208 for i := 0; i < b.N; i++ { 209 db.Exec(b, "SELECT * FROM bench.sort ORDER BY v") 210 } 211 b.StopTimer() 212 }) 213 } 214 215 // BenchmarkTableResolution benchmarks table name resolution 216 // for a variety of different naming schemes. 217 func BenchmarkTableResolution(b *testing.B) { 218 if testing.Short() { 219 b.Skip("short flag") 220 } 221 defer log.Scope(b).Close(b) 222 223 for _, createTempTables := range []bool{false, true} { 224 b.Run(fmt.Sprintf("temp_schema_exists:%t", createTempTables), func(b *testing.B) { 225 benchmarkCockroach(b, func(b *testing.B, db *sqlutils.SQLRunner) { 226 defer func() { 227 db.Exec(b, `DROP TABLE IF EXISTS bench.tbl`) 228 if createTempTables { 229 db.Exec(b, `DROP TABLE IF EXISTS bench.pg_temp.temp_tbl`) 230 } 231 }() 232 233 db.Exec(b, ` 234 USE bench; 235 CREATE TABLE tbl (k INT PRIMARY KEY, v INT); 236 `) 237 238 type benchCase struct { 239 desc string 240 tblName string 241 } 242 cases := []benchCase{ 243 {"table", "tbl"}, 244 {"database.table", "bench.tbl"}, 245 {"database.public.table", "bench.public.tbl"}, 246 {"public.table", "public.tbl"}, 247 } 248 if createTempTables { 249 db.Exec(b, ` 250 SET experimental_enable_temp_tables=true; 251 CREATE TEMP TABLE temp_tbl (k INT PRIMARY KEY); 252 `) 253 cases = append(cases, []benchCase{ 254 {"temp_table", "temp_tbl"}, 255 {"database.pg_temp.table", "bench.pg_temp.temp_tbl"}, 256 {"pg_temp.table", "pg_temp.temp_tbl"}, 257 }...) 258 } 259 for _, c := range cases { 260 b.Run(c.desc, func(b *testing.B) { 261 query := "SELECT * FROM " + c.tblName 262 b.ResetTimer() 263 for i := 0; i < b.N; i++ { 264 db.Exec(b, query) 265 } 266 b.StopTimer() 267 }) 268 } 269 }) 270 }) 271 } 272 } 273 274 // runBenchmarkInsert benchmarks inserting count rows into a table. 275 func runBenchmarkInsert(b *testing.B, db *sqlutils.SQLRunner, count int) { 276 defer func() { 277 db.Exec(b, `DROP TABLE IF EXISTS bench.insert`) 278 }() 279 280 db.Exec(b, `CREATE TABLE bench.insert (k INT PRIMARY KEY)`) 281 282 b.ResetTimer() 283 var buf bytes.Buffer 284 val := 0 285 for i := 0; i < b.N; i++ { 286 buf.Reset() 287 buf.WriteString(`INSERT INTO bench.insert VALUES `) 288 for j := 0; j < count; j++ { 289 if j > 0 { 290 buf.WriteString(", ") 291 } 292 fmt.Fprintf(&buf, "(%d)", val) 293 val++ 294 } 295 db.Exec(b, buf.String()) 296 } 297 b.StopTimer() 298 299 } 300 301 // runBenchmarkInsertFK benchmarks inserting count rows into a table with a 302 // present foreign key into another table. 303 func runBenchmarkInsertFK(b *testing.B, db *sqlutils.SQLRunner, count int) { 304 for _, nFks := range []int{1, 5, 10} { 305 b.Run(fmt.Sprintf("nFks=%d", nFks), func(b *testing.B) { 306 defer func() { 307 dropStmt := "DROP TABLE IF EXISTS bench.insert" 308 for i := 0; i < nFks; i++ { 309 dropStmt += fmt.Sprintf(",bench.fk%d", i) 310 } 311 db.Exec(b, dropStmt) 312 }() 313 314 for i := 0; i < nFks; i++ { 315 db.Exec(b, fmt.Sprintf(`CREATE TABLE bench.fk%d (k INT PRIMARY KEY)`, i)) 316 db.Exec(b, fmt.Sprintf(`INSERT INTO bench.fk%d VALUES(1), (2), (3)`, i)) 317 } 318 319 createStmt := `CREATE TABLE bench.insert (k INT PRIMARY KEY` 320 valuesStr := "(%d" 321 for i := 0; i < nFks; i++ { 322 createStmt += fmt.Sprintf(",fk%d INT, FOREIGN KEY(fk%d) REFERENCES bench.fk%d(k)", i, i, i) 323 valuesStr += ",1" 324 } 325 createStmt += ")" 326 valuesStr += ")" 327 db.Exec(b, createStmt) 328 329 b.ResetTimer() 330 var buf bytes.Buffer 331 val := 0 332 for i := 0; i < b.N; i++ { 333 buf.Reset() 334 buf.WriteString(`INSERT INTO bench.insert VALUES `) 335 for j := 0; j < count; j++ { 336 if j > 0 { 337 buf.WriteString(", ") 338 } 339 fmt.Fprintf(&buf, valuesStr, val) 340 val++ 341 } 342 db.Exec(b, buf.String()) 343 } 344 b.StopTimer() 345 346 }) 347 } 348 } 349 350 // runBenchmarkInsertSecondaryIndex benchmarks inserting count rows into a table with a 351 // secondary index. 352 func runBenchmarkInsertSecondaryIndex(b *testing.B, db *sqlutils.SQLRunner, count int) { 353 defer func() { 354 db.Exec(b, `DROP TABLE IF EXISTS bench.insert`) 355 }() 356 357 db.Exec(b, `CREATE TABLE bench.insert (k INT PRIMARY KEY, v INT, INDEX(v))`) 358 359 for i := 0; i < 10; i++ { 360 db.Exec(b, `CREATE INDEX ON bench.insert (v)`) 361 } 362 363 b.ResetTimer() 364 var buf bytes.Buffer 365 val := 0 366 for i := 0; i < b.N; i++ { 367 buf.Reset() 368 buf.WriteString(`INSERT INTO bench.insert VALUES `) 369 for j := 0; j < count; j++ { 370 if j > 0 { 371 buf.WriteString(", ") 372 } 373 fmt.Fprintf(&buf, "(%d, %d)", val, val) 374 val++ 375 } 376 db.Exec(b, buf.String()) 377 } 378 b.StopTimer() 379 } 380 381 func BenchmarkSQL(b *testing.B) { 382 if testing.Short() { 383 b.Skip("short flag") 384 } 385 defer log.Scope(b).Close(b) 386 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 387 for _, runFn := range []func(*testing.B, *sqlutils.SQLRunner, int){ 388 runBenchmarkDelete, 389 runBenchmarkInsert, 390 runBenchmarkInsertDistinct, 391 runBenchmarkInsertFK, 392 runBenchmarkInsertSecondaryIndex, 393 runBenchmarkInterleavedSelect, 394 runBenchmarkInterleavedFK, 395 runBenchmarkTrackChoices, 396 runBenchmarkUpdate, 397 runBenchmarkUpsert, 398 } { 399 fnName := runtime.FuncForPC(reflect.ValueOf(runFn).Pointer()).Name() 400 fnName = strings.TrimPrefix(fnName, "github.com/cockroachdb/cockroach/pkg/bench.runBenchmark") 401 b.Run(fnName, func(b *testing.B) { 402 for _, count := range []int{1, 10, 100, 1000} { 403 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 404 runFn(b, db, count) 405 }) 406 } 407 }) 408 } 409 }) 410 } 411 412 // runBenchmarkUpdate benchmarks updating count random rows in a table. 413 func runBenchmarkUpdate(b *testing.B, db *sqlutils.SQLRunner, count int) { 414 defer func() { 415 db.Exec(b, `DROP TABLE IF EXISTS bench.update`) 416 }() 417 418 const rows = 10000 419 db.Exec(b, `CREATE TABLE bench.update (k INT PRIMARY KEY, v INT)`) 420 421 var buf bytes.Buffer 422 buf.WriteString(`INSERT INTO bench.update VALUES `) 423 for i := 0; i < rows; i++ { 424 if i > 0 { 425 buf.WriteString(", ") 426 } 427 fmt.Fprintf(&buf, "(%d, %d)", i, i) 428 } 429 db.Exec(b, buf.String()) 430 431 s := rand.New(rand.NewSource(5432)) 432 433 b.ResetTimer() 434 for i := 0; i < b.N; i++ { 435 buf.Reset() 436 buf.WriteString(`UPDATE bench.update SET v = v + 1 WHERE k IN (`) 437 for j := 0; j < count; j++ { 438 if j > 0 { 439 buf.WriteString(", ") 440 } 441 fmt.Fprintf(&buf, `%d`, s.Intn(rows)) 442 } 443 buf.WriteString(`)`) 444 db.Exec(b, buf.String()) 445 } 446 b.StopTimer() 447 } 448 449 // runBenchmarkUpsert benchmarks upserting count rows in a table. 450 func runBenchmarkUpsert(b *testing.B, db *sqlutils.SQLRunner, count int) { 451 defer func() { 452 db.Exec(b, `DROP TABLE IF EXISTS bench.upsert`) 453 }() 454 455 db.Exec(b, `CREATE TABLE bench.upsert (k INT PRIMARY KEY, v INT)`) 456 457 // Upsert in Cockroach doesn't let you conflict the same row twice in one 458 // statement (fwiw, neither does Postgres), so build one statement that 459 // inserts half the values requested by `count` followed by a statement that 460 // updates each of the values just inserted. This also weighs the benchmark 461 // 50/50 for inserts vs updates. 462 var upsertBuf bytes.Buffer 463 upsertBuf.WriteString(`UPSERT INTO bench.upsert VALUES `) 464 for j := 0; j < count; j += 2 { 465 if j > 0 { 466 upsertBuf.WriteString(`, `) 467 } 468 fmt.Fprintf(&upsertBuf, "($1+%d, unique_rowid())", j) 469 } 470 471 b.ResetTimer() 472 key := 0 473 for i := 0; i < b.N; i++ { 474 db.Exec(b, upsertBuf.String(), key) 475 db.Exec(b, upsertBuf.String(), key) 476 key += count 477 } 478 b.StopTimer() 479 } 480 481 // runBenchmarkDelete benchmarks deleting count rows from a table. 482 func runBenchmarkDelete(b *testing.B, db *sqlutils.SQLRunner, rows int) { 483 defer func() { 484 db.Exec(b, `DROP TABLE IF EXISTS bench.delete`) 485 }() 486 487 db.Exec(b, `CREATE TABLE bench.delete (k INT PRIMARY KEY, v1 INT, v2 INT, v3 INT)`) 488 489 b.ResetTimer() 490 var buf bytes.Buffer 491 for i := 0; i < b.N; i++ { 492 b.StopTimer() 493 buf.Reset() 494 buf.WriteString(`INSERT INTO bench.delete VALUES `) 495 for j := 0; j < rows; j++ { 496 if j > 0 { 497 buf.WriteString(", ") 498 } 499 fmt.Fprintf(&buf, "(%d, %d, %d, %d)", j, j, j, j) 500 } 501 db.Exec(b, buf.String()) 502 b.StartTimer() 503 504 buf.Reset() 505 buf.WriteString(`DELETE FROM bench.delete WHERE k IN (`) 506 for j := 0; j < rows; j++ { 507 if j > 0 { 508 buf.WriteString(", ") 509 } 510 fmt.Fprintf(&buf, `%d`, j) 511 } 512 buf.WriteString(`)`) 513 db.Exec(b, buf.String()) 514 } 515 b.StopTimer() 516 } 517 518 // runBenchmarkScan benchmarks scanning a table containing count rows. 519 func runBenchmarkScan(b *testing.B, db *sqlutils.SQLRunner, count int, limit int) { 520 defer func() { 521 db.Exec(b, `DROP TABLE IF EXISTS bench.scan`) 522 }() 523 524 db.Exec(b, `CREATE TABLE bench.scan (k INT PRIMARY KEY)`) 525 526 var buf bytes.Buffer 527 buf.WriteString(`INSERT INTO bench.scan VALUES `) 528 for i := 0; i < count; i++ { 529 if i > 0 { 530 buf.WriteString(", ") 531 } 532 fmt.Fprintf(&buf, "(%d)", i) 533 } 534 db.Exec(b, buf.String()) 535 536 query := `SELECT * FROM bench.scan` 537 if limit != 0 { 538 query = fmt.Sprintf(`%s LIMIT %d`, query, limit) 539 } 540 541 b.ResetTimer() 542 for i := 0; i < b.N; i++ { 543 rows := db.Query(b, query) 544 n := 0 545 for rows.Next() { 546 n++ 547 } 548 rows.Close() 549 if err := rows.Err(); err != nil { 550 b.Fatal(err) 551 } 552 expected := count 553 if limit != 0 && limit < expected { 554 expected = limit 555 } 556 if n != expected { 557 b.Fatalf("unexpected result count: %d (expected %d)", n, expected) 558 } 559 } 560 b.StopTimer() 561 } 562 563 func BenchmarkScan(b *testing.B) { 564 if testing.Short() { 565 b.Skip("short flag") 566 } 567 defer log.Scope(b).Close(b) 568 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 569 for _, count := range []int{1, 10, 100, 1000, 10000} { 570 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 571 for _, limit := range []int{0, 1, 10, 100} { 572 b.Run(fmt.Sprintf("limit=%d", limit), func(b *testing.B) { 573 runBenchmarkScan(b, db, count, limit) 574 }) 575 } 576 }) 577 } 578 }) 579 } 580 581 // runBenchmarkScanFilter benchmarks scanning (w/filter) from a table containing count1 * count2 rows. 582 func runBenchmarkScanFilter( 583 b *testing.B, db *sqlutils.SQLRunner, count1, count2 int, limit int, filter string, 584 ) { 585 defer func() { 586 db.Exec(b, `DROP TABLE IF EXISTS bench.scan2`) 587 }() 588 589 db.Exec(b, `CREATE TABLE bench.scan2 (a INT, b INT, PRIMARY KEY (a, b))`) 590 591 var buf bytes.Buffer 592 buf.WriteString(`INSERT INTO bench.scan2 VALUES `) 593 for i := 0; i < count1; i++ { 594 for j := 0; j < count2; j++ { 595 if i+j > 0 { 596 buf.WriteString(", ") 597 } 598 fmt.Fprintf(&buf, "(%d, %d)", i, j) 599 } 600 } 601 db.Exec(b, buf.String()) 602 603 query := fmt.Sprintf(`SELECT * FROM bench.scan2 WHERE %s`, filter) 604 if limit != 0 { 605 query += fmt.Sprintf(` LIMIT %d`, limit) 606 } 607 608 b.ResetTimer() 609 for i := 0; i < b.N; i++ { 610 rows := db.Query(b, query) 611 n := 0 612 for rows.Next() { 613 n++ 614 } 615 rows.Close() 616 if err := rows.Err(); err != nil { 617 b.Fatal(err) 618 } 619 } 620 b.StopTimer() 621 } 622 623 func BenchmarkScanFilter(b *testing.B) { 624 defer log.Scope(b).Close(b) 625 const count1 = 25 626 const count2 = 400 627 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 628 b.Run(fmt.Sprintf("count1=%d", count1), func(b *testing.B) { 629 b.Run(fmt.Sprintf("count2=%d", count2), func(b *testing.B) { 630 for _, limit := range []int{1, 10, 50} { 631 b.Run(fmt.Sprintf("limit=%d", limit), func(b *testing.B) { 632 runBenchmarkScanFilter( 633 b, db, count1, count2, limit, 634 `a IN (1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20, 21, 23) AND b < 10*a`, 635 ) 636 }) 637 } 638 639 }) 640 }) 641 }) 642 } 643 644 func runBenchmarkInterleavedSelect(b *testing.B, db *sqlutils.SQLRunner, count int) { 645 defer func() { 646 db.Exec(b, `DROP TABLE IF EXISTS bench.interleaved_select2`) 647 db.Exec(b, `DROP TABLE IF EXISTS bench.interleaved_select1`) 648 }() 649 650 db.Exec(b, `CREATE TABLE bench.interleaved_select1 (a INT PRIMARY KEY, b INT)`) 651 db.Exec(b, `CREATE TABLE bench.interleaved_select2 (c INT PRIMARY KEY, d INT) INTERLEAVE IN PARENT interleaved_select1 (c)`) 652 653 const interleaveFreq = 4 654 655 var buf1 bytes.Buffer 656 var buf2 bytes.Buffer 657 buf1.WriteString(`INSERT INTO bench.interleaved_select1 VALUES `) 658 buf2.WriteString(`INSERT INTO bench.interleaved_select2 VALUES `) 659 for i := 0; i < count; i++ { 660 if i > 0 { 661 buf1.WriteString(", ") 662 } 663 fmt.Fprintf(&buf1, "(%d, %d)", i, i) 664 if i%interleaveFreq == 0 { 665 if i > 0 { 666 buf2.WriteString(", ") 667 } 668 fmt.Fprintf(&buf2, "(%d, %d)", i, i) 669 } 670 } 671 db.Exec(b, buf1.String()) 672 db.Exec(b, buf2.String()) 673 674 query := `SELECT * FROM bench.interleaved_select1 is1 INNER JOIN bench.interleaved_select2 is2 on is1.a = is2.c` 675 676 b.ResetTimer() 677 for i := 0; i < b.N; i++ { 678 rows := db.Query(b, query) 679 n := 0 680 for rows.Next() { 681 n++ 682 } 683 rows.Close() 684 if err := rows.Err(); err != nil { 685 b.Fatal(err) 686 } 687 expected := (count + interleaveFreq - 1) / interleaveFreq 688 if n != expected { 689 b.Fatalf("unexpected result count: %d (expected %d)", n, expected) 690 } 691 } 692 b.StopTimer() 693 } 694 695 func runBenchmarkInterleavedFK(b *testing.B, db *sqlutils.SQLRunner, count int) { 696 defer func() { 697 db.Exec(b, `DROP TABLE IF EXISTS bench.parent CASCADE; DROP TABLE IF EXISTS bench.child CASCADE`) 698 }() 699 700 db.Exec(b, ` 701 CREATE TABLE bench.parent (a INT PRIMARY KEY); 702 INSERT INTO bench.parent VALUES(0); 703 CREATE TABLE bench.child 704 (a INT REFERENCES bench.parent(a), 705 b INT, PRIMARY KEY(a, b)) 706 INTERLEAVE IN PARENT bench.parent (a) 707 `) 708 709 b.ResetTimer() 710 var buf bytes.Buffer 711 val := 0 712 for i := 0; i < b.N; i++ { 713 buf.Reset() 714 buf.WriteString(`INSERT INTO bench.child VALUES `) 715 for j := 0; j < count; j++ { 716 if j > 0 { 717 buf.WriteString(", ") 718 } 719 fmt.Fprintf(&buf, "(0, %d)", val) 720 val++ 721 } 722 db.Exec(b, buf.String()) 723 } 724 } 725 726 // runBenchmarkOrderBy benchmarks scanning a table and sorting the results. 727 func runBenchmarkOrderBy( 728 b *testing.B, db *sqlutils.SQLRunner, count int, limit int, distinct bool, 729 ) { 730 defer func() { 731 db.Exec(b, `DROP TABLE IF EXISTS bench.sort`) 732 }() 733 734 db.Exec(b, `CREATE TABLE bench.sort (k INT PRIMARY KEY, v INT, w INT)`) 735 736 var buf bytes.Buffer 737 buf.WriteString(`INSERT INTO bench.sort VALUES `) 738 for i := 0; i < count; i++ { 739 if i > 0 { 740 buf.WriteString(", ") 741 } 742 fmt.Fprintf(&buf, "(%d, %d, %d)", i, i%(count*4/limit), i%2) 743 } 744 db.Exec(b, buf.String()) 745 746 var dist string 747 if distinct { 748 dist = `DISTINCT ` 749 } 750 query := fmt.Sprintf(`SELECT %sv, w, k FROM bench.sort`, dist) 751 if limit != 0 { 752 query = fmt.Sprintf(`%s ORDER BY v DESC, w ASC, k DESC LIMIT %d`, query, limit) 753 } 754 755 b.ResetTimer() 756 for i := 0; i < b.N; i++ { 757 rows := db.Query(b, query) 758 n := 0 759 for rows.Next() { 760 n++ 761 } 762 rows.Close() 763 if err := rows.Err(); err != nil { 764 b.Fatal(err) 765 } 766 expected := count 767 if limit != 0 { 768 expected = limit 769 } 770 if n != expected { 771 b.Fatalf("unexpected result count: %d (expected %d)", n, expected) 772 } 773 } 774 b.StopTimer() 775 } 776 777 func BenchmarkOrderBy(b *testing.B) { 778 if testing.Short() { 779 b.Skip("short flag") 780 } 781 defer log.Scope(b).Close(b) 782 const count = 100000 783 const limit = 10 784 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 785 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 786 b.Run(fmt.Sprintf("limit=%d", limit), func(b *testing.B) { 787 for _, distinct := range []bool{false, true} { 788 b.Run(fmt.Sprintf("distinct=%t", distinct), func(b *testing.B) { 789 runBenchmarkOrderBy(b, db, count, limit, distinct) 790 }) 791 } 792 }) 793 }) 794 }) 795 } 796 797 func runBenchmarkTrackChoices(b *testing.B, db *sqlutils.SQLRunner, batchSize int) { 798 defer func() { 799 db.Exec(b, `DROP TABLE IF EXISTS bench.track_choices`) 800 }() 801 802 const numOptions = 10000 803 // The CREATE INDEX statements are separate in order to be compatible with 804 // Postgres. 805 const createStmt = ` 806 CREATE TABLE bench.track_choices ( 807 user_id bigint NOT NULL DEFAULT 0, 808 track_id bigint NOT NULL DEFAULT 0, 809 created_at timestamp NOT NULL, 810 PRIMARY KEY (user_id, track_id) 811 ); 812 CREATE INDEX user_created_at ON bench.track_choices (user_id, created_at); 813 CREATE INDEX track_created_at ON bench.track_choices (track_id, created_at); 814 ` 815 db.Exec(b, createStmt) 816 817 b.ResetTimer() 818 var buf bytes.Buffer 819 for i := 0; i < b.N; i += batchSize { 820 buf.Reset() 821 buf.WriteString(`INSERT INTO bench.track_choices VALUES `) 822 count := b.N - i 823 if count > batchSize { 824 count = batchSize 825 } 826 for j := 0; j < count; j++ { 827 if j > 0 { 828 buf.WriteString(", ") 829 } 830 fmt.Fprintf(&buf, "(%d, %d, now())", rand.Int63(), rand.Int63n(numOptions)) 831 } 832 db.Exec(b, buf.String()) 833 } 834 b.StopTimer() 835 } 836 837 // Benchmark inserting distinct rows in batches where the min and max rows in 838 // separate batches overlap. This stresses the spanlatch manager implementation 839 // and verifies that we're allowing parallel execution of commands where possible. 840 func runBenchmarkInsertDistinct(b *testing.B, db *sqlutils.SQLRunner, numUsers int) { 841 defer func() { 842 db.Exec(b, `DROP TABLE IF EXISTS bench.insert_distinct`) 843 }() 844 845 const schema = ` 846 CREATE TABLE bench.insert_distinct ( 847 articleID INT, 848 userID INT, 849 uniqueID INT DEFAULT unique_rowid(), 850 PRIMARY KEY (articleID, userID, uniqueID)) 851 ` 852 db.Exec(b, schema) 853 854 b.ResetTimer() 855 856 errChan := make(chan error) 857 858 var count int64 859 for i := 0; i < numUsers; i++ { 860 go func(i int) { 861 errChan <- func() error { 862 var buf bytes.Buffer 863 864 rnd := rand.New(rand.NewSource(int64(i))) 865 // Article IDs are chosen from a zipf distribution. These values select 866 // articleIDs that are mostly <10000. The parameters were experimentally 867 // determined, but somewhat arbitrary. 868 zipf := rand.NewZipf(rnd, 2, 10000, 100000) 869 870 for { 871 n := atomic.AddInt64(&count, 1) 872 if int(n) >= b.N { 873 return nil 874 } 875 876 // Insert between [1,100] articles in a batch. 877 numArticles := 1 + rnd.Intn(100) 878 buf.Reset() 879 buf.WriteString(`INSERT INTO bench.insert_distinct VALUES `) 880 for j := 0; j < numArticles; j++ { 881 if j > 0 { 882 buf.WriteString(", ") 883 } 884 fmt.Fprintf(&buf, "(%d, %d)", zipf.Uint64(), n) 885 } 886 887 if _, err := db.DB.ExecContext(context.Background(), buf.String()); err != nil { 888 return err 889 } 890 } 891 }() 892 }(i) 893 } 894 895 for i := 0; i < numUsers; i++ { 896 if err := <-errChan; err != nil { 897 b.Fatal(err) 898 } 899 } 900 901 b.StopTimer() 902 } 903 904 const wideTableSchema = `CREATE TABLE bench.widetable ( 905 f1 INT, f2 INT, f3 INT, f4 INT, f5 INT, f6 INT, f7 INT, f8 INT, f9 INT, f10 INT, 906 f11 TEXT, f12 TEXT, f13 TEXT, f14 TEXT, f15 TEXT, f16 TEXT, f17 TEXT, f18 TEXT, f19 TEXT, 907 f20 TEXT, 908 PRIMARY KEY (f1, f2, f3) 909 )` 910 911 func insertIntoWideTable( 912 b *testing.B, 913 buf bytes.Buffer, 914 i, count, bigColumnBytes int, 915 s *rand.Rand, 916 db *sqlutils.SQLRunner, 917 ) { 918 buf.WriteString(`INSERT INTO bench.widetable VALUES `) 919 for j := 0; j < count; j++ { 920 if j != 0 { 921 if j%3 == 0 { 922 buf.WriteString(`;`) 923 db.Exec(b, buf.String()) 924 buf.Reset() 925 buf.WriteString(`INSERT INTO bench.widetable VALUES `) 926 } else { 927 buf.WriteString(`,`) 928 } 929 } 930 buf.WriteString(`(`) 931 for k := 0; k < 20; k++ { 932 if k != 0 { 933 buf.WriteString(`,`) 934 } 935 if k < 10 { 936 fmt.Fprintf(&buf, "%d", i*count+j) 937 } else if k < 19 { 938 fmt.Fprintf(&buf, "'%d'", i*count+j) 939 } else { 940 fmt.Fprintf(&buf, "'%x'", randutil.RandBytes(s, bigColumnBytes)) 941 } 942 } 943 buf.WriteString(`)`) 944 } 945 buf.WriteString(`;`) 946 db.Exec(b, buf.String()) 947 buf.Reset() 948 } 949 950 // runBenchmarkWideTable measures performance on a table with a large number of 951 // columns (20), half of which are fixed size, half of which are variable sized 952 // and presumed small. 1 of the presumed small columns is actually large. 953 // 954 // This benchmark tracks the tradeoff in column family allocation at table 955 // creation. Fewer column families mean fewer kv entries, which is faster. But 956 // fewer column families mean updates are less targeted, which means large 957 // columns in a family may be copied unnecessarily when it's updated. Perfect 958 // knowledge of traffic patterns can result in much better heuristics, but we 959 // don't have that information at table creation. 960 func runBenchmarkWideTable(b *testing.B, db *sqlutils.SQLRunner, count int, bigColumnBytes int) { 961 defer func() { 962 db.Exec(b, `DROP TABLE IF EXISTS bench.widetable`) 963 }() 964 965 db.Exec(b, wideTableSchema) 966 967 s := rand.New(rand.NewSource(5432)) 968 969 var buf bytes.Buffer 970 971 b.ResetTimer() 972 for i := 0; i < b.N; i++ { 973 buf.Reset() 974 975 insertIntoWideTable(b, buf, i, count, bigColumnBytes, s, db) 976 977 // These are all updates, but ON CONFLICT DO UPDATE is (much!) faster 978 // because it can do blind writes. 979 buf.WriteString(`INSERT INTO bench.widetable (f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) VALUES `) 980 for j := 0; j < count; j++ { 981 if j != 0 { 982 buf.WriteString(`,`) 983 } 984 fmt.Fprintf(&buf, `(%d,%d,%d,`, i*count+j, i*count+j, i*count+j) 985 for k := 0; k < 7; k++ { 986 if k != 0 { 987 buf.WriteString(`,`) 988 } 989 fmt.Fprintf(&buf, "%d", s.Intn(j+1)) 990 } 991 buf.WriteString(`)`) 992 } 993 buf.WriteString(`ON CONFLICT (f1,f2,f3) DO UPDATE SET f4=excluded.f4,f5=excluded.f5,f6=excluded.f6,f7=excluded.f7,f8=excluded.f8,f9=excluded.f9,f10=excluded.f10;`) 994 995 buf.WriteString(`DELETE FROM bench.widetable WHERE f1 in (`) 996 for j := 0; j < count; j++ { 997 if j != 0 { 998 buf.WriteString(`,`) 999 } 1000 fmt.Fprintf(&buf, "%d", j) 1001 } 1002 buf.WriteString(`);`) 1003 1004 db.Exec(b, buf.String()) 1005 } 1006 b.StopTimer() 1007 } 1008 1009 // BenchmarkVecSkipScan benchmarks the vectorized engine's performance 1010 // when skipping unneeded key values in the decoding process. 1011 func BenchmarkVecSkipScan(b *testing.B) { 1012 benchmarkCockroach(b, func(b *testing.B, db *sqlutils.SQLRunner) { 1013 create := ` 1014 CREATE TABLE bench.scan( 1015 x INT, y INT, z INT, 1016 a INT, w INT, v INT, 1017 PRIMARY KEY (x, y, z, a, w, v) 1018 ) 1019 ` 1020 db.Exec(b, create) 1021 const count = 1000 1022 for i := 0; i < count; i++ { 1023 db.Exec( 1024 b, 1025 fmt.Sprintf( 1026 "INSERT INTO bench.scan VALUES (%d, %d, %d, %d, %d, %d)", 1027 i, i, i, i, i, i, 1028 ), 1029 ) 1030 } 1031 b.ResetTimer() 1032 b.Run("Bench scan with skip", func(b *testing.B) { 1033 for i := 0; i < b.N; i++ { 1034 db.Exec(b, `SET vectorize=on; SELECT y FROM bench.scan`) 1035 } 1036 }) 1037 }) 1038 } 1039 1040 func BenchmarkWideTable(b *testing.B) { 1041 if testing.Short() { 1042 b.Skip("short flag") 1043 } 1044 defer log.Scope(b).Close(b) 1045 const count = 10 1046 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 1047 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 1048 for _, bigColumnBytes := range []int{10, 100, 1000, 10000, 100000, 1000000} { 1049 b.Run(fmt.Sprintf("bigColumnBytes=%d", bigColumnBytes), func(b *testing.B) { 1050 runBenchmarkWideTable(b, db, count, bigColumnBytes) 1051 }) 1052 } 1053 }) 1054 }) 1055 } 1056 1057 func BenchmarkWideTableIgnoreColumns(b *testing.B) { 1058 if testing.Short() { 1059 b.Skip("short flag") 1060 } 1061 defer log.Scope(b).Close(b) 1062 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 1063 db.Exec(b, wideTableSchema) 1064 var buf bytes.Buffer 1065 s := rand.New(rand.NewSource(5432)) 1066 insertIntoWideTable(b, buf, 0, 10000, 10, s, db) 1067 1068 b.ResetTimer() 1069 1070 for i := 0; i < b.N; i++ { 1071 db.Exec(b, "SELECT count(*) FROM bench.widetable WHERE f4 < 10") 1072 } 1073 }) 1074 } 1075 1076 // BenchmarkPlanning runs some queries on an empty table. The purpose is to 1077 // benchmark (and get memory allocation statistics for) the planning process. 1078 func BenchmarkPlanning(b *testing.B) { 1079 if testing.Short() { 1080 b.Skip("short flag") 1081 } 1082 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 1083 db.Exec(b, `CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT, INDEX(b), UNIQUE INDEX(c))`) 1084 1085 queries := []string{ 1086 `SELECT * FROM abc`, 1087 `SELECT * FROM abc WHERE a > 5 ORDER BY a`, 1088 `SELECT * FROM abc WHERE b = 5`, 1089 `SELECT * FROM abc WHERE b = 5 ORDER BY a`, 1090 `SELECT * FROM abc WHERE c = 5`, 1091 `SELECT * FROM abc JOIN abc AS abc2 ON abc.a = abc2.a`, 1092 } 1093 for _, q := range queries { 1094 b.Run(q, func(b *testing.B) { 1095 for i := 0; i < b.N; i++ { 1096 db.Exec(b, q) 1097 } 1098 }) 1099 } 1100 }) 1101 } 1102 1103 // BenchmarkIndexJoin measure an index-join with 1000 rows. 1104 func BenchmarkIndexJoin(b *testing.B) { 1105 defer log.Scope(b).Close(b) 1106 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 1107 // The table will have an extra column not contained in the index to force a 1108 // join with the PK. 1109 create := ` 1110 CREATE TABLE tidx ( 1111 k INT NOT NULL, 1112 v INT NULL, 1113 extra STRING NULL, 1114 CONSTRAINT "primary" PRIMARY KEY (k ASC), 1115 INDEX idx (v ASC), 1116 FAMILY "primary" (k, v, extra) 1117 ) 1118 ` 1119 // We'll insert 1000 rows with random values below 1000 in the index. We'll 1120 // then query the index with a query that retrieves all the data (but the 1121 // optimizer doesn't know that). 1122 insert := "insert into tidx(k,v) select generate_series(1,1000), (random()*1000)::int" 1123 1124 db.Exec(b, create) 1125 db.Exec(b, insert) 1126 b.ResetTimer() 1127 1128 for i := 0; i < b.N; i++ { 1129 db.Exec(b, "select * from bench.tidx where v < 1000") 1130 } 1131 }) 1132 } 1133 1134 func BenchmarkSortJoinAggregation(b *testing.B) { 1135 defer log.Scope(b).Close(b) 1136 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 1137 tables := []struct { 1138 create string 1139 populate string 1140 }{ 1141 { 1142 create: ` 1143 CREATE TABLE abc ( 1144 a INT PRIMARY KEY, 1145 b INT, 1146 c FLOAT 1147 )`, 1148 populate: ` 1149 INSERT INTO abc SELECT generate_series(1,1000), (random()*1000)::int, random()::float`, 1150 }, 1151 { 1152 create: ` 1153 CREATE TABLE xyz ( 1154 x INT PRIMARY KEY, 1155 y INT, 1156 z FLOAT 1157 )`, 1158 populate: ` 1159 INSERT INTO xyz SELECT generate_series(1,1000), (random()*1000)::int, random()::float`, 1160 }, 1161 } 1162 1163 for _, table := range tables { 1164 db.Exec(b, table.create) 1165 db.Exec(b, table.populate) 1166 } 1167 1168 query := ` 1169 SELECT b, count(*) FROM 1170 (SELECT b, avg(c) FROM (SELECT b, c FROM abc ORDER BY b) GROUP BY b) 1171 JOIN 1172 (SELECT y, sum(z) FROM (SELECT y, z FROM xyz ORDER BY y) GROUP BY y) 1173 ON b = y 1174 GROUP BY b 1175 ORDER BY b` 1176 1177 b.ResetTimer() 1178 for i := 0; i < b.N; i++ { 1179 db.Exec(b, query) 1180 } 1181 }) 1182 } 1183 1184 func BenchmarkNameResolution(b *testing.B) { 1185 if testing.Short() { 1186 b.Skip("short flag") 1187 } 1188 defer log.Scope(b).Close(b) 1189 ForEachDB(b, func(b *testing.B, db *sqlutils.SQLRunner) { 1190 db.Exec(b, `CREATE TABLE namespace (k INT PRIMARY KEY, v INT)`) 1191 db.Exec(b, `INSERT INTO namespace VALUES(1, 2)`) 1192 1193 b.ResetTimer() 1194 1195 for i := 0; i < b.N; i++ { 1196 db.Exec(b, "SELECT * FROM namespace") 1197 } 1198 b.StopTimer() 1199 }) 1200 }