Skip to content

Commit

Permalink
Reuse the result channel in QueryContexxt
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
charlievieth committed Oct 27, 2024
1 parent 2ed6519 commit 34da3d3
Showing 1 changed file with 19 additions and 8 deletions.
27 changes: 19 additions & 8 deletions sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
}
Expand Down

0 comments on commit 34da3d3

Please sign in to comment.