-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
Refactor/no virtual sax #1153
Refactor/no virtual sax #1153
Conversation
ah, funny, i was just wondering about this yesterday, why it has to be virtual :). |
@theodelrieu Thanks for the PR. I'll run the benchmarks hopefully by the end of the week. I hope there is a larger performance difference - without such, I would still rather go with the base class approach, because I still think it is is much easier for a developer to implement the interface rather than relying on templates here. Let's see what performance has to say about this ;) |
Well, as you can see in the tests/benchmarks, users don't have to write the word Anyway, going the virtual way can still be changed afterwards if needed. It might be better to have people use SAX for a while and hear their feedbacks. |
I really need to look at the code... |
I would advise adding the flag |
I set my cpu frequence to 100% to avoid latency/noise instead of adding |
But won’t inlining then still occur? |
I did split the source files so that the compiler dos not see the derived class definition in every translation unit, it should do the trick (?) |
Here are my results: $ ./json_benchmarks --benchmark_filter="ParseFileSAX.*" --benchmark_min_time=30
2018-07-05 22:10:06
Run on (8 X 2900 MHz CPU s)
CPU Caches:
L1 Data 32K (x4)
L1 Instruction 32K (x4)
L2 Unified 262K (x4)
L3 Unified 8388K (x1)
***WARNING*** Library was built as DEBUG. Timings may be affected.
--------------------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------------------
ParseFileSAXVirtual/jeopardy 2491 ms 2487 ms 17 21.3033MB/s
ParseFileSAXVirtual/canada 156 ms 155 ms 271 13.8122MB/s
ParseFileSAXVirtual/citm_catalog 69 ms 69 ms 605 23.9219MB/s
ParseFileSAXVirtual/twitter 27 ms 27 ms 1540 21.9946MB/s
ParseFileSAXVirtual/floats 1397 ms 1395 ms 30 15.4981MB/s
ParseFileSAXVirtual/signed_ints 1228 ms 1227 ms 33 18.9534MB/s
ParseFileSAXVirtual/unsigned_ints 1208 ms 1206 ms 35 19.2918MB/s
ParseFileSAXTemplate/jeopardy 2467 ms 2463 ms 17 21.513MB/s
ParseFileSAXTemplate/canada 157 ms 157 ms 265 13.7093MB/s
ParseFileSAXTemplate/citm_catalog 70 ms 69 ms 601 23.7028MB/s
ParseFileSAXTemplate/twitter 27 ms 27 ms 1522 22.0489MB/s
ParseFileSAXTemplate/floats 1431 ms 1429 ms 29 15.129MB/s
ParseFileSAXTemplate/signed_ints 1301 ms 1299 ms 32 17.9037MB/s
ParseFileSAXTemplate/unsigned_ints 1282 ms 1280 ms 34 18.1799MB/s |
Sorry I did tweak the
|
First off, thanks a lot @theodelrieu for pushing this topic! It makes a large difference between just uttering possible interface changes and actually proposing a runnable realization thereof. I really appreciate this and it is always a pleasure working with you! My 2 cents:
|
Now the results are more consistent: ./json_benchmarks --benchmark_filter="ParseFileSAX.*" --benchmark_min_time=30
2018-07-05 23:00:04
Run on (8 X 2900 MHz CPU s)
CPU Caches:
L1 Data 32K (x4)
L1 Instruction 32K (x4)
L2 Unified 262K (x4)
L3 Unified 8388K (x1)
--------------------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------------------
ParseFileSAXVirtual/jeopardy 498 ms 497 ms 83 106.574MB/s
ParseFileSAXVirtual/canada 58 ms 58 ms 726 37.247MB/s
ParseFileSAXVirtual/citm_catalog 13 ms 13 ms 3213 125.259MB/s
ParseFileSAXVirtual/twitter 5 ms 5 ms 7711 110.439MB/s
ParseFileSAXVirtual/floats 485 ms 484 ms 88 44.6818MB/s
ParseFileSAXVirtual/signed_ints 264 ms 264 ms 158 88.1108MB/s
ParseFileSAXVirtual/unsigned_ints 259 ms 259 ms 161 89.8282MB/s
ParseFileSAXTemplate/jeopardy 490 ms 490 ms 85 108.163MB/s
ParseFileSAXTemplate/canada 57 ms 57 ms 730 37.5386MB/s
ParseFileSAXTemplate/citm_catalog 13 ms 13 ms 3255 125.02MB/s
ParseFileSAXTemplate/twitter 5 ms 5 ms 7818 111.926MB/s
ParseFileSAXTemplate/floats 474 ms 474 ms 88 45.6156MB/s
ParseFileSAXTemplate/signed_ints 262 ms 261 ms 161 88.9603MB/s
ParseFileSAXTemplate/unsigned_ints 257 ms 257 ms 164 90.4741MB/s |
Thanks a lot :) Performance is one aspect of why I like this version better, but might not be the most important. I agree there are some benefits to have a virtual base class, but it's a problem if the library forces users to use this mechanism. However, we can have the best of both worlds. With little changes to this PR, the library can still provide the base class to help users implement a SAX interface, thus supporting both approaches. About the // elements is either nullptr, or a value
bool start_object(const std::size_t* elements) = 0; About the "natural feeling" of virtual classes, I can understand it for the SAX interface, because their intrusiveness is not that big of a deal on such a specific structure. But had we gone with this approach for user-defined types, I believe we would have received lots of complaints about it (way more than we do right now). Finally, if we decide to add functionalities to the SAX interface later on, using a Concept is way more flexible: // json v3.5.1 (example)
bool start_object_better_prototype();
// With SFINAE, we can detect if the new prototype is implemented
// or call the old one if that's not the case. (a bit similar to UDT conversion workflow)
// This is not possible with virtual classes
// (at least without breaking code or adding a new base class) So even with the proposed changes, if we provide a base class future API extensions will be compromised. |
-1 on the poor mans optional: it cannot be called with a literal. Otherwise my 5cts also: do both. Allow static handlers and provide a virtual base also |
I would agree if this API was designed to be called by users. However, only the library will call it, so creating a local variable and passing its address is not problematic. There might be better way to implement this |
I finally had some more time looking at this PR.
I think the SAX API should be the "killer feature" for the next release. Once we have this PR out of the way, I think only proper documentation is required. What do you think? |
No problem :)
Yep, that's the idea.
I believe SFINAE is not needed here (unlike constructors), because I fail to see a use-case of "Does this call of We can go with detailed
I monkey-patched the benchmarks' code for testing purposes, I will remove it.
Looks good, I will try to find time this week to finish the PR. |
9c773ef
to
7c14438
Compare
I rebased the PR, and added static_asserts. To see them in action, you can try to remove/modify some of the SAX interfaces in tests (e.g. |
7c14438
to
6acab2f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have some minor issues/questions.
benchmarks/template
Outdated
@@ -0,0 +1,25 @@ | |||
2018-06-29 18:02:43 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file should not be part of the PR. Please remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops
benchmarks/virtual
Outdated
@@ -0,0 +1,25 @@ | |||
2018-06-29 18:05:46 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file should not be part of the PR. Please remove it.
{ | ||
// (void)detail::is_sax_static_asserts<SAX, BasicJsonType>{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either the line should be removed or it should be un-commented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, this should be un-commented
@@ -34,68 +34,68 @@ using nlohmann::json; | |||
|
|||
#include <fstream> | |||
|
|||
class SaxCountdown : public nlohmann::json::json_sax_t | |||
class SaxCountdown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why aren't we inheriting here any more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did remove inheritance from tests, but since we decided to expose a base class, I shall add a test using this base class.
I will be able to work on the PR on Thursday
6acab2f
to
6f040e2
Compare
Should be good now. |
6f040e2
to
0cc3db4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
This PR is a WIP, to show a different API for SAX-related functions.
I'd like to have other people running benchmarks, my results are not that significant, even after removing every optimization flag. There is a 40ms improvement on
jeopardy.json
from time to time though.Few minor improvements that could be done:
no_limit
value, I usedstd::size_t(-1)
instead.As you can see, the public interface stays the same, there is no
static_assert
on the SAX interface for now. If we decide to go this route, I'll add detailed and useful ones (unlike those that exist today).Pull request checklist
Read the Contribution Guidelines for detailed information.
include/nlohmann
directory, runmake amalgamate
to create the single-header filesingle_include/nlohmann/json.hpp
. The whole process is described here.