Last active
September 10, 2015 01:30
-
-
Save skids/7b099e80ce8d378f0cdd to your computer and use it in GitHub Desktop.
Estimated impact of Backtrace creation on Str.Numeric during soft-failure modes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This shows the cost of generating backtraces when using Str.Numeric | |
# to test whether a string can be numified, which is a normal usage | |
# scenario. | |
# | |
# Test code: different from the pre-allomorph original in that it will only | |
# ever generate one Failure per .Numeric, and as long as a :$backtrace is | |
# provided and the result is not thrown, should not generate any new backtraces | |
# (I think.) But -- also different in that it always uses an exception unwind | |
# instead of letting Failure objects go through the normal return path. | |
diff --git a/src/core/Main.pm b/src/core/Main.pm | |
index 8a5d0f8..8dc75c8 100644 | |
--- a/src/core/Main.pm | |
+++ b/src/core/Main.pm | |
@@ -27,7 +27,8 @@ my sub MAIN_HELPER($retval = 0) { | |
my $val; | |
if $v ~~ /^ 'Bool::'?'False' $/ { $val := Bool::False } | |
elsif $v ~~ /^ 'Bool::'?'True' $/ { $val := Bool::True } | |
- elsif $v.Numeric.defined { $val := +$v } | |
+ elsif $v.Numeric(:backtrace(Backtrace.new(nqp::list()))).defined | |
+ { $val := +$v } | |
else { return $v } | |
# Mix in original stringifications | |
diff --git a/src/core/Str.pm b/src/core/Str.pm | |
index 32d2bfc..3dde3a5 100644 | |
--- a/src/core/Str.pm | |
+++ b/src/core/Str.pm | |
@@ -220,7 +220,7 @@ my class Str does Stringy { # declared in BOOTSTRAP | |
# * Performance tuning | |
# * Fix remaining XXXX | |
- multi method Numeric(Str:D: :$strict = True) { | |
+ multi method Numeric(Str:D: :$strict = True, :$backtrace?) { | |
my str $str = nqp::unbox_s(self); | |
my int $eos = nqp::chars($str); | |
@@ -239,14 +239,17 @@ my class Str does Stringy { # declared in BOOTSTRAP | |
# Reset end-of-string after trimming | |
$eos = nqp::add_i($end, 1); | |
- # Fail all the way out when parse failures occur | |
+ # Unwind all the way out when parse failures occur | |
my &parse_fail := -> $msg { | |
- fail X::Str::Numeric.new( | |
- source => self, | |
- reason => $msg, | |
- :$pos, | |
- ); | |
+ X::Str::Numeric.new( | |
+ source => self, | |
+ reason => $msg, | |
+ :$pos, | |
+ ).throw($backtrace); | |
}; | |
+ CATCH { | |
+ when X::Str::Numeric { return Failure.new($_); } | |
+ } | |
my sub parse-simple-number () { | |
# Handle NaN here, to make later parsing simpler | |
diff --git a/src/core/Version.pm b/src/core/Version.pm | |
index 67cca00..cce70ce 100644 | |
--- a/src/core/Version.pm | |
+++ b/src/core/Version.pm | |
@@ -9,7 +9,7 @@ class Version { | |
$_ = *; | |
} | |
else { | |
- my $numeric = .Numeric; | |
+ my $numeric = .Numeric(:backtrace(Backtrace.new(nqp::list()))); | |
$_ = $numeric if $numeric.defined; | |
} | |
} | |
Results: | |
# With strings that hit the error handling code, 20% performance impact. | |
bri@atlas:~/git/rakudobrew/moar-10dcbad-HEAD-HEAD$ time perl6 -e 'use nqp; my $bt = Backtrace.new(nqp::list()); for ("10000a3".."29999a3") { .Numeric.defined }' | |
real 0m22.255s | |
user 0m22.164s | |
sys 0m0.080s | |
bri@atlas:~/git/rakudobrew/moar-10dcbad-HEAD-HEAD$ time perl6 -e 'use nqp; my $bt = Backtrace.new(nqp::list()); for ("10000a3".."29999a3") { .Numeric(:backtrace($bt)).defined }' | |
real 0m17.454s | |
user 0m17.412s | |
sys 0m0.032s | |
# With strings that numify cleanly, no big difference as expected (the numbers go back and forth between the two) | |
# Note the successful case is much faster, so we have to look also at the throw/unwind. | |
bri@atlas:~/git/rakudobrew/moar-10dcbad-HEAD-HEAD$ time perl6 -e 'use nqp; my $bt = Backtrace.new(nqp::list()); for ("10000e3".."29999e3") { .Numeric.defined }' | |
real 0m5.969s | |
user 0m5.952s | |
sys 0m0.012s | |
bri@atlas:~/git/rakudobrew/moar-10dcbad-HEAD-HEAD$ time perl6 -e 'use nqp; my $bt = Backtrace.new(nqp::list()); for ("10000e3".."29999e3") { .Numeric(:backtrace($bt)).defined }' | |
real 0m6.073s | |
user 0m6.048s | |
sys 0m0.020s | |
# Unpatched, for comparison: does make bactraces, but also, does not always throw, so we can estimate the cost of a | |
# throw/nqp::exception() at another 20%, maybe? That seems kind of high. | |
bri@atlas:~/git/rakudobrew/moar-10dcbad-HEAD-HEAD$ time perl6 -e 'use nqp; my $bt = Backtrace.new(nqp::list()); for ("10000a3".."29999a3") { .Numeric.defined }' | |
real 0m16.909s | |
user 0m16.812s | |
sys 0m0.092s | |
bri@atlas:~/git/rakudobrew/moar-10dcbad-HEAD-HEAD$ time perl6 -e 'use nqp; my $bt = Backtrace.new(nqp::list()); for ("10000e3".."29999e3") { .Numeric.defined }' | |
real 0m5.880s | |
user 0m5.868s | |
sys 0m0.008s | |
# I noticed ShimmerFairy's first allomorphic patch used a phony Failure class to | |
# deal with some of this as well, and tried to stack up a list of reasons why | |
# parses failed. I haven't read the latest patch yet, but I did at one point | |
# think maybe we needed some sort of cascading-failure tracking ability. | |
# | |
# We probably want potential hot-path things to be available in a failure-free | |
# version, for internal use, when we know we are checking return codes, but doing | |
# it like the code above makes it very hard to pass through wrappers like prefix:<+>. | |
# | |
# Being able to inject a Backtrace from outside a tight loop would help for any | |
# internal things that run into situations like this, and it only shaves | |
# low-level code from the backtrace, which would normally be hidden anyway. | |
# | |
# In summary, Failure behavior could use a lot more tuning knobs. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment