mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-28 06:22:26 -05:00
[performance] use our own typed value context types for Value() key checking to improve performance (#4316)
Replaces our gtscontext package context.Context handling with our own typed contexts instead of `context.WithValue()`.
I wrote a quick benchmark consisting of (printlns to stop the compiler optimizing instructions away):
```golang
func BenchmarkContexts(b *testing.B) {
var receiving *gtsmodel.Account
var requesting *gtsmodel.Account
var otherIRIs []*url.URL
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
ctx := context.Background()
ctx = gtscontext.SetBarebones(ctx)
ctx = gtscontext.SetFastFail(ctx)
ctx = gtscontext.SetDryRun(ctx)
ctx = gtscontext.SetReceivingAccount(ctx, receiving)
ctx = gtscontext.SetRequestingAccount(ctx, requesting)
ctx = gtscontext.SetOtherIRIs(ctx, otherIRIs)
if !gtscontext.Barebones(ctx) {
println("oh no!")
}
if !gtscontext.IsFastfail(ctx) {
println("oh no!")
}
if !gtscontext.DryRun(ctx) {
println("oh no!")
}
if gtscontext.ReceivingAccount(ctx) != nil {
println("oh no!")
}
if gtscontext.RequestingAccount(ctx) != nil {
println("oh no!")
}
if len(gtscontext.OtherIRIs(ctx)) > 0 {
println("oh no!")
}
}
})
}
```
Before results:
```shell
kim @ ~/Projects/main/gts.4
--> go test -v -run=none -bench=.* -benchmem ./internal/gtscontext/ -count=5
goos: linux
goarch: amd64
pkg: code.superseriousbusiness.org/gotosocial/internal/gtscontext
cpu: AMD Ryzen 7 7840U w/ Radeon 780M Graphics
BenchmarkContexts
BenchmarkContexts-16 19050348 61.73 ns/op 288 B/op 6 allocs/op
BenchmarkContexts-16 18245772 61.71 ns/op 288 B/op 6 allocs/op
BenchmarkContexts-16 18853680 61.80 ns/op 288 B/op 6 allocs/op
BenchmarkContexts-16 18561621 62.67 ns/op 288 B/op 6 allocs/op
BenchmarkContexts-16 17819241 62.89 ns/op 288 B/op 6 allocs/op
PASS
ok code.superseriousbusiness.org/gotosocial/internal/gtscontext 6.112s
```
After results:
```shell
kim @ ~/Projects/main/gts.4
--> go test -v -run=none -bench=.* -benchmem ./internal/gtscontext/ -count=5
goos: linux
goarch: amd64
pkg: code.superseriousbusiness.org/gotosocial/internal/gtscontext
cpu: AMD Ryzen 7 7840U w/ Radeon 780M Graphics
BenchmarkContexts
BenchmarkContexts-16 28038618 41.67 ns/op 144 B/op 6 allocs/op
BenchmarkContexts-16 26537552 42.50 ns/op 144 B/op 6 allocs/op
BenchmarkContexts-16 26720542 42.39 ns/op 144 B/op 6 allocs/op
BenchmarkContexts-16 27408031 43.15 ns/op 144 B/op 6 allocs/op
BenchmarkContexts-16 25597026 44.02 ns/op 144 B/op 6 allocs/op
PASS
ok code.superseriousbusiness.org/gotosocial/internal/gtscontext 5.997s
```
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4316
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
parent
07f61a13de
commit
c8a4ce9a88
2 changed files with 200 additions and 12 deletions
|
|
@ -57,7 +57,16 @@ func DryRun(ctx context.Context) bool {
|
|||
// SetDryRun sets the "dryrun" context flag and returns this wrapped context.
|
||||
// See DryRun() for further information on the "dryrun" context flag.
|
||||
func SetDryRun(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, dryRunKey, struct{}{})
|
||||
return dryRunContext{ctx}
|
||||
}
|
||||
|
||||
type dryRunContext struct{ context.Context }
|
||||
|
||||
func (ctx dryRunContext) Value(key any) any {
|
||||
if key == dryRunKey {
|
||||
return struct{}{}
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// RequestID returns the request ID associated with context. This value will usually
|
||||
|
|
@ -72,7 +81,19 @@ func RequestID(ctx context.Context) string {
|
|||
// SetRequestID stores the given request ID value and returns the wrapped
|
||||
// context. See RequestID() for further information on the request ID value.
|
||||
func SetRequestID(ctx context.Context, id string) context.Context {
|
||||
return context.WithValue(ctx, requestIDKey, id)
|
||||
return requestIDContext{Context: ctx, requestID: id}
|
||||
}
|
||||
|
||||
type requestIDContext struct {
|
||||
context.Context
|
||||
requestID string
|
||||
}
|
||||
|
||||
func (ctx requestIDContext) Value(key any) any {
|
||||
if key == requestIDKey {
|
||||
return ctx.requestID
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// OutgoingPublicKeyID returns the public key ID (URI) associated with context. This
|
||||
|
|
@ -86,7 +107,19 @@ func OutgoingPublicKeyID(ctx context.Context) string {
|
|||
// SetOutgoingPublicKeyID stores the given public key ID value and returns the wrapped
|
||||
// context. See PublicKeyID() for further information on the public key ID value.
|
||||
func SetOutgoingPublicKeyID(ctx context.Context, id string) context.Context {
|
||||
return context.WithValue(ctx, outgoingPubKeyIDKey, id)
|
||||
return outgoingPublicKeyIDKeyContext{Context: ctx, pubKeyID: id}
|
||||
}
|
||||
|
||||
type outgoingPublicKeyIDKeyContext struct {
|
||||
context.Context
|
||||
pubKeyID string
|
||||
}
|
||||
|
||||
func (ctx outgoingPublicKeyIDKeyContext) Value(key any) any {
|
||||
if key == outgoingPubKeyIDKey {
|
||||
return ctx.pubKeyID
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// ReceivingAccount returns the local account who owns the resource being
|
||||
|
|
@ -99,7 +132,19 @@ func ReceivingAccount(ctx context.Context) *gtsmodel.Account {
|
|||
// SetReceivingAccount stores the given receiving account value and returns the wrapped
|
||||
// context. See ReceivingAccount() for further information on the receiving account value.
|
||||
func SetReceivingAccount(ctx context.Context, acct *gtsmodel.Account) context.Context {
|
||||
return context.WithValue(ctx, receivingAccountKey, acct)
|
||||
return receivingAccountContext{Context: ctx, account: acct}
|
||||
}
|
||||
|
||||
type receivingAccountContext struct {
|
||||
context.Context
|
||||
account *gtsmodel.Account
|
||||
}
|
||||
|
||||
func (ctx receivingAccountContext) Value(key any) any {
|
||||
if key == receivingAccountKey {
|
||||
return ctx.account
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// RequestingAccount returns the remote account interacting with a local
|
||||
|
|
@ -112,7 +157,19 @@ func RequestingAccount(ctx context.Context) *gtsmodel.Account {
|
|||
// SetRequestingAccount stores the given requesting account value and returns the wrapped
|
||||
// context. See RequestingAccount() for further information on the requesting account value.
|
||||
func SetRequestingAccount(ctx context.Context, acct *gtsmodel.Account) context.Context {
|
||||
return context.WithValue(ctx, requestingAccountKey, acct)
|
||||
return requestingAccountContext{Context: ctx, account: acct}
|
||||
}
|
||||
|
||||
type requestingAccountContext struct {
|
||||
context.Context
|
||||
account *gtsmodel.Account
|
||||
}
|
||||
|
||||
func (ctx requestingAccountContext) Value(key any) any {
|
||||
if key == requestingAccountKey {
|
||||
return ctx.account
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// OtherIRIs returns other IRIs which are involved in the current ActivityPub request
|
||||
|
|
@ -126,7 +183,19 @@ func OtherIRIs(ctx context.Context) []*url.URL {
|
|||
// SetOtherIRIs stores the given IRIs slice and returns the wrapped context.
|
||||
// See OtherIRIs() for further information on the IRIs slice value.
|
||||
func SetOtherIRIs(ctx context.Context, iris []*url.URL) context.Context {
|
||||
return context.WithValue(ctx, otherIRIsKey, iris)
|
||||
return otherIRIsContext{Context: ctx, iris: iris}
|
||||
}
|
||||
|
||||
type otherIRIsContext struct {
|
||||
context.Context
|
||||
iris []*url.URL
|
||||
}
|
||||
|
||||
func (ctx otherIRIsContext) Value(key any) any {
|
||||
if key == otherIRIsKey {
|
||||
return ctx.iris
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// HTTPClientSignFunc returns an httpclient signing function for the current client
|
||||
|
|
@ -139,7 +208,19 @@ func HTTPClientSignFunc(ctx context.Context) func(*http.Request) error {
|
|||
// SetHTTPClientSignFunc stores the given httpclient signing function and returns the wrapped
|
||||
// context. See HTTPClientSignFunc() for further information on the signing function value.
|
||||
func SetHTTPClientSignFunc(ctx context.Context, fn func(*http.Request) error) context.Context {
|
||||
return context.WithValue(ctx, httpClientSignFnKey, fn)
|
||||
return httpClientSignFuncContext{Context: ctx, signfn: fn}
|
||||
}
|
||||
|
||||
type httpClientSignFuncContext struct {
|
||||
context.Context
|
||||
signfn func(*http.Request) error
|
||||
}
|
||||
|
||||
func (ctx httpClientSignFuncContext) Value(key any) any {
|
||||
if key == httpClientSignFnKey {
|
||||
return ctx.signfn
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// HTTPSignatureVerifier returns an http signature verifier for the current ActivityPub
|
||||
|
|
@ -152,7 +233,19 @@ func HTTPSignatureVerifier(ctx context.Context) httpsig.VerifierWithOptions {
|
|||
// SetHTTPSignatureVerifier stores the given http signature verifier and returns the
|
||||
// wrapped context. See HTTPSignatureVerifier() for further information on the verifier value.
|
||||
func SetHTTPSignatureVerifier(ctx context.Context, verifier httpsig.VerifierWithOptions) context.Context {
|
||||
return context.WithValue(ctx, httpSigVerifierKey, verifier)
|
||||
return httpSignatureVerifierContext{Context: ctx, verifier: verifier}
|
||||
}
|
||||
|
||||
type httpSignatureVerifierContext struct {
|
||||
context.Context
|
||||
verifier httpsig.VerifierWithOptions
|
||||
}
|
||||
|
||||
func (ctx httpSignatureVerifierContext) Value(key any) any {
|
||||
if key == httpSigVerifierKey {
|
||||
return ctx.verifier
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// HTTPSignature returns the http signature string
|
||||
|
|
@ -165,7 +258,19 @@ func HTTPSignature(ctx context.Context) string {
|
|||
// SetHTTPSignature stores the given http signature string and returns the wrapped
|
||||
// context. See HTTPSignature() for further information on the verifier value.
|
||||
func SetHTTPSignature(ctx context.Context, signature string) context.Context {
|
||||
return context.WithValue(ctx, httpSigKey, signature)
|
||||
return httpSignatureContext{Context: ctx, signature: signature}
|
||||
}
|
||||
|
||||
type httpSignatureContext struct {
|
||||
context.Context
|
||||
signature string
|
||||
}
|
||||
|
||||
func (ctx httpSignatureContext) Value(key any) any {
|
||||
if key == httpSigKey {
|
||||
return ctx.signature
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// HTTPSignaturePubKeyID returns the public key id of the http signature
|
||||
|
|
@ -178,7 +283,19 @@ func HTTPSignaturePubKeyID(ctx context.Context) *url.URL {
|
|||
// SetHTTPSignaturePubKeyID stores the given http signature public key id and returns
|
||||
// the wrapped context. See HTTPSignaturePubKeyID() for further information on the value.
|
||||
func SetHTTPSignaturePubKeyID(ctx context.Context, pubKeyID *url.URL) context.Context {
|
||||
return context.WithValue(ctx, httpSigPubKeyIDKey, pubKeyID)
|
||||
return httpSigPubKeyIDContext{Context: ctx, pubKeyID: pubKeyID}
|
||||
}
|
||||
|
||||
type httpSigPubKeyIDContext struct {
|
||||
context.Context
|
||||
pubKeyID *url.URL
|
||||
}
|
||||
|
||||
func (ctx httpSigPubKeyIDContext) Value(key any) any {
|
||||
if key == httpSigPubKeyIDKey {
|
||||
return ctx.pubKeyID
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// IsFastFail returns whether the "fastfail" context key has been set. This
|
||||
|
|
@ -192,7 +309,16 @@ func IsFastfail(ctx context.Context) bool {
|
|||
// SetFastFail sets the "fastfail" context flag and returns this wrapped context.
|
||||
// See IsFastFail() for further information on the "fastfail" context flag.
|
||||
func SetFastFail(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, fastFailKey, struct{}{})
|
||||
return fastFailContext{ctx}
|
||||
}
|
||||
|
||||
type fastFailContext struct{ context.Context }
|
||||
|
||||
func (ctx fastFailContext) Value(key any) any {
|
||||
if key == fastFailKey {
|
||||
return struct{}{}
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// Barebones returns whether the "barebones" context key has been set. This
|
||||
|
|
@ -206,5 +332,14 @@ func Barebones(ctx context.Context) bool {
|
|||
// SetBarebones sets the "barebones" context flag and returns this wrapped context.
|
||||
// See Barebones() for further information on the "barebones" context flag.
|
||||
func SetBarebones(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, barebonesKey, struct{}{})
|
||||
return barebonesContext{ctx}
|
||||
}
|
||||
|
||||
type barebonesContext struct{ context.Context }
|
||||
|
||||
func (ctx barebonesContext) Value(key any) any {
|
||||
if key == barebonesKey {
|
||||
return struct{}{}
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
|
|
|||
53
internal/gtscontext/context_test.go
Normal file
53
internal/gtscontext/context_test.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package gtscontext_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
func BenchmarkContexts(b *testing.B) {
|
||||
var receiving *gtsmodel.Account
|
||||
var requesting *gtsmodel.Account
|
||||
var otherIRIs []*url.URL
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
ctx := context.Background()
|
||||
|
||||
ctx = gtscontext.SetBarebones(ctx)
|
||||
ctx = gtscontext.SetFastFail(ctx)
|
||||
ctx = gtscontext.SetDryRun(ctx)
|
||||
ctx = gtscontext.SetReceivingAccount(ctx, receiving)
|
||||
ctx = gtscontext.SetRequestingAccount(ctx, requesting)
|
||||
ctx = gtscontext.SetOtherIRIs(ctx, otherIRIs)
|
||||
|
||||
if !gtscontext.Barebones(ctx) {
|
||||
println("oh no!")
|
||||
}
|
||||
|
||||
if !gtscontext.IsFastfail(ctx) {
|
||||
println("oh no!")
|
||||
}
|
||||
|
||||
if !gtscontext.DryRun(ctx) {
|
||||
println("oh no!")
|
||||
}
|
||||
|
||||
if gtscontext.ReceivingAccount(ctx) != nil {
|
||||
println("oh no!")
|
||||
}
|
||||
|
||||
if gtscontext.RequestingAccount(ctx) != nil {
|
||||
println("oh no!")
|
||||
}
|
||||
|
||||
if len(gtscontext.OtherIRIs(ctx)) > 0 {
|
||||
println("oh no!")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue