commit 5491d961e463320a16ade349cde332346b18f1a6 Author: 4rkal <4rkal@horsefucker.org> Date: Thu Sep 19 20:54:54 2024 +0300 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..751ec28 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# goapiperf + +Benchmarks comparing jsoniter VS encoding/json and HTTP reuse + +## Results (on my own hardware): + +HTTP reuse: +``` +goos: linux +goarch: amd64 +pkg: apiSpeedImprove/httpReuse +cpu: AMD Ryzen 5 7640U w/ Radeon 760M Graphics +BenchmarkReusedHandler-12 2162 528014 ns/op +BenchmarkNewHandlerPerRequest-12 2329 497124 ns/op +PASS +ok apiSpeedImprove/httpReuse 7.193s +``` + +jsoniter: +``` +goos: linux +goarch: amd64 +pkg: apiSpeedImprove +cpu: AMD Ryzen 5 7640U w/ Radeon 760M Graphics +BenchmarkEncodingJSON-12 140383 7381 ns/op +BenchmarkJSONIter-12 974605 1217 ns/op +PASS +ok apiSpeedImprove 3.216s +``` + +Read the article [here](https://4rkal.com/posts/goapi) \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3b72172 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module apiSpeedImprove + +go 1.22.5 + +require ( + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ea794b2 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/httpReuse/httpReuse_test.go b/httpReuse/httpReuse_test.go new file mode 100644 index 0000000..8c54069 --- /dev/null +++ b/httpReuse/httpReuse_test.go @@ -0,0 +1,46 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func makeRequest(client *http.Client, url string) error { + resp, err := client.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + return nil +} + +func BenchmarkNewHttpClientEachRequest(b *testing.B) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK")) + })) + defer server.Close() + + for i := 0; i < b.N; i++ { + client := &http.Client{} + if err := makeRequest(client, server.URL); err != nil { + b.Fatalf("Request failed: %v", err) + } + } +} + +func BenchmarkReuseHttpClient(b *testing.B) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK")) + })) + defer server.Close() + + client := &http.Client{} // Reusable HTTP client + for i := 0; i < b.N; i++ { + if err := makeRequest(client, server.URL); err != nil { + b.Fatalf("Request failed: %v", err) + } + } +} diff --git a/jsoniter/jsonBenchmark_test.go b/jsoniter/jsonBenchmark_test.go new file mode 100644 index 0000000..70345f8 --- /dev/null +++ b/jsoniter/jsonBenchmark_test.go @@ -0,0 +1,83 @@ +package main + +import ( + "encoding/json" + "testing" + + jsoniter "github.com/json-iterator/go" +) + +var jsonData = []byte(`{ + "name": "Alice", + "age": 30, + "email": "alice@example.com", + "address": { + "street": "123 Maple Street", + "city": "Wonderland", + "postal_code": "12345" + }, + "phones": [ + { + "type": "home", + "number": "555-1234" + }, + { + "type": "work", + "number": "555-5678" + } + ], + "is_active": true, + "metadata": { + "created_at": "2024-08-16T10:00:00Z", + "updated_at": "2024-08-17T10:00:00Z" + }, + "tags": ["developer", "golang", "openai"] +}`) + +type User struct { + Name string `json:"name"` + Age int `json:"age"` + Email string `json:"email"` + Address Address + Phones []Phone + IsActive bool `json:"is_active"` + Metadata Metadata + Tags []string +} + +type Phone struct { + Type string `json:"type"` + Number string `json:"number"` +} + +type Address struct { + Street string `json:"street"` + City string `json:"city"` + PostalCode string `json:"postal_code"` +} + +type Metadata struct { + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +func BenchmarkEncodingJSON(b *testing.B) { + for i := 0; i < b.N; i++ { + var user User + err := json.Unmarshal(jsonData, &user) + if err != nil { + b.Fatalf("Failed to unmarshal: %v", err) + } + } +} + +func BenchmarkJSONIter(b *testing.B) { + json := jsoniter.ConfigCompatibleWithStandardLibrary + for i := 0; i < b.N; i++ { + var user User + err := json.Unmarshal(jsonData, &user) + if err != nil { + b.Fatalf("Failed to unmarshal: %v", err) + } + } +}