Mencoba preload (PHP 7.4) dan RoadRunner



Halo, Habr!

Kami sering menulis dan berbicara tentang kinerja PHP: bagaimana kami menghadapinya secara umum, bagaimana kami menghemat $ 1 juta ketika beralih ke PHP 7.0, dan juga menerjemahkan berbagai bahan tentang topik ini. Hal ini disebabkan oleh fakta bahwa jumlah pengguna produk kami meningkat, dan penskalaan backend PHP dengan besi sangat mahal - kami memiliki 600 server dengan PHP-FPM. Karena itu, menginvestasikan waktu dalam pengoptimalan bermanfaat bagi kami.

Sebelumnya, kami berbicara terutama tentang cara yang biasa dan sudah mapan dalam bekerja dengan produktivitas. Tetapi komunitas PHP bersiaga! JIT akan muncul di PHP 8, preload akan muncul di PHP 7.4, dan kerangka kerja di luar inti pengembangan PHP akan dikembangkan, yang menganggap bahwa PHP berfungsi sebagai daemon. Sudah waktunya untuk bereksperimen dengan sesuatu yang baru dan melihat apa yang dapat memberi kita.

Karena rilis PHP 8 masih jauh, dan kerangka kerja asinkron kurang cocok untuk tugas-tugas kami (mengapa - saya akan katakan di bawah), hari ini kami akan fokus pada preload, yang akan muncul dalam PHP 7.4, dan kerangka kerja untuk mengutilisasi PHP - RoadRunner.

Ini adalah versi teks laporan saya dengan Badoo PHP Meetup # 3 . Video semua pidato yang telah kami kumpulkan di pos ini .

PHP-FPM, Apache mod_php, dan cara-cara serupa untuk menjalankan skrip PHP dan permintaan proses (yang dijalankan oleh sebagian besar situs dan layanan; untuk kesederhanaan, saya akan menyebutnya PHP "klasik") bekerja berdasarkan apa-apa yang dibagikan dalam pengertian luas istilah:

  • negara tidak mencari-cari antara pekerja PHP;
  • negara tidak mencari-cari di antara berbagai permintaan.

Pertimbangkan ini dengan contoh skrip sederhana:

//  $app = \App::init(); $storage = $app->getCitiesStorage(); //   $name = $storage->getById($_COOKIE['city_id']); echo " : {$name}"; 

Untuk setiap permintaan, skrip dieksekusi dari baris pertama ke baris terakhir: walaupun faktanya inisialisasi, kemungkinan besar, tidak akan berbeda dari permintaan ke permintaan dan berpotensi dilakukan sekali (menghemat sumber daya), Anda masih harus mengulanginya untuk setiap permintaan. Kami tidak bisa hanya mengambil dan menyimpan variabel (misalnya, $app ) antara permintaan karena kekhasan cara kerja PHP "klasik".

Bagaimana jadinya jika kita melampaui lingkup PHP "klasik"? Sebagai contoh, skrip kami dapat berjalan terlepas dari permintaan, menginisialisasi dan memiliki loop permintaan di dalamnya, di dalamnya ia akan menunggu yang berikutnya, memprosesnya dan mengulangi loop tanpa membersihkan lingkungan (selanjutnya saya akan menyebut solusi ini "PHP sebagai daemon ").

 //  $app = \App::init(); $storage = $app->getCitiesStorage(); $cities = $storage->getAll(); //    while ($req = getNextRequest()) {    $name = $cities[$req->getCookie('city_id')];    echo " : {$name}"; } 

Kami tidak hanya dapat menyingkirkan inisialisasi yang diulang untuk setiap permintaan, tetapi juga menyimpan daftar kota satu kali ke variabel $cities dan menggunakannya dari berbagai permintaan tanpa mengakses di mana pun kecuali memori (ini adalah cara tercepat untuk mendapatkan data).

Kinerja solusi semacam itu berpotensi jauh lebih tinggi daripada PHP "klasik". Tetapi biasanya peningkatan produktivitas tidak diberikan secara gratis - Anda harus membayar sejumlah harga untuk itu. Mari kita lihat apa yang ada dalam kasus kita.

Untuk melakukan ini, mari kita sedikit mempersulit skrip kita dan alih-alih menampilkan variabel $name , kita akan mengisi array:

 -  $name = $cities[$req->getCookie('city_id')]; +  $names[] = $cities[$req->getCookie('city_id')]; 

Dalam kasus PHP "klasik", tidak akan ada masalah yang muncul - di akhir kueri, variabel $name akan dihancurkan dan setiap permintaan berikutnya akan berfungsi seperti yang diharapkan. Dalam hal memulai PHP sebagai daemon, setiap permintaan akan menambahkan kota lain ke variabel ini, yang akan mengarah pada pertumbuhan array yang tidak terkontrol hingga memori habis pada mesin.

Secara umum, tidak hanya memori yang dapat diakhiri - beberapa kesalahan lain dapat terjadi yang akan menyebabkan kematian proses. Dengan masalah seperti itu, PHP "klasik" menangani secara otomatis. Dalam kasus memulai PHP sebagai daemon, kita perlu memonitor daemon ini, me-restart itu jika crash.

Kesalahan jenis ini tidak menyenangkan, tetapi ada solusi efektif untuk mereka. Jauh lebih buruk jika, karena kesalahan, skrip tidak jatuh, tetapi secara tak terduga mengubah nilai beberapa variabel (misalnya, ini menghapus array $cities ). Dalam hal ini, semua permintaan berikutnya akan berfungsi dengan data yang salah.

Untuk meringkas, lebih mudah untuk menulis kode untuk "klasik" PHP (PHP-FPM, Apache mod_php dan sejenisnya) - itu membebaskan kita dari sejumlah masalah dan kesalahan. Tetapi untuk ini kami membayar dengan kinerja.

Dari contoh di atas, kita melihat bahwa di beberapa bagian kode, PHP menghabiskan sumber daya yang tidak bisa dihabiskan (atau terbuang sekali) dalam memproses setiap permintaan yang "klasik". Ini adalah area-area berikut:

  • koneksi file (termasuk, memerlukan, dll.);
  • inisialisasi (kerangka kerja, perpustakaan, wadah DI, dll);
  • meminta data dari penyimpanan eksternal (alih-alih disimpan dalam memori).

PHP telah ada selama bertahun-tahun dan bahkan mungkin menjadi populer berkat model kerja ini. Selama waktu ini, banyak metode dengan berbagai tingkat keberhasilan dikembangkan untuk memecahkan masalah yang dijelaskan. Saya menyebutkan beberapa di antaranya di artikel saya sebelumnya. Hari ini kita akan membahas dua solusi yang cukup baru untuk komunitas: preload dan RoadRunner.

Preload


Dari tiga poin yang tercantum di atas, preload dirancang untuk menangani overhead pertama saat menghubungkan file. Pada pandangan pertama, ini mungkin tampak aneh dan tidak berarti, karena PHP sudah memiliki OPcache, yang dibuat hanya untuk tujuan ini. Untuk memahami esensi, mari kita profil nyata dengan bantuan perf , di mana OPcache diaktifkan, dengan hit rate sama dengan 100%.



Meskipun OPcache, kami melihat bahwa persistent_compile_file mengambil 5,84% dari waktu eksekusi kueri.

Untuk memahami mengapa ini terjadi, kita dapat melihat sumber-sumber zend_accel_load_script . Dapat dilihat dari mereka bahwa, meskipun ada OPcache, dengan setiap panggilan untuk include/require tanda tangan kelas dan fungsi disalin dari memori bersama ke memori proses pekerja, dan berbagai pekerjaan tambahan dilakukan. Dan pekerjaan ini harus dilakukan untuk setiap permintaan, karena pada akhirnya memori dari proses pekerja dihapus.



Ini diperparah oleh sejumlah besar panggilan sertakan / mengharuskan yang biasanya kita buat dalam satu permintaan. Misalnya, Symfony 4 menyertakan sekitar 310 file sebelum mengeksekusi baris kode pertama yang bermanfaat. Terkadang ini terjadi secara implisit: untuk membuat turunan dari kelas A, di bawah ini, PHP akan memuat secara otomatis semua kelas lainnya (B, C, D, E, F, G). Dan terutama dalam hal ini, ketergantungan Composer yang menyatakan fungsi menonjol: untuk memastikan bahwa fungsi-fungsi ini akan tersedia selama pelaksanaan kode pengguna, Komposer selalu dipaksa untuk menghubungkannya terlepas dari penggunaan, karena PHP tidak memiliki fungsi pengisian otomatis dan mereka tidak dapat dimuat pada saat panggilan.

 class A extends \B implements \C {    use \D;    const SOME_CONST = \E::E1;    private static $someVar = \F::F1;    private $anotherVar = \G::G1; } 


Bagaimana preload bekerja


Preload memiliki satu pengaturan utama, opcache.preload, di mana jalur ke skrip PHP dilewatkan. Script ini akan dieksekusi satu kali ketika memulai PHP-FPM / Apache /, dll., Dan semua tanda tangan kelas, metode dan fungsi yang akan dideklarasikan dalam file ini akan tersedia untuk semua skrip yang memproses permintaan dari baris pertama eksekusi mereka (penting Catatan: ini tidak berlaku untuk variabel dan konstanta global - nilainya akan diatur ulang ke nol setelah akhir fase preload). Anda tidak lagi perlu menyertakan / mengharuskan panggilan dan fungsi tanda tangan / kelas salin dari memori bersama ke memori proses: semuanya dinyatakan tidak dapat diubah dan karena ini semua proses dapat merujuk ke lokasi memori yang sama yang memuatnya.

Biasanya kelas dan fungsi yang kita butuhkan ada di file yang berbeda dan tidak nyaman untuk menggabungkannya menjadi satu skrip preload. Tetapi ini tidak perlu dilakukan: karena preload adalah skrip PHP biasa, kita bisa menggunakan include / require atau opcache_compile_file () dari skrip preload untuk semua file yang kita butuhkan. Selain itu, karena semua file ini akan dimuat sekali, PHP akan dapat membuat optimasi tambahan yang tidak dapat dilakukan saat kami secara terpisah menghubungkan file-file ini pada saat permintaan. PHP membuat optimasi hanya dalam kerangka setiap file yang terpisah, tetapi dalam kasus preload, untuk semua kode yang dimuat dalam fase preload.

Prapuat patokan awal


Untuk menunjukkan dalam praktiknya manfaat preload, saya mengambil satu titik akhir Badoo yang terikat CPU. Backend kami umumnya ditandai dengan beban yang terikat CPU. Fakta ini adalah jawaban untuk pertanyaan mengapa kami tidak mempertimbangkan kerangka kerja asinkron: mereka tidak memberikan keuntungan apa pun dalam kasus beban yang terikat CPU dan pada saat yang sama semakin menyulitkan kode (perlu ditulis secara berbeda), serta untuk bekerja dengan jaringan, disk, dll. diperlukan driver asinkron khusus.

Untuk sepenuhnya menghargai manfaat preload, untuk percobaan saya unduh dengan semua file yang diperlukan skrip uji saat bekerja, dan memuatnya dengan kemiripan beban produksi normal menggunakan wrk2 - analog lebih maju dari Apache Benchmark, tetapi sama sederhana .

Untuk mencoba preload, Anda harus meningkatkan ke PHP 7.4 (kami sekarang memiliki PHP 7.2). Saya mengukur kinerja PHP 7.2, PHP 7.4 tanpa preload dan PHP 7.4 dengan preload. Hasilnya adalah gambar seperti itu:



Jadi, transisi dari PHP 7.2 ke PHP 7.4 memberikan + 10% ke kinerja di titik akhir kami, dan preload memberi 10% lagi dari atas.

Dalam kasus preload, hasilnya akan sangat tergantung pada jumlah file yang terhubung dan kompleksitas dari logika yang dapat dieksekusi: jika banyak file terhubung dan logikanya sederhana, preload akan memberikan lebih banyak daripada jika ada beberapa file dan logikasinya rumit.

Nuansa preload


Apa yang meningkatkan produktivitas biasanya memiliki kerugian. Preload memiliki banyak nuansa, yang akan saya berikan di bawah ini. Semuanya perlu diperhitungkan, tetapi hanya satu (pertama) yang bisa menjadi fundamental.

Ubah - mulai ulang


Karena semua file preload dikompilasi hanya pada startup, ditandai sebagai tidak dapat diubah dan tidak dikompilasi ulang di masa depan, satu-satunya cara untuk menerapkan perubahan pada file-file ini adalah me-restart (memuat atau me-restart) PHP-FPM / Apache /, dll.

Dalam kasus pemuatan ulang, PHP mencoba memulai ulang seakurat mungkin: permintaan pengguna tidak akan terputus, namun demikian, ketika fase preload sedang berlangsung, semua permintaan baru akan menunggu sampai selesai. Jika tidak ada banyak kode dalam preload, ini mungkin tidak menimbulkan masalah, tetapi jika Anda mencoba mengunduh seluruh aplikasi, itu akan dipenuhi dengan peningkatan waktu respons yang signifikan selama restart.

Juga, restart (terlepas dari apakah itu memuat ulang atau restart) memiliki fitur penting - sebagai akibat dari tindakan ini, OPcache dihapus. Artinya, semua permintaan setelah itu akan berfungsi dengan cache opcode yang dingin, yang dapat meningkatkan waktu respons lebih banyak lagi.

Karakter yang tidak ditentukan


Untuk preload untuk memuat kelas, semua yang bergantung padanya harus ditentukan hingga saat ini. Untuk kelas di bawah ini, ini berarti bahwa semua kelas lain (B, C, D, E, F, G), variabel $someGlobalVar dan SOME_CONST konstan harus tersedia sebelum mengkompilasi kelas ini. Karena skrip preload hanyalah kode PHP biasa, kita dapat mendefinisikan autoloader. Dalam hal ini, semua yang terhubung dengan kelas lain akan dimuat olehnya secara otomatis. Tetapi ini tidak bekerja dengan variabel dan konstanta: kita sendiri harus memastikan bahwa mereka didefinisikan pada saat kelas ini dideklarasikan.

 class A extends \B implements \C {    use \D;    const SOME_CONST = \E::E1;    private static $someVar = \F::F1;    private $anotherVar = \G::G1;    private $varLink = $someGlobalVar;    private $constLink = SOME_CONST; } 

Untungnya, preload berisi alat yang cukup untuk memahami apakah Anda mendapatkan sesuatu atau tidak. Pertama, ini adalah pesan peringatan dengan informasi tentang apa yang gagal dimuat dan alasannya:

 PHP Warning: Can't preload class MyTestClass with unresolved initializer for constant RAND in /local/preload-internal.php on line 6 PHP Warning: Can't preload unlinked class MyTestClass: Unknown parent AnotherClass in /local/preload-internal.php on line 5 

Kedua, preload menambahkan bagian terpisah ke hasil fungsi opcache_get_status (), yang menunjukkan apa yang berhasil dimuat pada fase preload:



Bidang kelas / optimasi konstan


Seperti yang saya tulis di atas, preload menyelesaikan nilai-nilai bidang / konstanta kelas dan menyimpannya. Ini memungkinkan Anda untuk mengoptimalkan kode: selama pemrosesan permintaan, data sudah siap dan tidak perlu diturunkan dari data lain. Tetapi ini dapat menyebabkan hasil yang tidak jelas, yang ditunjukkan oleh contoh berikut:

 const.php: <?php define('MYTESTCONST', mt_rand(1, 1000)); 

 preload.php: <?php include 'const.php'; class MyTestClass {    const RAND = MYTESTCONST; } 

 script.php: <?php include 'const.php'; echo MYTESTCONST, ', ', MyTestClass::RAND; // 32, 154 

Hasilnya adalah situasi yang berlawanan dengan intuisi: kelihatannya konstanta harus sama, karena salah satu dari mereka diberi nilai yang lain, tetapi dalam kenyataannya tidak demikian. Hal ini disebabkan oleh fakta bahwa konstanta global, berbeda dengan konstanta / bidang kelas, secara paksa dihapus setelah fase preload berakhir, sementara konstanta / bidang kelas diselesaikan dan disimpan. Ini mengarah pada fakta bahwa selama pelaksanaan permintaan kita harus mendefinisikan konstanta global lagi, sebagai akibatnya ia bisa mendapatkan nilai yang berbeda.

Tidak dapat mendeklarasikan ulang someFunc ()


Dalam kasus kelas, situasinya sederhana: biasanya kita tidak secara eksplisit menghubungkannya, tetapi menggunakan autoloader. Ini berarti bahwa jika kelas didefinisikan dalam fase preload, maka autoloader tidak akan dieksekusi selama permintaan dan kami tidak akan mencoba untuk menghubungkan kelas ini untuk kedua kalinya.

Situasinya berbeda dengan fungsi: kita harus menghubungkannya secara eksplisit. Ini dapat mengarah pada situasi di mana dalam skrip preload kami akan menghubungkan semua file yang diperlukan dengan fungsi, dan selama permintaan kami akan mencoba melakukannya lagi (contoh khasnya adalah bootloader Komposer: selalu akan mencoba untuk menghubungkan semua file dengan fungsi). Dalam hal ini, kami mendapatkan kesalahan: fungsi telah ditentukan dan tidak dapat didefinisikan ulang.

Masalah ini dapat diselesaikan dengan berbagai cara. Dalam kasus Komposer, Anda dapat, misalnya, menghubungkan semua yang ada dalam fase preload, dan tidak menghubungkan apa pun yang terkait dengan Komposer selama permintaan. Solusi lain adalah tidak menghubungkan file dengan fungsi secara langsung, tetapi untuk melakukan ini melalui file proxy dengan cek untuk function_exists (), seperti, misalnya, Guzzle HTTP tidak .



PHP 7.4 belum dirilis secara resmi (belum)


Nuansa ini akan menjadi tidak relevan setelah beberapa waktu, tetapi sejauh ini versi PHP 7.4 belum secara resmi dirilis dan tim PHP dalam catatan rilis secara eksplisit menulis : "Tolong JANGAN menggunakan versi ini dalam produksi, ini adalah versi tes awal." Selama percobaan kami dengan preload, kami menemukan beberapa bug, kami memperbaikinya sendiri dan bahkan mengirim sesuatu ke hulu. Untuk menghindari kejutan, lebih baik menunggu rilis resmi.

Roadrunner


RoadRunner adalah daemon yang ditulis dalam Go, yang, di satu sisi, menciptakan pekerja PHP dan memonitor mereka (mulai / berakhir / restart sesuai kebutuhan), dan di sisi lain, menerima permintaan dan meneruskannya ke pekerja ini. Dalam hal ini, pekerjaannya tidak berbeda dengan pekerjaan PHP-FPM (di mana ada juga proses master yang memonitor pekerja). Namun masih ada perbedaan. Kuncinya adalah bahwa RoadRunner tidak menyetel ulang status skrip setelah penyelesaian kueri.

Jadi, jika kita mengingat daftar sumber daya apa yang dihabiskan untuk PHP "klasik", RoadRunner memungkinkan Anda untuk menangani semua poin (preload, seperti yang kita ingat, hanya dengan yang pertama):

  • koneksi file (termasuk, memerlukan, dll.);
  • inisialisasi (kerangka kerja, perpustakaan, wadah DI, dll);
  • meminta data dari penyimpanan eksternal (alih-alih disimpan dalam memori).

Contoh Hello World RoadRunner terlihat seperti ini:

 $relay = new Spiral\Goridge\StreamRelay(STDIN, STDOUT); $psr7 = new Spiral\RoadRunner\PSR7Client(new Spiral\RoadRunner\Worker($relay)); while ($req = $psr7->acceptRequest()) {        $resp = new \Zend\Diactoros\Response();        $resp->getBody()->write("hello world");        $psr7->respond($resp); } 

Kami akan mencoba titik akhir kami saat ini, yang kami uji dengan preload, untuk berjalan di RoadRunner tanpa modifikasi, memuatnya dan mengukur kinerja. Tidak ada modifikasi - karena jika tidak patokan tidak akan sepenuhnya jujur.

Mari kita coba untuk mengadaptasi contoh Hello World untuk ini.

Pertama, seperti yang saya tulis di atas, kami tidak ingin pekerja jatuh jika terjadi kesalahan. Untuk melakukan ini, kita perlu membungkus semuanya dalam try..catch global. Kedua, karena skrip kami tidak tahu apa-apa tentang Zend Diactoros, untuk jawabannya kami perlu mengonversi hasilnya. Untuk ini kami menggunakan fungsi-ob_. Ketiga, skrip kami tidak tahu apa-apa tentang sifat permintaan PSR-7. Solusinya adalah mengisi lingkungan PHP standar dari entitas ini. Dan keempat, skrip kami mengharapkan permintaan untuk mati dan seluruh negara akan dihapus. Karena itu, dengan RoadRunner kita perlu melakukan pembersihan ini sendiri.

Dengan demikian, versi Hello World awal berubah menjadi seperti ini:

 while ($req = $psr7->acceptRequest()) {    try {        $uri = $req->getUri();        $_COOKIE = $req->getCookieParams();        $_POST = $req->getParsedBody();        $_SERVER = [            'REQUEST_METHOD' => $req->getMethod(),            'HTTP_HOST' => $uri->getHost(),            'DOCUMENT_URI' => $uri->getPath(),            'SERVER_NAME' => $uri->getHost(),            'QUERY_STRING' => $uri->getQuery(),            // ...        ];        ob_start();        // our logic here        $output = ob_get_contents();        ob_clean();               $resp = new \Zend\Diactoros\Response();        $resp->getBody()->write($output, 200);        $psr7->respond($resp);    } catch (\Throwable $Throwable) {        // some error handling logic here    }    \UDS\Event::flush();    \PinbaClient::sendAll();    \PinbaClient::flushAll();    \HTTP::clear();    \ViewFactory::clear();    \Logger::clearCaches();       // ... } 


RoadRunner Benchmarks


Nah, saatnya untuk meluncurkan tolok ukur.



Hasilnya tidak memenuhi harapan: RoadRunner memungkinkan Anda untuk lebih banyak faktor yang menyebabkan kerugian kinerja daripada preload, tetapi hasilnya lebih buruk. Mari kita cari tahu mengapa ini terjadi, seperti biasa, dengan menjalankan perf untuk ini.



Dalam hasil perf, kita melihat phar_compile_file. Ini karena kami menyertakan beberapa file selama eksekusi skrip, dan karena OPcache tidak diaktifkan (RoadRunner menjalankan skrip sebagai CLI, di mana OPcache dimatikan secara default), file-file ini dikompilasi lagi dengan setiap permintaan.

Edit konfigurasi RoadRunner - aktifkan OPcache:





Hasil ini sudah lebih seperti yang kami harapkan: RoadRunner mulai menunjukkan lebih banyak kinerja daripada preload. Tapi mungkin kita akan bisa mendapatkan lebih banyak lagi!

Tampaknya tidak ada yang lebih aneh dengan perf - mari kita lihat kode PHP. Cara termudah untuk membuat profil adalah menggunakan phpspy : tidak memerlukan modifikasi kode PHP - Anda hanya perlu menjalankannya di konsol. Mari kita lakukan ini dan buat grafik nyala:



Karena kami sepakat untuk tidak mengubah logika aplikasi kami untuk kemurnian percobaan, kami tertarik pada cabang tumpukan yang terkait dengan pekerjaan RoadRunner:



Bagian utama adalah memanggil fread (), hampir tidak ada yang bisa dilakukan dengan ini. Tetapi kita melihat beberapa cabang lain di \ Spiral \ RoadRunner \ PSR7Client :: acceptRequest () , kecuali untuk ketakutan itu sendiri. Anda dapat memahami maknanya dengan melihat kode sumber:

    /**     * @return ServerRequestInterface|null     */    public function acceptRequest()    {        $rawRequest = $this->httpClient->acceptRequest();        if ($rawRequest === null) {            return null;        }        $_SERVER = $this->configureServer($rawRequest['ctx']);        $request = $this->requestFactory->createServerRequest(            $rawRequest['ctx']['method'],            $rawRequest['ctx']['uri'],            $_SERVER        );        parse_str($rawRequest['ctx']['rawQuery'], $query);        $request = $request            ->withProtocolVersion(static::fetchProtocolVersion($rawRequest['ctx']['protocol']))            ->withCookieParams($rawRequest['ctx']['cookies'])            ->withQueryParams($query)            ->withUploadedFiles($this->wrapUploads($rawRequest['ctx']['uploads'])); 

Menjadi jelas bahwa RoadRunner sedang mencoba untuk membuat objek permintaan yang sesuai dengan PSR-7 menggunakan array serial. Jika kerangka kerja Anda bekerja dengan objek permintaan PSR-7 secara langsung (misalnya, Symfony tidak berfungsi ), maka ini sepenuhnya dibenarkan. Dalam kasus lain, PSR-7 menjadi tautan tambahan sebelum permintaan dikonversi ke aplikasi Anda. Mari kita hapus tautan perantara ini dan lihat kembali hasilnya:



Skrip tes cukup mudah, jadi saya berhasil memeras sebagian besar kinerja - + 17% dibandingkan dengan PHP murni (saya ingat preload memberi + 10% pada skrip yang sama).

Nuansa RoadRunner


Secara umum, penggunaan RoadRunner adalah perubahan yang lebih serius dari sekadar dimasukkannya preload, sehingga nuansa di sini bahkan lebih signifikan.

-, RoadRunner, , PHP- , , , : , , .

-, RoadRunner , ยซยป โ€” . / RoadRunner ; , , , , - .

-, endpoint', , , RoadRunner. .

Kesimpulan


, ยซยป PHP, , preload RoadRunner.

PHP ยซยป (PHP-FPM, Apache mod_php ) . - , . , , preload JIT.

, , , RoadRunner, .

, (: ):

  • PHP 7.2 โ€” 845 RPS;
  • PHP 7.4 โ€” 931 RPS;
  • RoadRunner โ€” 987 RPS;
  • PHP 7.4 + preload โ€” 1030 RPS;
  • RoadRunner โ€” 1089 RPS.

Badoo PHP 7.4 , ( ).

RoadRunner , , , , .

Terima kasih atas perhatian anda!

Source: https://habr.com/ru/post/id472528/


All Articles