From 34da3d358ad0d20f5a64ae1c24bc242025eac525 Mon Sep 17 00:00:00 2001 From: Charlie Vieth Date: Sun, 27 Oct 2024 00:36:36 -0400 Subject: [PATCH] Reuse the result channel in QueryContexxt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces the number of allocs needed by half. goos: darwin goarch: arm64 pkg: github.com/mattn/go-sqlite3 cpu: Apple M1 Max │ 2.txt │ 1.txt │ │ sec/op │ sec/op vs base │ Scan10Cols-10 267.7µ ± 1% 262.7µ ± 2% -1.86% (p=0.023 n=10) │ 2.txt │ 1.txt │ │ B/op │ B/op vs base │ Scan10Cols-10 17.057Ki ± 0% 6.070Ki ± 0% -64.41% (p=0.000 n=10) │ 2.txt │ 1.txt │ │ allocs/op │ allocs/op vs base │ Scan10Cols-10 247.0 ± 0% 138.0 ± 1% -44.13% (p=0.000 n=10) --- sqlite3.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sqlite3.go b/sqlite3.go index a52d11fe..9ba4e97c 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -451,6 +451,7 @@ type SQLiteRows struct { cls bool closed bool ctx context.Context // no better alternative to pass context into Next() method + resultCh chan error } type functionInfo struct { @@ -2222,23 +2223,33 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { return io.EOF } - if rc.ctx.Done() == nil { + done := rc.ctx.Done() + if done == nil { return rc.nextSyncLocked(dest) } - resultCh := make(chan error) + + // Fast check if the Context is closed. + if err := rc.ctx.Err(); err != nil { + return err + } + if rc.resultCh == nil { + rc.resultCh = make(chan error, 1) + } go func() { - resultCh <- rc.nextSyncLocked(dest) + rc.resultCh <- rc.nextSyncLocked(dest) }() select { - case err := <-resultCh: + case err := <-rc.resultCh: return err - case <-rc.ctx.Done(): + case <-done: select { - case <-resultCh: // no need to interrupt + case <-rc.resultCh: // no need to interrupt default: - // this is still racy and can be no-op if executed between sqlite3_* calls in nextSyncLocked. + // this is still racy and can be no-op if executed between + // sqlite3_* calls in nextSyncLocked. C.sqlite3_interrupt(rc.s.c.db) - <-resultCh // ensure goroutine completed + + <-rc.resultCh // ensure goroutine completed } return rc.ctx.Err() }