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

[مؤرشف] آخر تحديث بواسطة Joe Schaefer في Thu, 08 Jan 2026    مصدر
 

نجوم الشمس فقط

المشكلة

بيرل 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 منذ أكثر من عقد من الزمان.