Try to release the GVL while gumbo parsing, for better concurrency #2964
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What problem is this PR intended to solve?
Parsing html documents in threads has no performance benefit; there is no concurrency.
I have just learned today about the "nogvl" ruby API, described in
thread.c
, as a way to permit concurrent/parallel execution of non-ruby code by releasing the GVL (Global VM Lock).So I tried experimenting a bit with wrapping
gumbo_parse_with_options
andbuild_tree
insiderb_thread_call_without_gvl
, and the results are promising. As seen in the table below, originally we have the same number of operations per second no matter the concurrency. By releasing the GVL duringgumbo_parse_with_options
we increase the number of operations per sec. Here the increase maxes out around 4 threads (the number of cores). 2x operations per sec is not bad at all I think. By also releasing the GVL duringbuild_tree
we get a bit of extra concurrency.parsing N times a 216k html file
0: original with GVL, and therefore no concurrency
1: release GVL for
gumbo_parse_with_options
2: release GVL for
gumbo_parse_with_options
andbuild_tree
What do you think?
Some caveats: I don't know very much about this nogvl API, so I'm not sure if I should have used
rb_thread_call_without_gvl2
, if theRUBY_UBF_IO
unblock function is appropriate to use here, if the code would crash on some other input, etc. Also I'm assuming (at least it looked that way to me) thatgumbo_parse_with_options
andbuild_tree
are pure gumbo/libxml functions that don't invoke any of the Ruby API that depends on the GVL.Ideally there would only be one call to
rb_thread_call_without_gvl
instead of two, but that would require pullingbuild_tree
insideperform_parse
somehow and I have no idea if that's feasible, whybuild_tree
is executed insiderb_ensure
, etc.