- 
                Notifications
    
You must be signed in to change notification settings  - Fork 602
 
Add Perl_sv_upgrade_fresh for more efficient upgrades of SVt_NULL SVs #19381
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
Conversation
| 
           Also, check failures. I'll work on those as time allows.  | 
    
| 
           Notes on Windows msvc142 fail: 
 note that this works fine (sv.c has the exact same c99 initializers in sv_upgrade): vs working when c99 initializers were added in #19270: 
 Any difference between v16.11.7 and v16.11.9? Do the flags on the failing build command line (-TP - treat source as CPP; -EHsc - exception handling settings) make a difference? Or should I just be declaring the function somewhere in a .c file?  | 
    
| 
           Notes on mingw64 fail: 
 But the other inline functions (e.g. Perl_av_store_simple) don't have this problem. It is because their embed.fnc entries are   | 
    
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, it 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 does two things: * adds Perl_sv_upgrade_fresh for use with SVs of type SVt_NULL * moves structure, macro and function definitions required to allow this new function to reside in inline.h Subsequent commits will make use of this new function.
Most of the various newSV* functions modified in this commit all aquire a new SV head and upgrade it to a specific PV type of choice. Changing the call from sv_upgrade(sv, type) to sv_upgrade_fresh(sv,type) allows for the inlining of only the relevant body allocation code into these functions. The exception to the above is Perl_newSV_type, which takes the target type as a parameter. For some callers, the target types may not be known until runtime. In this case, the entirety of Perl_sv_upgrade_fresh will be inlined. The growth in the size of the compiled perl binary is small (only about 40 bytes, at least on x64 using gcc or clang).
210cadc    to
    709d0a4      
    Compare
  
    | 
           Initializers changed to fix the msvc142 build.  | 
    
| =cut | ||
| */ | ||
| 
               | 
          ||
| #define PERL_IN_SV_C | 
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 effectively defines PERL_IN_SV_C everywhere, which defeats its purpose. If you need some symbol that isn't available otherwise, you need to make it public.
| 
           IMO it shouldn't be a public API and it belongs inside   | 
    
| 
           Oh, it isn't actually marked as an API function. Still, it belongs in   | 
    
          
 It's the only place that uses it so far. I was going to do some follow up commits, but first see chat below. 
 I would agree, but for  e.g. You create an AV outside of sv.c via newAV() and despite  
 I would definitely be happy to leave  I just wasn't sure what the reception would be to that approach - more newSV* functions in sv.c - vs the one currently in this PR. (It seemed more useful to present a concrete approach in a PR, even to be shot down in favour of something else.)  | 
    
          
 It's because I forgot to make   | 
    
| 
           Now that I think of it, maybe   | 
    
          
 But: 
 
 If there's no   | 
    
          
 If  
 All those functions are doing this: which is equivalent to The main advantages of this approach are: 
 BTW, if you're afraid of the inlining overhead in the cases when the type can't be deduced at compile time, something like this can be done: TBH I'm not sure if it's worth it.  | 
    
          
 Oh, I see. I hadn't understood that you meant converting  Yes, I'd thought about that as a possible approach, but was worried about code bloat. It quickly became easy in my experiments to add 8kb+ to the binary size. I wasn't sure what kind of size increase would be acceptable. If that is a big enough concern, what do you think about a  There's also still the same "problem" that the various structs and macros moves from sv.h and sv.c into inline.h would be required.  | 
    
          
 Oh, more than that - additional definitions would also have to move so that   | 
    
          
 Binary size isn't nearly as important as most people think. It's not like the CPU has to store the whole binary in the cache. It (pretty much) only caches the parts that are actually being called. Not to mention that caches are much bigger than they used to. Also, cache lines are tiny (on most processors it's 64 bytes, although there are exceptions like Apple M1 with 128 bytes), so it's pretty granular. Of course, only benchmarks can tell us the truth. It's a shame we don't a general benchmark suite for perl. It would be wonderful to be able to just click a button and get some numbers. t/benchmark doesn't count because it's valgrind and it basically only counts instructions. 
 It isn't a bad idea, but I think we should first try inlining everything (maybe combined with the  
 I don't have strong opinions on this topic. Personally, I'd probably put the structures and the macros in   | 
    
| 
           Agreed on cache characteristics. I just had the feeling that a non-trivial increase in size might cause waves. Wonder if we could suggest porting t/benchmark to perf as a Summer of Code project? I'll hopefully have a few hours this weekend, I'll make a start on an inlined   | 
    
          
 A thrown-together PoC of this is at https://github.com/richardleach/perl5/tree/inline-newSV_type Because of the dependencies between the existing .h files and the parts of sv.,c that have to move, I ended up creating sv_inline,.h. The compiled perl is almost 33kb larger than blead and performs ~12% WORSE than blead on the simple split benchmark mentioned at the top of this PR. :( I haven't had time to run with perf yet to try to see what's going on.  | 
    
| 
           Hahah, user error was somehow going on. The inline newSV_type branch mentioned above is about as fast as the sv_upgrade_fresh branch in this PR. However, blead and the sv_upgrade_fresh _perl_s are both around the 3.7 MB mark, but the newSV_type perl is a ridiculous 11 MB! I'll try to see if something daft is going on. If not, then that's surely too much of a size increase and a more selective approach will be needed?  | 
    
| 
           The inlined   | 
    
| 
           #19414 was merged instead.  | 
    
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, it 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:
sv_upgrade_fresh, to be used when(typically) a new SV is created (so of type SVt_NULL) and then upgraded
to a type known at compile time.
sv.h and sv.c to allow the new function to reside in inline.h.
On Linux, with gcc & clang, I found that the resulting perl size is only
about 40 bytes larger than blead.
I have follow-up branches in various stages of completeness for using
the new function elsewhere in core. (e.g. more directly with
newAV()andnewHV(), rather than have them usenewSV_type, which still hasavoidable branches.) These branches may not be finalized in this dev cycle.
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 these numbers for blead:
with
sv_upgradetaking 25% of the run time:perf showed these numbers for patched:
and other functions coming to the fore:
I don't consider this PR fit for commit just yet, since moving parts of sv.h and sv.c into inline.h:
table. I don't know how best to tidy that up.
Hoping for some feedback and suggestions on the above two points!