go-sql-driver/mysql — pull request #68 by bradfitz (2013-05-02 15:45:48+00:00)
Title: Fewer memory allocations

Feel free to take all or part of this. I'll be away until Monday, at which time I can resume working on memory/CPU/contention in the MySQL driver and database/sql code.

Before: 603 B/op	24 allocs/op
After: 344 B/op	15 allocs/op

Fewer garbage collection events, since we're allocating less.

From:

package main

import (
"database/sql"
"sync"
"sync/atomic"
"testing"

_ "github.com/go-sql-driver/mysql"
)

const concurrencyLevel = 10

type TB interface {
Fatal(...interface{})
}

func check(tb TB, err error) {
if err != nil {
tb.Fatal(err)
}
}

func initDB(tb TB) *sql.DB {
checkRes := func(res sql.Result, err error) {
check(tb, err)
}
db, err := sql.Open("mysql", "root:r@/test?charset=utf8")
check(tb, err)
db.SetMaxIdleConns(concurrencyLevel)
checkRes(db.Exec("DROP TABLE IF EXISTS foo"))
checkRes(db.Exec("CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))"))
checkRes(db.Exec(`INSERT INTO foo VALUES (1, "one")`))
checkRes(db.Exec(`INSERT INTO foo VALUES (2, "two")`))
return db
}

func BenchmarkQuery(b *testing.B) {
b.StopTimer()
b.ReportAllocs()
db := initDB(b)
defer db.Close()

stmt, err := db.Prepare("SELECT val FROM foo WHERE id=?")
check(b, err)
defer stmt.Close()
b.StartTimer()

remain := int64(b.N)
var wg sync.WaitGroup
wg.Add(concurrencyLevel)
defer wg.Wait()
for i := 0; i < concurrencyLevel; i ++ {
go func() {
defer wg.Done()
for {
if atomic.AddInt64(&remain, -1) < 0 {
return
}
var got string
check(b, stmt.QueryRow(1).Scan(&got))
if got != "one" {
b.Errorf("query = %q; want one", got)
return
}
}
}()
}
}