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

encoding/protojson: poor serialization performance #1285

Open
tonybase opened this issue Feb 28, 2021 · 10 comments
Open

encoding/protojson: poor serialization performance #1285

tonybase opened this issue Feb 28, 2021 · 10 comments

Comments

@tonybase
Copy link

tonybase commented Feb 28, 2021

https://github.com/tonybase/benchmark/tree/main/benchmark-protobuf-json:

➜  benchmark-protobuf-json git:(main) go test -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: github.com/tonybase/benchmark/benchmark-protobuf-json
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
BenchmarkOrderProto3Marshal-8     	 3230240	       352.5 ns/op	     112 B/op	       1 allocs/op
BenchmarkOrderJSONMarshal-8       	 1411366	       858.9 ns/op	     240 B/op	       1 allocs/op
BenchmarkOrderJSONPBMarshal-8     	  259860	      4432 ns/op	    1016 B/op	      29 allocs/op
BenchmarkOrderProto3Unmarshal-8   	 1879772	       630.8 ns/op	     368 B/op	      11 allocs/op
BenchmarkOrderJSONUnmarshal-8     	  288832	      3992 ns/op	     696 B/op	      20 allocs/op
BenchmarkOrderJSONPBUnmarshal-8   	  181722	      6217 ns/op	    1088 B/op	      56 allocs/op
PASS
ok  	github.com/tonybase/benchmark/benchmark-protobuf-json	9.430s

It doesn't seem to optimize performance very well, and Encoder doesn't use a similar standard library EncodeStatePool to reduce object allocation

@dsnet
Copy link
Member

dsnet commented Mar 4, 2021

The protojson package uses its own implementation of JSON since it needs to comply with a stricter version of JSON (RFC 7493) than what encoding/json supports (RFC 7159). The internal implementation does not benefit from years of optimizations that have been added to the standard library. There's some work going on to investigate major changes to the encoding/json package which may allow us to have native RFC 7159 support, in which case we will delete the internal library and use the standard one instead (and benefit from its optimizations).

@dsnet dsnet changed the title encoding/protojson: Proto JSON has poor serialization performance encoding/protojson: poor serialization performance Mar 4, 2021
@tonybase
Copy link
Author

tonybase commented Mar 6, 2021

The main reason that encoding/json is not used is because google/protobuf/duration.proto, int64(string) and oneof marshal are inconsistent with the standard library JSON and there is no good solution.

https://developers.google.com/protocol-buffers/docs/proto3#json

@tonybase
Copy link
Author

tonybase commented Mar 9, 2021

I want to use encoding/json. Can I implement MarshalJSON and UnmarshalJSON interfaces for known types?

https://github.com/protocolbuffers/protobuf-go/tree/master/types/known

@dsnet
Copy link
Member

dsnet commented May 12, 2021

The main reason that encoding/json is not used is because google/protobuf/duration.proto, int64(string) and oneof marshal are inconsistent with the standard library JSON and there is no good solution.

That's not true. There are many reasons why encoding/json is not used including the representation of oneofs, well-known types, and so forth.

related: protocolbuffers/protobuf#8331

Let's keep this topic about improving performance. Trying to serialize int64 as a JSON number is an unrelated concern.

@ash2k
Copy link

ash2k commented Sep 15, 2021

Another potential performance optimization is to allow to disable field sorting. Any/random order is fine in most situations.

@tonybase
Copy link
Author

Are there any current plans for this optimization?

@schattian
Copy link

schattian commented Dec 19, 2021

Hi, afaik the issue tracker continues to be this one both for this and the newer protobuf-go repo, let me know if that's not the case.

I've submitted a PR to improve marshaling performance at https://go-review.googlesource.com/c/protobuf/+/373356

name                        old time/op    new time/op    delta
Marshal_ScalarsUnordered-8    3.49µs ± 8%    2.06µs ± 1%  -40.84%  (p=0.000 n=29+27)
Marshal_Scalars-8             4.18µs ± 7%    2.80µs ± 1%  -32.94%  (p=0.000 n=29+30)

name                        old alloc/op   new alloc/op   delta
Marshal_ScalarsUnordered-8    1.47kB ± 0%    0.14kB ± 0%  -90.76%  (p=0.000 n=30+30)
Marshal_Scalars-8             1.63kB ± 0%    0.30kB ± 0%  -81.87%  (p=0.000 n=30+30)

name                        old allocs/op  new allocs/op  delta
Marshal_ScalarsUnordered-8      29.0 ± 0%       4.0 ± 0%  -86.21%  (p=0.000 n=30+30)
Marshal_Scalars-8               34.0 ± 0%       9.0 ± 0%  -73.53%  (p=0.000 n=30+30)

Boring details: I've added the 'Unordered' marshalopt on the old impl to sample unordered and ordered benchmarks.

@puellanivis
Copy link
Collaborator

With benchstat try running with -count=10 to get ten samples each, and then the delta will provide you with details about how much better/worse the metric is, and the confidence of that value. You’ll notice all of these have a p-value of 1, which is not particularly convincing on its own.

@schattian
Copy link

updated

@schattian
Copy link

going back to this in a bit, on vacation rn

Mynacol pushed a commit to Mynacol/flowpipeline that referenced this issue Nov 22, 2024
This change speeds up the stdin segment by moving the unmarshalling step
of protojson into a goroutine. The protojson implementation is known to
be not as optimized as the stdlib json implementation [1].
This change improves performance with more parallelization, leading
to the following speedups on an AMD EPYC 7742 dual-socket server:

nr. of records in JSON | old wall time | new wall time | speedup
 5473180               | 1m53.214s     | 0m55.954s     | 50%
16002618               | 5m35.092s     | 2m48.978s     | 50%

This might lead to the reordering of records, which might also happen on
other input segments like goflow. Instead, users should depend on
sequence numbers and timestamps in the EnhancedFlow struct.

[1] golang/protobuf#1285
Mynacol pushed a commit to Mynacol/flowpipeline that referenced this issue Nov 22, 2024
This change speeds up the stdin segment by moving the unmarshalling step
of protojson into a goroutine. The protojson implementation is known to
be not as optimized as the stdlib json implementation [1].
This change improves performance with more parallelization, leading
to the following speedups on an AMD EPYC 7742 dual-socket server:

| nr. of records in JSON | old wall time | new wall time | speedup |
| ---------------------- | ------------- | ------------- | ------- |
|  5473180               | 1m53.214s     | 0m55.954s     | 50%     |
| 16002618               | 5m35.092s     | 2m48.978s     | 50%     |

This might lead to the reordering of records, which might also happen on
other input segments like goflow. Instead, users should depend on
sequence numbers and timestamps in the EnhancedFlow struct.

[1] golang/protobuf#1285
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants