Skip to content

Conversation

@ncruces
Copy link
Owner

@ncruces ncruces commented Mar 20, 2025

See: #247 (reply in thread)

This currently just adds a (micro) benchmark to measure the problem.

Results:

goos: linux
goarch: amd64
pkg: github.com/ncruces/go-sqlite3/driver
cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz
         │  1_off.txt  │          2_noctx.txt          │           3_nosched.txt            │               4_tip.txt                │              5_1000.txt              │
         │   sec/op    │   sec/op     vs base          │   sec/op     vs base               │    sec/op      vs base                 │    sec/op     vs base                │
_loop-12   807.8m ± 2%   817.3m ± 2%  ~ (p=0.190 n=10)   828.9m ± 2%  +2.61% (p=0.015 n=10)   2084.9m ± 13%  +158.09% (p=0.000 n=10)   897.4m ± 10%  +11.08% (p=0.000 n=10)

Interpreting results:

@ncruces
Copy link
Owner Author

ncruces commented Mar 20, 2025

For context...

This is the code to either optimize, or call less often:

go-sqlite3/conn.go

Lines 377 to 387 in d78239b

func progressCallback(ctx context.Context, mod api.Module, _ ptr_t) (interrupt int32) {
if c, ok := ctx.Value(connKey{}).(*Conn); ok {
if c.interrupt.Done() != nil {
runtime.Gosched()
}
if c.interrupt.Err() != nil {
interrupt = 1
}
}
return interrupt
}

Gosched is particularly painful, but was added in #208 to fix starvation breaking this test in wasip1.

@ncruces
Copy link
Owner Author

ncruces commented Mar 20, 2025

This is an interesting study:
simonw/datasette#1679 (comment)

I similarly measured this, and got a median of 434µs with a "tick" of 1000 VDBE instructions.
So maybe that's a better default.

I could do a double whammy and only doing Gosched every N calls. That would mean checking the context more regularly, but only yielding every few milliseconds.

@ncruces
Copy link
Owner Author

ncruces commented Mar 21, 2025

New implementation results:

goos: linux
goarch: amd64
pkg: github.com/ncruces/go-sqlite3/driver
cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz
         │    tip.txt    │               off.txt               │               new.txt               │
         │    sec/op     │   sec/op     vs base                │   sec/op     vs base                │
_loop-12   2084.9m ± 13%   807.8m ± 2%  -61.25% (p=0.000 n=10)   805.0m ± 2%  -61.39% (p=0.000 n=10)

@ncruces ncruces marked this pull request as ready for review March 21, 2025 10:58
@ncruces
Copy link
Owner Author

ncruces commented Mar 21, 2025

golang/go#64417 makes it sound that calling back every few hundreds of μs is OK; a value of 1000 should be OK. And since the big cost is Gosched we avoid doing that every time.

Combined, this means that we're calling Gosched every 16000 VDBE opcodes, or roughly every 10ms (compiler), probably around every 100ms (interpreter), regardless of there being a context. wasip1 seems OK with this, no regressions.

@ncruces ncruces changed the title Improve context performance. Improve context cancellation performance. Mar 21, 2025
@ncruces ncruces merged commit 35a2dbd into main Mar 21, 2025
17 checks passed
@ncruces ncruces deleted the benchctx branch March 21, 2025 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants