Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve json unmarshalling performance #71

Merged
merged 4 commits into from
Jan 31, 2024

Conversation

camswords
Copy link
Collaborator

Summary

This pull request improves JSON unmarshalling performance by:

  • Inlining the Chromium error response into the response struct so that json.Unmarshal only needs to be called once per message, and
  • Replacing jsoniter with go-json as it is better maintained and seems to be more performant.

Benchmarks

The following test, benchmarked with the before and after versions of the code shows a 2.5x improvement on the sample data used. This is likely due to the reduced number of allocations.

goos: darwin
goarch: arm64
pkg: github.com/wirepair/gcd/v2
BenchmarkUnmarshalComputedStyleOld
BenchmarkUnmarshalComputedStyleOld-10    	   44449	     26598 ns/op	   10384 B/op	     551 allocs/op
BenchmarkUnmarshalComputedStyleNew
BenchmarkUnmarshalComputedStyleNew-10    	  111758	     10212 ns/op	    9760 B/op	     110 allocs/op

Test:

package gcd_test

import (
	"context"
	"github.com/wirepair/gcd/v2/gcdapi"
	"github.com/wirepair/gcd/v2/gcdmessage"
	"testing"
	"time"
)

func BenchmarkUnmarshalComputedStyleOld(b *testing.B) {
	var computedStyleJSON = []byte(`{"id":1015,"result":{"computedStyle":[{"name":"color","value":"rgb(255, 255, 255)"},{"name":"direction","value":"ltr"},{"name":"font-family","value":"\"Font Awesome 5 Free\""},{"name":"font-feature-settings","value":"normal"},{"name":"font-kerning","value":"auto"},{"name":"font-optical-sizing","value":"auto"},{"name":"font-palette","value":"normal"},{"name":"font-size","value":"22px"},{"name":"font-stretch","value":"100%"},{"name":"font-style","value":"normal"},{"name":"font-synthesis-small-caps","value":"auto"},{"name":"font-synthesis-style","value":"auto"},{"name":"font-synthesis-weight","value":"auto"},{"name":"font-variant-alternates","value":"normal"},{"name":"font-variant-caps","value":"normal"},{"name":"font-variant-east-asian","value":"normal"},{"name":"font-variant-ligatures","value":"normal"},{"name":"font-variant-numeric","value":"normal"},{"name":"font-variant-position","value":"normal"},{"name":"font-variation-settings","value":"normal"},{"name":"font-weight","value":"900"},{"name":"forced-color-adjust","value":"auto"},{"name":"text-orientation","value":"mixed"},{"name":"text-rendering","value":"auto"},{"name":"-webkit-font-smoothing","value":"antialiased"},{"name":"-webkit-locale","value":"\"en\""},{"name":"-webkit-text-orientation","value":"vertical-right"},{"name":"-webkit-writing-mode","value":"horizontal-tb"},{"name":"writing-mode","value":"horizontal-tb"},{"name":"zoom","value":"1"},{"name":"accent-color","value":"auto"},{"name":"align-content","value":"normal"},{"name":"align-items","value":"normal"},{"name":"align-self","value":"auto"},{"name":"alignment-baseline","value":"auto"},{"name":"all","value":""},{"name":"animation-composition","value":"replace"},{"name":"animation-delay","value":"0s"},{"name":"animation-direction","value":"normal"},{"name":"animation-duration","value":"0s"},{"name":"animation-fill-mode","value":"none"},{"name":"animation-iteration-count","value":"1"},{"name":"animation-name","value":"none"},{"name":"animation-play-state","value":"running"},{"name":"animation-range-end","value":"normal"},{"name":"animation-range-start","value":"normal"},{"name":"animation-timeline","value":"auto"},{"name":"animation-timing-function","value":"ease"},{"name":"app-region","value":"none"},{"name":"appearance","value":"none"},{"name":"aspect-ratio","value":"auto"},{"name":"backdrop-filter","value":"none"},{"name":"backface-visibility","value":"visible"},{"name":"background-attachment","value":"scroll"},{"name":"background-blend-mode","value":"normal"},{"name":"background-clip","value":"border-box"},{"name":"background-color","value":"rgba(0, 0, 0, 0)"},{"name":"background-image","value":"none"},{"name":"background-origin","value":"padding-box"},{"name":"background-position-x","value":"0%"},{"name":"background-position-y","value":"0%"},{"name":"background-repeat","value":"repeat"},{"name":"background-size","value":"auto"},{"name":"baseline-shift","value":"0px"},{"name":"baseline-source","value":"auto"},{"name":"block-size","value":"22px"},{"name":"border-block-end-color","value":"rgb(255, 255, 255)"},{"name":"border-block-end-style","value":"none"},{"name":"border-block-end-width","value":"0px"},{"name":"border-block-start-color","value":"rgb(255, 255, 255)"},{"name":"border-block-start-style","value":"none"},{"name":"border-block-start-width","value":"0px"},{"name":"border-bottom-color","value":"rgb(255, 255, 255)"},{"name":"border-bottom-left-radius","value":"0px"},{"name":"border-bottom-right-radius","value":"0px"},{"name":"border-bottom-style","value":"none"},{"name":"border-bottom-width","value":"0px"},{"name":"border-collapse","value":"separate"},{"name":"border-end-end-radius","value":"0px"},{"name":"border-end-start-radius","value":"0px"},{"name":"border-image-outset","value":"0"},{"name":"border-image-repeat","value":"stretch"},{"name":"border-image-slice","value":"100%"},{"name":"border-image-source","value":"none"},{"name":"border-image-width","value":"1"},{"name":"border-inline-end-color","value":"rgb(255, 255, 255)"},{"name":"border-inline-end-style","value":"none"},{"name":"border-inline-end-width","value":"0px"},{"name":"border-inline-start-color","value":"rgb(255, 255, 255)"},{"name":"border-inline-start-style","value":"none"},{"name":"border-inline-start-width","value":"0px"},{"name":"border-left-color","value":"rgb(255, 255, 255)"},{"name":"border-left-style","value":"none"},{"name":"border-left-width","value":"0px"},{"name":"border-right-color","value":"rgb(255, 255, 255)"},{"name":"border-right-style","value":"none"},{"name":"border-right-width","value":"0px"},{"name":"border-start-end-radius","value":"0px"},{"name":"border-start-start-radius","value":"0px"},{"name":"border-top-color","value":"rgb(255, 255, 255)"},{"name":"border-top-left-radius","value":"0px"},{"name":"border-top-right-radius","value":"0px"},{"name":"border-top-style","value":"none"},{"name":"border-top-width","value":"0px"},{"name":"bottom","value":"auto"},{"name":"box-shadow","value":"none"}]}}`)
	css := gcdapi.NewCSS(NewGCDFakeChromeTargeter(computedStyleJSON))
	ctx := context.Background()
	params := &gcdapi.CSSGetComputedStyleForNodeParams{NodeId: 1}

	results := make([][]*gcdapi.CSSCSSComputedStyleProperty, b.N)
	errors := make([]error, b.N)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		results[i], errors[i] = css.GetComputedStyleForNodeWithParams(ctx, params)
	}

	b.StopTimer()

	for i := 0; i < b.N; i++ {
		if errors[i] != nil {
			b.Fatalf("failed to parse message %v", errors[i].Error())
		}

		if len(results[i]) != 106 {
			b.Fatalf("expected 106 results, got %d", len(results[i]))
		}
	}
}

type GCDFakeChromeTargeter struct {
	message *gcdmessage.Message
}

func NewGCDFakeChromeTargeter(responseData []byte) *GCDFakeChromeTargeter {
	return &GCDFakeChromeTargeter{
		message: &gcdmessage.Message{
			ReplyCh: nil,
			Id:      0,
			Data:    responseData,
			Method:  "",
			Target:  nil,
		},
	}
}

func (t *GCDFakeChromeTargeter) GetId() int64 {
	return 0
}

func (t *GCDFakeChromeTargeter) GetApiTimeout() time.Duration {
	return 0
}

func (t *GCDFakeChromeTargeter) GetSendCh() chan *gcdmessage.Message {
	return nil
}

func (t *GCDFakeChromeTargeter) GetDoneCh() chan struct{} {
	return make(chan struct{})
}

func (t *GCDFakeChromeTargeter) SendCustomReturn(_ context.Context, _ *gcdmessage.ParamRequest) (*gcdmessage.Message, error) {
	return t.message, nil
}

func (t *GCDFakeChromeTargeter) SendDefaultRequest(_ context.Context, _ *gcdmessage.ParamRequest) (*gcdmessage.ChromeResponse, error) {
	return nil, nil
}

v2/gcd.go Outdated
@@ -26,6 +26,7 @@ package gcd

import (
"context"
"encoding/json"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be "github.com/goccy/go-json" ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eeep! It should be, cheers. I'll fix it.

@wirepair wirepair merged commit 1102ae6 into wirepair:master Jan 31, 2024
1 check passed
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