Skip to content

Instantly share code, notes, and snippets.

@up1
Last active July 24, 2026 16:35
Show Gist options
  • Select an option

  • Save up1/7c51506a3d05ffa2cc55434bfa045eaf to your computer and use it in GitHub Desktop.

Select an option

Save up1/7c51506a3d05ffa2cc55434bfa045eaf to your computer and use it in GitHub Desktop.
Golang :: Compare JSON Serializer
// 1. SonicSerializer implements echo.JSONSerializer using bytedance/sonic
type SonicSerializer struct{}
// Serialize converts a Go object into a JSON byte slice and writes it to the context
func (s SonicSerializer) Serialize(c echo.Context, i interface{}, indent string) error {
enc := sonic.ConfigDefault.NewEncoder(c.Response())
if indent != "" {
enc.SetIndent("", indent)
}
return enc.Encode(i)
}
// Deserialize reads JSON from the request body and parses it into a Go object
func (s SonicSerializer) Deserialize(c echo.Context, i interface{}) error {
err := sonic.ConfigDefault.NewDecoder(c.Request().Body).Decode(i)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return nil
}
// 2. V2Serializer implements echo.JSONSerializer using the standard library's encoding/json
type V2Serializer struct{}
func (s *V2Serializer) Serialize(c echo.Context, i interface{}, indent string) error {
// Optional: Match Echo's internal indentation parameter if requested
if indent != "" {
return json.MarshalWrite(c.Response(), i, jsontext.WithIndent(indent))
}
return json.MarshalWrite(c.Response(), i)
}
func (s *V2Serializer) Deserialize(c echo.Context, i interface{}) error {
err := json.UnmarshalRead(c.Request().Body, i)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return nil
}
// User payload for testing
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
// Add 5 layers of nested structs to increase complexity
Profile struct {
Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
Zip string `json:"zip"`
Geo struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
} `json:"geo"`
} `json:"address"`
PhoneNumbers []struct {
Type string `json:"type"`
Number string `json:"number"`
} `json:"phone_numbers"`
} `json:"profile"`
}
func BenchmarkEchoJSON(b *testing.B) {
// 1. Setup Standard Echo
eStd := echo.New()
// 2. Setup Sonic Echo
eSonic := echo.New()
eSonic.JSONSerializer = &demo.SonicSerializer{}
// 3. Setup json v2 Echo
easyjson := echo.New()
easyjson.JSONSerializer = &demo.V2Serializer{}
// Target Handler
handler := func(c echo.Context) error {
u := new(User)
if err := c.Bind(u); err != nil {
return err
}
return c.JSON(http.StatusOK, u)
}
eStd.POST("/user", handler)
eSonic.POST("/user", handler)
easyjson.POST("/user", handler)
jsonPayload := []byte(`{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"profile": {
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345",
"geo": {
"lat": 37.7749,
"lng": -122.4194
}
},
"phone_numbers": [
{
"type": "home",
"number": "555-555-5555"
},
{
"type": "work",
"number": "555-555-5556"
}
]
}
}`)
// Run Standard Benchmark
b.Run("Standard_JSON", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req := httptest.NewRequest(http.MethodPost, "/user", bytes.NewReader(jsonPayload))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
eStd.ServeHTTP(rec, req)
}
})
// Run Sonic Benchmark
b.Run("Sonic_JSON", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req := httptest.NewRequest(http.MethodPost, "/user", bytes.NewReader(jsonPayload))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
eSonic.ServeHTTP(rec, req)
}
})
// Run json v2 Benchmark
b.Run("JSON_V2", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req := httptest.NewRequest(http.MethodPost, "/user", bytes.NewReader(jsonPayload))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
easyjson.ServeHTTP(rec, req)
}
})
}
$go version
go version go1.26.5 darwin/arm64
$go test -bench=. -benchmem -cpu=6
goos: darwin
goarch: arm64
pkg: demo
cpu: Apple M2 Max
BenchmarkEchoJSON/Standard_JSON-6 214736 5661 ns/op 9587 B/op 42 allocs/op
BenchmarkEchoJSON/Sonic_JSON-6 288771 3991 ns/op 11233 B/op 33 allocs/op
BenchmarkEchoJSON/JSON_V2-6 287110 4251 ns/op 7314 B/op 30 allocs/op
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment