mirror of
				https://github.com/glebarez/go-sqlite.git
				synced 2025-11-01 03:22:33 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			377 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2021 The Sqlite Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| package benchmark
 | |
| 
 | |
| /*
 | |
| this file contains benchmarks inspired by https://www.sqlite.org/speed.html
 | |
| */
 | |
| 
 | |
| import (
 | |
| 	"database/sql"
 | |
| 	"fmt"
 | |
| 	"math/rand"
 | |
| 	"testing"
 | |
| )
 | |
| 
 | |
| // corresponds to Test 1 from https://www.sqlite.org/speed.html
 | |
| func benchInsert(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db)
 | |
| 
 | |
| 	// measure from here
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	fillTestTable(db, b.N)
 | |
| }
 | |
| 
 | |
| // corresponds to Test 2 from https://www.sqlite.org/speed.html
 | |
| func benchInsertInTransaction(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db)
 | |
| 
 | |
| 	// measure from here
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	fillTestTableInTx(db, b.N)
 | |
| }
 | |
| 
 | |
| // corresponds to Test 3 from https://www.sqlite.org/speed.html
 | |
| func benchInsertIntoIndexed(b *testing.B, db *sql.DB) {
 | |
| 	// create test table with indexed column
 | |
| 	createTestTable(db, `c`)
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	fillTestTableInTx(db, b.N)
 | |
| }
 | |
| 
 | |
| // corresponds to Test 4 from https://www.sqlite.org/speed.html
 | |
| func benchSelectWithoutIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db)
 | |
| 
 | |
| 	// fill test table
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	// prepare statement for selection
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`SELECT count(*), avg(b) FROM %s WHERE b>=? AND b<?`, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	runInTransaction(db, func() {
 | |
| 		// exec many selects
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE b>=0 AND b<1000;
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE b>=100 AND b<1100;
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE b>=200 AND b<1200;
 | |
| 		// ...
 | |
| 		for i := 0; i < b.N; i++ {
 | |
| 			b := (i * 100) % maxGeneratedNum
 | |
| 			if _, err := stmt.Exec(b, b+1000); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // corresponds to Test 5 from https://www.sqlite.org/speed.html
 | |
| func benchSelectOnStringComparison(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db)
 | |
| 
 | |
| 	// fill test table
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	// prepare statement for selection
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`SELECT count(*), avg(b) FROM %s WHERE c LIKE ?`, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	runInTransaction(db, func() {
 | |
| 		// exec many selects
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%one%';
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%two%';
 | |
| 		// ...
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%ninety nine%';
 | |
| 		for i := 1; i <= b.N; i++ {
 | |
| 			// e.g. following will produce "... WHERE c LIKE '%seventy eight%' "
 | |
| 			likeCond := pronounceNum(uint32(i) % 100)
 | |
| 			if _, err := stmt.Exec(`%` + likeCond + `%`); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // corresponds to Test 6 from https://www.sqlite.org/speed.html
 | |
| func benchCreateIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db)
 | |
| 
 | |
| 	// fill test table
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	var (
 | |
| 		nameIdxA       = "iA"
 | |
| 		nameIdxB       = "iB"
 | |
| 		createIndStmtA = fmt.Sprintf(`CREATE INDEX %s ON %s(a)`, nameIdxA, testTableName)
 | |
| 		createIndStmtB = fmt.Sprintf(`CREATE INDEX %s ON %s(c)`, nameIdxB, testTableName)
 | |
| 		dropIndStmtA   = fmt.Sprintf(`DROP INDEX IF EXISTS %s`, nameIdxA)
 | |
| 		dropIndStmtB   = fmt.Sprintf(`DROP INDEX IF EXISTS %s`, nameIdxB)
 | |
| 	)
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	for i := 1; i <= b.N; i++ {
 | |
| 		// drop indexes from previous iteration (if any)
 | |
| 		b.StopTimer()
 | |
| 		mustExec(db, dropIndStmtA, dropIndStmtB)
 | |
| 		b.StartTimer()
 | |
| 
 | |
| 		// create indexes
 | |
| 		runInTransaction(db, func() {
 | |
| 			mustExec(db, createIndStmtA, createIndStmtB)
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // corresponds to Test 7 from https://www.sqlite.org/speed.html
 | |
| func benchSelectWithIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table with indexed field
 | |
| 	createTestTable(db, `b`)
 | |
| 
 | |
| 	// fill test table
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	// prepare statement for selection
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`SELECT count(*), avg(b) FROM %s WHERE b>=? AND b<?`, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	runInTransaction(db, func() {
 | |
| 		// exec many selects
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE b>=0 AND b<100;
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE b>=100 AND b<200;
 | |
| 		// SELECT count(*), avg(b) FROM t2 WHERE b>=200 AND b<300;
 | |
| 		for i := 0; i < b.N; i++ {
 | |
| 			b := (i * 100) % maxGeneratedNum
 | |
| 			if _, err := stmt.Exec(b, b+100); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| }
 | |
| 
 | |
| // corresponds to Test 8 from https://www.sqlite.org/speed.html
 | |
| func benchUpdateWithoutIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db)
 | |
| 
 | |
| 	// fill test table
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	// prepare statement
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`UPDATE %s SET b=b*2 WHERE a>=? AND a<?`, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	runInTransaction(db, func() {
 | |
| 		// exec many
 | |
| 		// UPDATE t1 SET b=b*2 WHERE a>=0 AND a<10;
 | |
| 		// UPDATE t1 SET b=b*2 WHERE a>=10 AND a<20;
 | |
| 		// UPDATE t1 SET b=b*2 WHERE a>=20 AND a<30;
 | |
| 		for i := 0; i < b.N; i++ {
 | |
| 			a := (i * 10) % testTableRowCount
 | |
| 			if _, err := stmt.Exec(a, a+10); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| }
 | |
| 
 | |
| // corresponds to Test 9 from https://www.sqlite.org/speed.html
 | |
| func benchUpdateWithIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db, `a`)
 | |
| 
 | |
| 	// fill test table
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	// prepare statement
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`UPDATE %s SET b=? WHERE a=?`, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	runInTransaction(db, func() {
 | |
| 		// exec many
 | |
| 		// UPDATE t2 SET b=468026 WHERE a=1;
 | |
| 		// UPDATE t2 SET b=121928 WHERE a=2;
 | |
| 		// ...
 | |
| 		for i := 0; i < b.N; i++ {
 | |
| 			if _, err := stmt.Exec(
 | |
| 				rand.Uint32(),         // b = ?
 | |
| 				i%testTableRowCount+1, // WHERE a=?
 | |
| 			); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // corresponds to Test 10 from https://www.sqlite.org/speed.html
 | |
| func benchUpdateTextWithIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db, `a`)
 | |
| 
 | |
| 	// fill test table
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	// prepare statement
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`UPDATE %s SET c=? WHERE a=?`, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 
 | |
| 	runInTransaction(db, func() {
 | |
| 		// exec many
 | |
| 		// UPDATE t2 SET c='one hundred forty eight thousand three hundred eighty two' WHERE a=1;
 | |
| 		// UPDATE t2 SET c='three hundred sixty six thousand five hundred two' WHERE a=2;
 | |
| 		for i := 0; i < b.N; i++ {
 | |
| 			// generate new random number-as-words for c
 | |
| 			if _, err := stmt.Exec(
 | |
| 				pronounceNum(uint32(rand.Int31n(maxGeneratedNum))), // SET c=?
 | |
| 				i%testTableRowCount+1,                              // WHERE a=?
 | |
| 			); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // corresponds to Test 11 from https://www.sqlite.org/speed.html
 | |
| func benchInsertFromSelect(b *testing.B, db *sql.DB) {
 | |
| 	// create source table
 | |
| 	createTestTable(db)
 | |
| 	fillTestTableInTx(db, testTableRowCount)
 | |
| 
 | |
| 	// create name for target table
 | |
| 	targetTableName := fmt.Sprintf("%s_copy", testTableName)
 | |
| 
 | |
| 	// need to create table here, otherwise prepared statement will give error
 | |
| 	createTestTableWithName(db, targetTableName)
 | |
| 
 | |
| 	// prepare statement
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`INSERT INTO %s SELECT b,a,c FROM %s`, targetTableName, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		// drop/create target table
 | |
| 		b.StopTimer()
 | |
| 		createTestTableWithName(db, targetTableName)
 | |
| 		b.StartTimer()
 | |
| 
 | |
| 		runInTransaction(db, func() {
 | |
| 			if _, err := stmt.Exec(); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // corresponds to Test 12 from https://www.sqlite.org/speed.html
 | |
| func benchDeleteWithoutIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table
 | |
| 	createTestTable(db)
 | |
| 
 | |
| 	// prepare statement for deletion
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`DELETE FROM %s WHERE c LIKE '%%fifty%%'`, testTableName))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		// drop/create target table
 | |
| 		b.StopTimer()
 | |
| 		fillTestTableInTx(db, testTableRowCount)
 | |
| 		b.StartTimer()
 | |
| 
 | |
| 		runInTransaction(db, func() {
 | |
| 			if _, err := stmt.Exec(); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // corresponds to Test 13 from https://www.sqlite.org/speed.html
 | |
| func benchDeleteWithIndex(b *testing.B, db *sql.DB) {
 | |
| 	// create test table with indexed column
 | |
| 	createTestTable(db, `a`)
 | |
| 
 | |
| 	// prepare statement for BIG deletion (nearly half of table rows)
 | |
| 	stmt, err := db.Prepare(fmt.Sprintf(`DELETE FROM %s WHERE a>%d AND a<%d`, testTableName, 10, testTableRowCount/2))
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	defer stmt.Close()
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		// drop/create target table
 | |
| 		b.StopTimer()
 | |
| 		fillTestTableInTx(db, testTableRowCount)
 | |
| 		b.StartTimer()
 | |
| 
 | |
| 		runInTransaction(db, func() {
 | |
| 			if _, err := stmt.Exec(); err != nil {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // corresponds to Test 16 from https://www.sqlite.org/speed.html
 | |
| func benchDropTable(b *testing.B, db *sql.DB) {
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		b.StopTimer()
 | |
| 		createTestTable(db)
 | |
| 		fillTestTableInTx(db, testTableRowCount)
 | |
| 		b.StartTimer()
 | |
| 
 | |
| 		if _, err := db.Exec(fmt.Sprintf("DROP TABLE %s", testTableName)); err != nil {
 | |
| 			panic(err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | 
