Skip to content

Instantly share code, notes, and snippets.

@skids
Last active September 10, 2015 01:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skids/7b099e80ce8d378f0cdd to your computer and use it in GitHub Desktop.
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 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