stephenberry / json_performance

Performance profiling of JSON libraries
MIT License
17 stars 11 forks source link

Reuse buffer in JSON Link test #1

Closed beached closed 1 year ago

beached commented 1 year ago

This more replicates the Glaze test and reuses storage.

stephenberry commented 1 year ago

Thanks!

stephenberry commented 1 year ago

Yeah, that brings the daw_json_link performance closer, which makes sense because the approach is similar.

beached commented 1 year ago

If one can guarantee the output buffer is large enough, one can go much faster on the output. The output isn't the most optimized part of JSON Link but a change like

      obj = daw::json::from_json<obj_t>(buffer);
      buffer.clear( );
      daw::json::to_json(obj, buffer);
      for (size_t i = 0; i < iterations-1; ++i) {
         obj = daw::json::from_json<obj_t>(buffer);
         daw::json::to_json(obj, buffer.data());
      }
  In the bench changes it from 
glaze runtime: 4.02644
daw_json_link runtime: 4.44281

On my machine to

glaze runtime: 3.97074
daw_json_link runtime: 2.96721

I tried to see if one could do that with glaze_write but im not sure it can or I didn't figure it out. The error looked like it took a string by ref or an ostream &

stephenberry commented 1 year ago

Yeah, this is a tricky thing to guarantee. But, I like that daw_json_link has direct support for it. std::string's back inserter or pushback is terrible because it constantly checks if it needs to grow. This is solved in C++23 with std::string's resize_and_overwrite https://en.cppreference.com/w/cpp/string/basic_string/resize_and_overwrite However, it's not supported yet.

I had implemented a raw buffer approach, but I'd rather gain the performance without compromising safety. With the difference in performance that you've shown here, though, I'll have to reconsider.

Thanks so much! And, daw_json_link is a great library. I hope that we can learn from each other's efforts. I'll have to caveat glaze's performance.

beached commented 1 year ago

You may also want to check out Boost.Describe for generating your mappings. I have an example over at https://github.com/beached/daw_json_link_describe on how I did it for JSON Link. It's somewhat close to what reflection would provide in a future OS.

Yeah, the resize_and_overwrite idiom is great. But it really only saves on the memset, which can cost a lot but nothing compared to push_back. resizing a string and then overwriting the zero'd contents costs little. I have a vector type that does this along with a resize and append like method(which is a more common operation for read( ... ) like ops) and push_back compared is orders of magnitude slower whereas resize( N ), then using data( ) is 10-20%

stephenberry commented 1 year ago

Thanks for the describe link. I'm really looking forward to compile time reflection in C++ some day.

I implemented raw buffer writing in glaze and got the following runtimes: glaze runtime: 2.28307 daw_json_link runtime: 2.58314

beached commented 1 year ago

Nice!

stephenberry commented 1 year ago

I was able to get the std::string writing performance to basically match that of the raw char buffer. It turns out the bottleneck was indeed checking the size too often. By making the size checking smarter and less common std::string can be used for safety without really any loss of performance. I've updated my benchmarks to reflect the updates.