-
Notifications
You must be signed in to change notification settings - Fork 532
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
Make newSV_type() an inline function #19414
Conversation
Note: xenu looked at preventing inlining when the desired type is not know
|
cd77597
to
6f86b0e
Compare
6f86b0e
to
562f242
Compare
On Sun, 13 Feb 2022 at 23:54, Richard Leach ***@***.***> wrote:
Note: This is an alternative to #19381
<#19381>, following feedback from @xenu
<https://github.com/xenu>.
When a new SV is created and upgraded to a type known at compile time,
using the general-purpose upgrade function (sv_upgrade) is clunky.
Specifically, while uprooting a SV head is lightweight (assuming there are
unused SVs), sv_upgrade is too big to be inlined, contains many branches
that can logically be resolved at compile time for known start & end types,
and the lookup of the correct body_details struct may add CPU cycles.
This PR:
- Adds a new file - *sv_inline.h*, into which are moved many of the
definitions
and structures from *sv.c*. This seemed necessary because of the spread
of type definitions across existing header files.
- Converts newSV_type into an inline function and adds to it the logic
from
sv_upgrade necessary to upgrade a SVt_NULL.
Building on that, the commits in this PR:
- Modify existing calls to newSV(sv) followed by an sv_upgrade(sv,
type) to
just use newSV_type, so that they also benefit.
- Replaces calls to newSV(0) with newSV_type(SVt_NULL)
- Add a new inline function, newSV_type_mortal, to address the absence
of
an efficient way to make a new non-SVt_NULL mortal SV.
With gcc version 10.2.1 on Debian Linux, the resulting perl binary was 25k
larger than blead. (The main commit accounts for almost all of this.)
I used the following trivial benchmark as a gauge of the performance
difference, finding the patched version to be about 30% faster:
perl -e '$str="A"x64; for (0 .. 1_000_000) { @svs = split //, $str }'
dumbbench concurs:
sv_upgrade_fresh1:~/git_tree/perl$ dumbbench -- ./perl -Ilib -e
'$str="A"x64; for (0 .. 1_000_000) { @svs = split //, $str }'
cmd: Ran 21 iterations (1 outliers).
cmd: Rounded run time per iteration: 3.0000e+00 +/- 5.6e-03 (0.2%)
blead:~/git_tree/perl2$ dumbbench -- ./perl -Ilib -e '$str="A"x64; for (0
.. 1_000_000) { @svs = split //, $str }'
cmd: Ran 24 iterations (4 outliers).
cmd: Rounded run time per iteration: 4.0168e+00 +/- 2.9e-03 (0.1%)
Nice.
cheers,
Yves
…--
perl -Mre=debug -e "/just|another|perl|hacker/"
|
1b996aa
to
83babae
Compare
b33fd2d
to
8a82591
Compare
Two small issues remaining:
Otherwise LGTM. |
8a82591
to
d310d10
Compare
When a new SV is created and upgraded to a type known at compile time, uprooting a SV head and then using the general-purpose upgrade function (sv_upgrade) is clunky. Specifically, while uprooting a SV head is lightweight (assuming there are unused SVs), sv_upgrade is too big to be inlined, contains many branches that can logically be resolved at compile time for known start & end types, and the lookup of the correct body_details struct may add CPU cycles. This commit tries to address that by making newSV_type an inline function and including only the parts of sv_upgrade needed to upgrade a SVt_NULL. When the destination type is known at compile time, a decent compiler will inline a call to newSV_type and use the type information to throw away all the irrelevant parts of the sv_upgrade logic. Because of the spread of type definitions across header files, it did not seem possible to make the necessary changed inside sv.h, and so a new header file (sv_inline.h) was created. For the inlined function to work outside of sv.c, many definitions from that file were moved to sv_inline.h. Finally, in order to also benefit from this change, existing code in sv.c that does things like this: SV* sv; new_SV(sv); sv_upgrade(sv, SVt_PV) has been modified to read something like: SV* sv = newSV_type(SVt_PV);
When a function outside of sv.c creates a SV via newSV(0): * There is a call to Perl_newSV * A SV head is uprooted and its flags set * A runtime check is made to effectively see if 0 > 0 * The new SV* is returned Replacing newSV(0) with newSV_type(SVt_NULL) should be more efficient, because (assuming there are SV heads to uproot), the only step is: * A SV head is uprooted and its flags set
There's no efficient way to create a mortal SV of any type other than SVt_NULL (via sv_newmortal). The options are either to do: * SV* sv = sv_newmortal; sv_upgrade(sv, SVt_SOMETYPE); but sv_upgrade is needlessly inefficient on new SVs. * SV* sv = sv_2mortal(newSV_type(SVt_SOMETYPE) but this will perform runtime checks to see if (sv) and if (SvIMMORTAL(sv), and for a new SV we know that those answers will always be yes and no. This commit adds a new inline function which is basically a mortalizing wrapper around the now-inlined newSV_type.
d310d10
to
f327c5a
Compare
Thanks, I've addressed those two points. (Odd how the waas-commented-out code didn't have an apparent effect, but I can always look at why separately.) |
Note: This is an alternative to #19381, following feedback from @xenu.
When a new SV is created and upgraded to a type known at compile time,
using the general-purpose upgrade function (
sv_upgrade
) is clunky.Specifically, while uprooting a SV head is lightweight (assuming there are
unused SVs),
sv_upgrade
is too big to be inlined, contains many branchesthat can logically be resolved at compile time for known start & end types,
and the lookup of the correct body_details struct may add CPU cycles.
This PR:
and structures from sv.c. This seemed necessary because of the spread
of type definitions across existing header files.
newSV_type
into an inline function and adds to it the logic fromsv_upgrade
necessary to upgrade a SVt_NULL.Building on that, the commits in this PR:
newSV(sv)
followed by ansv_upgrade(sv, type)
tojust use
newSV_type
, so that they also benefit.newSV(0)
withnewSV_type(SVt_NULL)
an efficient way to make a new non-SVt_NULL mortal SV.
With gcc version 10.2.1 on Debian Linux, the resulting perl binary was 25k
larger than blead. (The main commit accounts for almost all of this.)
I used the following trivial benchmark as a gauge of the performance
difference, finding the patched version to be about 30% faster:
perl -e '$str="A"x64; for (0 .. 1_000_000) { @svs = split //, $str }'
perf showed numbers in this region for blead:
with sv_upgrade taking 25% of the run time:
perf showed numbers in this region for patched:
and other functions coming to the fore:
Note: This might be the best-case benchmark, as it is pretty much all about SV creation
and destruction, with little overhead from op dispatch or other functions.
This commit has left the big chunk of body commentary in sv.c somewhat adrift of
e.g.
the bodies_by_type
table. I don't know how best to tidy that up. Hoping for somefeedback and suggestions!