Perl 7 Funktionsbegäran: förseglade underdelar för typangivna lexikaler
Problemet
Perl 5:s OO-exekveringsmetoduppslagning har 50 % mer prestandakostnader än ett direkt, namngivet subrutinanrop
Den första lösningen: Doug MacEachern’s metodsökoptimeringar
Doug var skaparen av mod_perl projektet tillbaka i mitten av 90-talet, så uppenbarligen skriva högpresterande Perl var hans forté. Ett av hans många bidrag till p5p skulle skära ned prestandapåföljden för uppslagning av metod utan erbjudandeoptimering till hälften med hjälp av en metod + @ISA
hierarkicache för att göra uppslagning av exekveringsobjektmetod för mod_perl-objekt som Apache2::RequestRec
Detta är inte ett trifling problem med samtal till C-struktur
get-set accessor-metoder — den gemensamma situationen med många mod_perl API:er. Perl’s runtime method-call lookup straff på httpd’s struktur request_rec *
som mod_perl exponerar via Apache2::RequestRec
Vad Doug söker.
Riktmärke, skript.
#!/usr/bin/env -S perl -Ilib -Iblib/arch
use Test::More tests => 3;
use POSIX 'dup2';
dup2 fileno(STDERR), fileno(STDOUT);
use strict;
use warnings;
use Benchmark ':all';
our ($x, $z);
$x = bless {}, "Foo";
$z = Foo->can("foo");
sub method {$x->foo}
sub class {Foo->foo}
sub anon {$z->($x)}
sub bar { 1 }
sub reentrant;
BEGIN {
package Foo;
use base 'sealed';
use sealed 'deparse';
sub foo { shift }
my $n;
sub _foo :Sealed { my main $x = shift; $n++ ? $x->bar : $x->reentrant }
}
sub func {Foo::foo($x)}
BEGIN {our @ISA=qw/Foo/}
use sealed 'deparse';
my main $y; #sealed src filter transforms this into: my main $y = 'main';
sub sealed :Sealed {
$y->foo();
}
sub also_sealed :Sealed {
my main $a = shift;
if ($a) {
my Benchmark $bench;
my $inner = $a;
return sub :Sealed {
my Foo $b = $a;
$inner->foo($b->foo($inner->bar, $inner, $bench->cmpthese));
$a = $inner;
$a->foo;
$b->bar; # error!
};
}
$a->bar();
}
sub reentrant :Sealed { my main $b = shift; local our @Q=1; my $c = $b->_foo }
ok($y->reentrant()==1);
my %tests = (
func => \&func,
method => \&method,
sealed => \&sealed,
class => \&class,
anon => \&anon,
);
cmpthese 20_000_000, \%tests;
ok(1);
use constant LOOPS => 3;
sub method2 {
my $obj = "main";
for (1..LOOPS) {
$obj->foo;
$obj->bar;
$obj->reentrant;
}
}
sub sealed2 :Sealed {
my main $obj; # sealed-src-filter
for (1..LOOPS) {
$obj->foo;
$obj->bar;
$obj->reentrant;
}
}
cmpthese 1_000_000, {
method => \&method2,
sealed => \&sealed2,
};
ok(1);
Riktmärkesresultat
1..3
sealed: compiling main->reentrant lookup.
sealed: compiling main->bar lookup.
sub _foo :sealed {
package Foo;
use warnings;
use strict;
my main $x = shift();
$n++ ? $x->bar:compiled : $x->reentrant:compiled;
}
sealed: compiling main->foo lookup.
sub sealed :sealed {
use warnings;
use strict;
$y->foo:compiled;
}
sealed: compiling Benchmark->cmpthese lookup.
sealed: compiling Foo->foo lookup.
sealed: compiling main->foo lookup.
sealed: compiling Foo->bar lookup.
Use of uninitialized value in addition (+) at lib/sealed.pm line 137.
sealed: tweak() aborted: sealed: invalid lookup: Foo->bar - did you forget to 'use Foo' first? at lib/sealed.pm line 75.
sub __ANON__ :sealed {
use warnings;
use strict;
my Foo $b = $a;
$inner->foo($b->foo:compiled($inner->bar, $inner, $bench->cmpthese:compiled));
$a = $inner;
$a->foo:compiled;
$b->bar;
}
sealed: compiling main->bar lookup.
sub also_sealed :sealed {
use warnings;
use strict;
my main $a = shift();
if ($a) {
my Benchmark $bench = 'Benchmark';
my $inner = $a;
return sub {
my Foo $b = $a;
$inner->foo($b->foo:compiled($inner->bar, $inner, $bench->cmpthese:compiled));
$a = $inner;
$a->foo:compiled;
$b->bar;
}
;
}
$a->bar:compiled;
}
sealed: compiling main->_foo lookup.
sub reentrant :sealed {
use warnings;
use strict;
my main $b = shift();
(local our(@Q)) = 1;
my $c = $b->_foo:compiled;
}
sealed: compiling main->foo lookup.
sealed: compiling main->bar lookup.
sealed: compiling main->reentrant lookup.
sub sealed2 :sealed {
use warnings;
use strict;
my main $obj = 'main';
foreach $_ (1 .. 3) {
$obj->foo:compiled;
$obj->bar:compiled;
$obj->reentrant:compiled;
}
}
ok 1
Rate class method anon func sealed
class 16129032/s -- -4% -26% -33% -36%
method 16806723/s 4% -- -23% -30% -34%
anon 21739130/s 35% 29% -- -10% -14%
func 24096386/s 49% 43% 11% -- -5%
sealed 25316456/s 57% 51% 16% 5% --
ok 2
Rate method sealed
method 546448/s -- -17%
sealed 662252/s 21% --
ok 3
Föreslagen Perl 7-lösning: :förseglad
Provkod:
use v7.0;
use Apache2::RequestRec;
sub handler :sealed {
my Apache2::RequestRec $r = shift;
$r->content_type("text/html"); #compile time method lookup
}
Produktionskvalitet, Robust Perl v5.28+ Prototyp: sealed.pm v7.0.7
Kompilera instruktioner för perl 5.30+ finns i sealed.pm
pod ska du köra mod_perl2 w/ ithreads och httpd-2.4 w/ event mpm, och inte segfault på any-skalan. Testad den Solaris 11.4
och Ubuntu 22.04
För skojs skull, prova detta apa till ModPerl::RegistryCooker
<VirtualHost *:443>
PerlModule ModPerl::RegistryCookerSealed
PerlResponseHandler ModPerl::Registry
AddHandler perl-script .pl
Options +ExecCGI
</VirtualHost>
Det möjliggör effekterna av underhanterare: Förseglad {script goes here}
på alla dina ModPerl::Register
Skript, något som denna.
~/src/cms% h2load -n 100000 -c 1000 -m 100 -t 10 http://localhost/perl-script/enquiry.pl\?lang=.es
starting benchmark...
spawning thread #0: 100 total client(s). 10000 total requests
spawning thread #1: 100 total client(s). 10000 total requests
spawning thread #2: 100 total client(s). 10000 total requests
spawning thread #3: 100 total client(s). 10000 total requests
spawning thread #4: 100 total client(s). 10000 total requests
spawning thread #5: 100 total client(s). 10000 total requests
spawning thread #6: 100 total client(s). 10000 total requests
spawning thread #7: 100 total client(s). 10000 total requests
spawning thread #8: 100 total client(s). 10000 total requests
spawning thread #9: 100 total client(s). 10000 total requests
Application protocol: h2c
progress: 10% done
progress: 20% done
progress: 30% done
progress: 40% done
progress: 50% done
progress: 60% done
progress: 70% done
progress: 80% done
progress: 90% done
progress: 100% done
finished in 13.07s, 7652.14 req/s, 11.83MB/s
requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored, 0 timeout
status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 154.61MB (162119955) total, 566.39KB (579980) headers (space savings 95.47%), 152.30MB (159700000) data
min max mean sd +/- sd
time for request: 5.74ms 12.77s 6.39s 3.61s 58.14%
time for connect: 304us 293.01ms 70.17ms 76.83ms 74.80%
time to 1st byte: 7.86ms 7.87s 3.33s 1.82s 50.40%
req/s : 7.71 248.17 19.60 28.07 92.70%
Se https://github.com/SunStarSys/sealed/blob/master/lib/sealed.pm. Sök efter t/bench.pl
Detta gör det möjligt för Perl 5 att göra provkoderna content_type
Denna engelska idé är gratis stulen från Dylan. Läs det här.