طلب ميزة بيرل 7: مختومة فرعية للمصطلحات المكتوبة

المشكلة
بيرل 5’يشتمل بحث أسلوب وقت تشغيل OO على مصروفات إضافية للأداء أكثر بنسبة 50% من استدعاء البرنامج الفرعي المحدد المباشر.
الحل الأولي: دوغ MacEachern’تحسينات بحث الأسلوب s
كان دوغ منشئ مشروع mod_perl مرة أخرى في منتصف 90s، ومن الواضح أن كتابة الأداء العالي بيرل كان ثروته. واحدة من مساهماته العديدة في p5p كان لخفض غرامة الأداء من OO طريقة البحث النفقات العامة في النصف، باستخدام طريقة + @إيسا ذاكرة التخزين المؤقت للتدرج لجعل البحث عن أسلوب كائن وقت التشغيل لكائنات mod_perl مثل Apache2::RequestRec تبسيط قدر الإمكان. ولكن هذا فقط يجعلنا في منتصف الطريق.
هذا ليس’t مشكلة تافهة مع المكالمات إلى هيكل C أساليب get-set accessor — الوضع المشترك مع العديد من واجهات برمجة تطبيقات mod_perl. بيرل’طريقة وقت التشغيل - غرامة البحث عن استدعاء في httpd’ss هيكل request_rec *، أن mod_perl يعرض عبر Apache2::RequestRec وحدة، هو على نفس الترتيب من حجم التنفيذ الكامل للمكالمة. بالنسبة إلى المواقع المدعومة من mod_perl التي تستدعي ملايين طريقة XS ثانية، فهذه مضيعة فظيعة لدورات وحدة المعالجة المركزية الثمينة.
ماذا كان دوغ يبحث عن كانت طريقة لإخبار Perl 5 بتنفيذ بحث الأسلوب في وقت التجميع، بالطريقة التي يستخدم بها استدعاءات البرنامج الفرعي المحدد. في كل مرة حاول دوغ، ضرب حواجز الطرق ذات الطبيعة الاجتماعية أو التقنية. لربما’حان الوقت لجعل تمريرة أخرى في هذه الفكرة مع ظهور بيرل 7.
سكريبت مقارنة الأداء.
#!/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 sealed 'all';
sub foo { shift }
my $n;
sub _foo { my main $x = shift; $n++ ? $x->bar : $x->reentrant }
}
sub func {Foo::foo($x)}
BEGIN {our @ISA=qw/Foo/}
use base 'sealed';
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);
نتائج مقارنة الأداء
1..3
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.
sealed: tweak() aborted: sealed: invalid lookup: Foo->bar - did you forget to 'use Foo' first?
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;
}
}
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;
}
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
حل Perl 7 المقترح: :مختومة البرامج الفرعية للمعاملات المكتوبة
التعليمة البرمجية العينة:
use v5.38;
use Apache2::RequestRec;
sub handler :Sealed (Apache2::RequestRec $r) {
$r->content_type("text/html"); #compile time method lookup
}
إنتاج الجودة، قوية بيرل v5.28 + النموذج الأولي: sealed.pm v8.7.7 (على CPAN).
تتوفر تعليمات التجميع لـ perl 5.30+ في sealed.pm إذا كنت تريد تشغيل mod_perl2 w/ ithreads وhttpd-2.4 w/ event mpm، وليس segfault على مقياس أي. تم الاختبار في سولاريس 11.4 و أوبونتو 22.04 على amd64.
للمتعة، جرب هذا تصحيح القرد لكي ModPerl::RegistryCooker:
<VirtualHost *:443>
PerlModule ModPerl::RegistryCookerSealed
PerlResponseHandler ModPerl::Registry
AddHandler perl-script .pl
Options +ExecCGI
</VirtualHost>
أنها تمكن من آثار المعالج الفرعي: مختوم {البرنامج النصي هنا} على الإطلاق ModPerl::سجل سيناريوهات، شيء مثل هذا واحد.
~/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%
راجع https://github.com/SunStarSys/sealed/blob/master/lib/sealed.pm. ابحث عن t/bench.pl في الدليل الرئيسي.
سيتيح ذلك لـ Perl 5 تنفيذ نموذج التعليمة البرمجية’ss content_type البحث عن الأساليب في وقت التجميع، دون التسبب في أي مشكلات في التوافق الخلفي أو مبرمجي CPAN المتضررين، لأن هذه الميزة ستستهدف مطوري التطبيقات. لا يوجد مؤلفو وحدات OO الموروثة.
تم سرقة هذه الفكرة من دون مقابل ديلان. اقرأ هذا لجهود CPython منذ أكثر من عقد من الزمان.
