From 4a3e8af9a9385825edb6c2cee735176995442c8a Mon Sep 17 00:00:00 2001 From: liu Date: Tue, 12 Nov 2024 19:02:51 +0800 Subject: [PATCH] opt: support NoCopyRawMessage (#708) --- decode_test.go | 194 +++++++++++++++++++++++++++++++++++++++++++------ rawmessage.go | 43 +++++++++++ 2 files changed, 216 insertions(+), 21 deletions(-) create mode 100644 rawmessage.go diff --git a/decode_test.go b/decode_test.go index f3ed66ecc..320d8cfb9 100644 --- a/decode_test.go +++ b/decode_test.go @@ -20,27 +20,28 @@ package sonic import ( - `bytes` - `encoding` - `encoding/json` - `errors` - `fmt` - `image` - `math` - `math/big` - `math/rand` - `net` - `reflect` - `strconv` - `strings` - `testing` - `time` - `unsafe` - - `github.com/bytedance/sonic/decoder` - `github.com/bytedance/sonic/internal/native/types` - `github.com/davecgh/go-spew/spew` - `github.com/stretchr/testify/assert` + "bytes" + "encoding" + "encoding/json" + "errors" + "fmt" + "image" + "math" + "math/big" + "math/rand" + "net" + "reflect" + "runtime" + "strconv" + "strings" + "testing" + "time" + "unsafe" + + "github.com/bytedance/sonic/decoder" + "github.com/bytedance/sonic/internal/native/types" + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" ) type T struct { @@ -2815,3 +2816,154 @@ func TestUseNumber(t *testing.T) { } } } + + +func BenchmarkDecoderRawMessage(b *testing.B) { + data := ` { + "coordinates": null, + "favorited": false, + "truncated": false, + "created_at": "Mon Sep 24 03:35:21 +0000 2012", + "id_str": "250075927172759552", + "entities": { + "urls": [ + + ], + "hashtags": [ + { + "text": "freebandnames", + "indices": [ + 20, + 34 + ] + } + ], + "user_mentions": [ + + ] + }, + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Aggressive Ponytail #freebandnames", + "metadata": { + "iso_language_code": "en", + "result_type": "recent" + }, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "id": 250075927172759552, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "place": null, + "user": { + "profile_sidebar_fill_color": "DDEEF6", + "profile_sidebar_border_color": "C0DEED", + "profile_background_tile": false, + "name": "Sean Cummings", + "profile_image_url": "https://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg", + "created_at": "Mon Apr 26 06:01:55 +0000 2010", + "location": "LA, CA", + "follow_request_sent": null, + "profile_link_color": "0084B4", + "is_translator": false, + "id_str": "137238150", + "entities": { + "url": { + "urls": [ + { + "expanded_url": null, + "url": "", + "indices": [ + 0, + 0 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "default_profile": true, + "contributors_enabled": false, + "favourites_count": 0, + "url": null, + "profile_image_url_https": "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg", + "utc_offset": -28800, + "id": 137238150, + "profile_use_background_image": true, + "listed_count": 2, + "profile_text_color": "333333", + "lang": "en", + "followers_count": 70, + "protected": false, + "notifications": null, + "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png", + "profile_background_color": "C0DEED", + "verified": false, + "geo_enabled": true, + "time_zone": "Pacific Time (US & Canada)", + "description": "Born 330 Live 310", + "default_profile_image": false, + "profile_background_image_url": "https://a0.twimg.com/images/themes/theme1/bg.png", + "statuses_count": 579, + "friends_count": 110, + "following": null, + "show_all_inline_media": false, + "screen_name": "sean_cummings" + }, + "in_reply_to_screen_name": null, + "source": "Twitter for Mac", + "in_reply_to_status_id": null + }` + + bench := func(b *testing.B, run func(b *testing.B) ) { + b.ResetTimer() + b.ReportAllocs() + b.SetBytes(int64(len(data))) + for i := 0; i < b.N; i++ { + run(b) + } + runtime.GC() + } + + b.Run("StdRawMessage", func(b *testing.B) { + bench(b, func(b *testing.B) { + var obj map[string]json.RawMessage + dc := decoder.NewDecoder(data) + dc.SetOptions(decoder.OptionUseNumber) + if err := dc.Decode(&obj); err!= nil { + b.Fatal(err.Error()) + } + _ = obj + }) + }) + + b.Run("NocopyRawMessage", func(b *testing.B) { + bench(b, func(b *testing.B) { + var obj map[string]NoCopyRawMessage + dc := decoder.NewDecoder(data) + dc.SetOptions(decoder.OptionUseNumber) + if err := dc.Decode(&obj); err!= nil { + b.Fatal(err.Error()) + } + _ = obj + }) + }) + + + b.Run("NocopyRawMessageWithoutValidation", func(b *testing.B) { + bench(b, func(b *testing.B) { + var obj map[string]NoCopyRawMessage + dc := decoder.NewDecoder(data) + dc.SetOptions(decoder.OptionNoValidateJSON | decoder.OptionUseNumber) + if err := dc.Decode(&obj); err!= nil { + b.Fatal(err.Error()) + } + _ = obj + }) + }) +} diff --git a/rawmessage.go b/rawmessage.go new file mode 100644 index 000000000..e90f42291 --- /dev/null +++ b/rawmessage.go @@ -0,0 +1,43 @@ +/* + * Copyright 2024 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sonic + +import ( + "errors" +) + +// NoCopyRawMessage is a NOCOPY raw encoded JSON value. +// It implements [Marshaler] and [Unmarshaler] and can +// be used to delay JSON decoding or precompute a JSON encoding. +type NoCopyRawMessage []byte + +// MarshalJSON returns m as the JSON encoding of m. +func (m NoCopyRawMessage) MarshalJSON() ([]byte, error) { + if m == nil { + return []byte("null"), nil + } + return m, nil +} + +// UnmarshalJSON sets *m to a reference of data. NoCopy here!!! +func (m *NoCopyRawMessage) UnmarshalJSON(data []byte) error { + if m == nil { + return errors.New("sonic.NoCopyRawMessage: UnmarshalJSON on nil pointer") + } + *m = data + return nil +}