Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel

Ekosistem PHP saat ini memiliki dua konektor untuk bekerja dengan server Tarantool - ini adalah ekstensi resmi PECL tarantool/tarantool-php, ditulis dalam C, dan tarantool-php/client, ditulis dalam PHP. Saya adalah penulis yang terakhir.

Pada artikel ini, saya ingin berbagi hasil pengujian kinerja kedua perpustakaan dan menunjukkan bagaimana, dengan sedikit perubahan pada kode, Anda dapat mencapai peningkatan kinerja sebesar 3-5 (pada tes sintetis!).

Apa yang akan kita uji?

Kami akan menguji yang disebutkan di atas sinkronis konektor berjalan secara asinkron, paralel, dan paralel asinkron. 🙂 Kami juga tidak ingin menyentuh kode konektor itu sendiri. Saat ini ada beberapa ekstensi yang tersedia untuk mencapai apa yang Anda inginkan:

  • swool - kerangka kerja asinkron berkinerja tinggi untuk PHP. Digunakan oleh raksasa internet seperti Alibaba dan Baidu. Sejak versi 4.1.0 metode ajaib telah muncul SwooleRuntime::enableCoroutine(), yang memungkinkan Anda untuk “mengonversi pustaka jaringan PHP yang sinkron menjadi pustaka asinkron dengan satu baris kode”.
  • Async sampai saat ini merupakan ekstensi yang sangat menjanjikan untuk pekerjaan asynchronous di PHP. Mengapa sampai saat ini? Sayangnya, karena alasan yang tidak saya ketahui, penulis menghapus repositori tersebut dan nasib proyek di masa depan tidak jelas. Saya harus menggunakannya satu per satu dari garpu. Seperti Swoole, ekstensi ini memungkinkan Anda mengenakan celana dengan mudah hanya dengan jentikan pergelangan tangan untuk mengaktifkan asinkron dengan mengganti implementasi standar aliran TCP dan TLS dengan versi asinkronnya. Ini dilakukan melalui opsi “async.tcp = 1".
  • Paralel ― ekstensi yang cukup baru dari Joe Watkins yang terkenal, penulis perpustakaan seperti phpdbg, apcu, pthreads, pcov, uopz. Ekstensi ini menyediakan API untuk multithreading di PHP dan diposisikan sebagai pengganti pthreads. Keterbatasan signifikan dari perpustakaan ini adalah ia hanya berfungsi dengan PHP versi ZTS (Zend Thread Safe).

Bagaimana kita akan mengujinya?

Mari kita luncurkan instance Tarantool dengan write-ahead logging dinonaktifkan (wal_mode = tidak ada) dan peningkatan buffer jaringan (baca ke depan = 1 * 1024 * 1024). Opsi pertama akan menghilangkan pekerjaan dengan disk, yang kedua akan memungkinkan untuk membaca lebih banyak permintaan dari buffer sistem operasi dan dengan demikian meminimalkan jumlah panggilan sistem.

Untuk benchmark yang bekerja dengan data (penyisipan, penghapusan, pembacaan, dll.), sebelum memulai benchmark, ruang memtx akan dibuat (kembali), di mana nilai indeks utama dibuat oleh generator nilai integer yang diurutkan ​(urutan).
Ruang DDL terlihat seperti ini:

space = box.schema.space.create(config.space_name, {id = config.space_id, temporary = true})
space:create_index('primary', {type = 'tree', parts = {1, 'unsigned'}, sequence = true})
space:format({{name = 'id', type = 'unsigned'}, {name = 'name', type = 'string', is_nullable = false}})

Jika perlu, sebelum menjalankan benchmark, ruang tersebut diisi dengan 10,000 tupel formulir

{id, "tuplе_<id>"}

Tupel diakses menggunakan nilai kunci acak.

Benchmark itu sendiri adalah satu permintaan ke server, yang dieksekusi 10,000 kali (putaran), yang kemudian dieksekusi secara iterasi. Iterasi diulangi hingga semua deviasi waktu antara 5 iterasi berada dalam kesalahan yang dapat diterima sebesar 3%*. Setelah itu diambil hasil rata-ratanya. Ada jeda 1 detik di antara iterasi untuk mencegah pelambatan prosesor. Pengumpul sampah Lua dinonaktifkan sebelum setiap iterasi dan dipaksa untuk memulai setelah iterasi selesai. Proses PHP diluncurkan hanya dengan ekstensi yang diperlukan untuk benchmark, dengan buffering keluaran diaktifkan dan pengumpul sampah dinonaktifkan.

* Jumlah putaran, iterasi, dan ambang kesalahan dapat diubah dalam pengaturan benchmark.

Lingkungan pengujian

Hasil yang dipublikasikan di bawah ini dibuat pada MacBookPro (2015), sistem operasi - Fedora 30 (kernel versi 5.3.8-200.fc30.x86_64). Tarantool diluncurkan di buruh pelabuhan dengan parameter "--network host".

Versi paket:

Tarantool: 2.3.0-115-g5ba5ed37e
Buruh pelabuhan: 19.03.3, buat a872fc2f86
PHP: 7.3.11 (cli) (dibangun: 22 Okt 2019 08:11:04)
tarantool/klien: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch untuk 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-paralel: 1.1.3

* Sayangnya, konektor resmi tidak berfungsi dengan PHP versi > 7.2. Untuk mengkompilasi dan menjalankan ekstensi pada PHP 7.3, saya harus menggunakan tambalan.

Temuan

Mode sinkron

Protokol Tarantool menggunakan format biner Paket Pesan untuk membuat serial pesan. Di konektor PECL, serialisasi tersembunyi jauh di dalam perpustakaan dan memengaruhi proses pengkodean dari kode userland sepertinya tidak mungkin. Sebaliknya, konektor PHP murni memberikan kemampuan untuk menyesuaikan proses pengkodean dengan memperluas encoder standar atau dengan menggunakan implementasi Anda sendiri. Ada dua pembuat enkode yang tersedia, satu didasarkan pada msgpack/msgpack-php (ekstensi PECL MessagePack resmi), yang lainnya aktif rybakit/msgpack (dalam PHP murni).

Sebelum membandingkan konektor, kami akan mengukur kinerja encoder MessagePack untuk konektor PHP dan dalam pengujian lebih lanjut kami akan menggunakan salah satu yang menunjukkan hasil terbaik:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Meskipun versi PHP (Murni) lebih rendah daripada ekstensi PECL dalam hal kecepatan, dalam proyek nyata saya tetap merekomendasikan untuk menggunakannya rybakit/msgpack, karena dalam ekstensi resmi MessagePack, spesifikasi format hanya diterapkan sebagian (misalnya, tidak ada dukungan untuk tipe data khusus, yang tanpanya Anda tidak akan dapat menggunakan Desimal - tipe data baru yang diperkenalkan di Tarantool 2.3) dan memiliki sejumlah lainnya masalah (termasuk masalah kompatibilitas dengan PHP 7.4). Secara umum, proyek tersebut terlihat terbengkalai.

Jadi, mari kita ukur kinerja konektor dalam mode sinkron:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Terlihat dari grafik, konektor PECL (Tarantool) menunjukkan kinerja yang lebih baik dibandingkan konektor PHP (Client). Namun hal ini tidak mengherankan, mengingat bahwa yang terakhir, selain diimplementasikan dalam bahasa yang lebih lambat, sebenarnya melakukan lebih banyak pekerjaan: sebuah objek baru dibuat dengan setiap panggilan Meminta и Tanggapan (dalam kasus Pilih - juga Kriteria, dan dalam kasus Update/Upsert ― Operasi), entitas terpisah Koneksi, Packer и Handler mereka juga menambahkan overhead. Tentu saja, fleksibilitas ada harganya. Namun secara umum interpreter PHP menunjukkan performa yang baik, walaupun terdapat perbedaan namun tidak signifikan dan mungkin akan lebih sedikit lagi jika menggunakan preloading di PHP 7.4, belum lagi JIT di PHP 8.

Mari kita lanjutkan. Tarantool 2.0 menambahkan dukungan untuk SQL. Mari kita coba melakukan operasi Pilih, Sisipkan, Perbarui, dan Hapus menggunakan protokol SQL dan bandingkan hasilnya dengan yang setara dengan noSQL (biner):

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Hasil SQL tidak terlalu mengesankan (izinkan saya mengingatkan Anda bahwa kami masih menguji mode sinkron). Namun, saya tidak akan kecewa dengan hal ini sebelumnya; dukungan SQL masih dalam pengembangan aktif (relatif baru, misalnya, dukungan telah ditambahkan pernyataan yang disiapkan) dan, dilihat dari daftarnya masalah, mesin SQL akan menjalani sejumlah optimasi di masa depan.

Asinkron

Nah, sekarang mari kita lihat bagaimana ekstensi Async dapat membantu kita meningkatkan hasil di atas. Untuk menulis program asinkron, ekstensi menyediakan API berdasarkan coroutine, yang akan kita gunakan. Kami menemukan secara empiris bahwa jumlah coroutine optimal untuk lingkungan kami adalah 25:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
“Sebarkan” 10,000 operasi ke 25 coroutine dan lihat apa yang terjadi:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Jumlah operasi per detik meningkat lebih dari 3 kali lipat tarantool-php/client!

Sayangnya, konektor PECL tidak dimulai dengan ext-async.

Bagaimana dengan SQL?

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Seperti yang Anda lihat, dalam mode asinkron, perbedaan antara protokol biner dan SQL berada dalam batas kesalahan.

swool

Sekali lagi kita mengetahui jumlah coroutine yang optimal, kali ini untuk Swoole:
Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Mari kita berhenti di 25. Mari ulangi trik yang sama seperti pada ekstensi Async - distribusikan 10,000 operasi di antara 25 coroutine. Selain itu, kami akan menambahkan pengujian lain di mana kami akan membagi semua pekerjaan menjadi 2 dua proses (yaitu, setiap proses akan melakukan 5,000 operasi dalam 25 coroutine). Proses akan dibuat menggunakan Proses Swoole.

Hasil:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Swole menunjukkan hasil yang sedikit lebih rendah dibandingkan dengan Async ketika dijalankan dalam satu proses, tetapi dengan 2 proses, gambarnya berubah secara dramatis (angka 2 tidak dipilih secara kebetulan; di mesin saya, 2 proseslah yang menunjukkan hasil terbaik).

Omong-omong, ekstensi Async juga memiliki API untuk bekerja dengan proses, tetapi di sana saya tidak melihat adanya perbedaan dari menjalankan benchmark dalam satu atau lebih proses (mungkin saya melakukan kesalahan di suatu tempat).

SQL vs protokol biner:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Seperti halnya Async, perbedaan antara operasi biner dan SQL dihilangkan dalam mode asinkron.

Paralel

Karena ekstensi Paralel bukan tentang coroutine, tetapi tentang thread, mari kita ukur jumlah thread paralel yang optimal:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Itu sama dengan 16 di mesin saya. Mari kita jalankan benchmark konektor pada 16 thread paralel:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Seperti yang Anda lihat, hasilnya bahkan lebih baik dibandingkan dengan ekstensi asinkron (tidak termasuk Swoole yang berjalan pada 2 proses). Perhatikan bahwa untuk konektor PECL, operasi Pembaruan dan Upsert kosong. Hal ini disebabkan oleh fakta bahwa operasi ini gagal karena kesalahan - Saya tidak tahu apakah itu kesalahan ext-parallel, ext-tarantool, atau keduanya.

Sekarang mari kita bandingkan kinerja SQL:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel
Perhatikan kemiripannya dengan grafik untuk konektor yang berjalan secara sinkron?

Bersama

Dan terakhir, mari kita rangkum semua hasil dalam satu grafik untuk melihat gambaran keseluruhan untuk ekstensi yang diuji. Mari tambahkan satu pengujian baru ke bagan, yang belum kita lakukan - mari jalankan coroutine Async secara paralel menggunakan Parallel*. Ide untuk mengintegrasikan ekstensi di atas sudah ada dibahas penulis, tetapi tidak ada konsensus yang tercapai, Anda harus melakukannya sendiri.

* Coroutine Swoole tidak dapat diluncurkan dengan Paralel; tampaknya ekstensi ini tidak kompatibel.

Jadi, hasil akhirnya:

Mempercepat konektor PHP untuk Tarantool menggunakan Async, Swoole dan Parallel

Alih-alih sebuah kesimpulan

Menurut saya, hasilnya ternyata cukup lumayan, dan entah kenapa saya yakin ini bukan batasnya! Apakah Anda perlu memutuskan ini dalam proyek nyata semata-mata untuk diri Anda sendiri, saya hanya akan mengatakan bahwa bagi saya ini adalah eksperimen menarik yang memungkinkan Anda mengevaluasi seberapa banyak Anda dapat "memeras" konektor TCP sinkron dengan sedikit usaha. Jika Anda memiliki ide untuk meningkatkan tolok ukur, saya akan dengan senang hati mempertimbangkan permintaan penarikan Anda. Semua kode dengan instruksi peluncuran dan hasil dipublikasikan secara terpisah repositori.

Sumber: www.habr.com

Tambah komentar