ProHoster > Blog > yönetim > Async, Swoole ve Paralel kullanarak Tarantool için PHP bağlayıcılarını hızlandırma
Async, Swoole ve Paralel kullanarak Tarantool için PHP bağlayıcılarını hızlandırma
PHP ekosisteminde şu anda Tarantool sunucusuyla çalışmak için iki konektör bulunmaktadır - bu resmi PECL uzantısıdır tarantool/tarantool-php, C ile yazılmış ve tarantool-php/istemci, PHP'de yazılmıştır. Ben ikincisinin yazarıyım.
Bu yazımda her iki kütüphanenin performans testi sonuçlarını paylaşmak ve kodda minimum değişiklik yaparak nasıl 3-5 performans artışı elde edebileceğinizi göstermek istiyorum (sentetik testlerde!).
Neyi test edeceğiz?
Yukarıda bahsedilenleri test edeceğiz senkron Eşzamansız, paralel ve eş zamanlı olmayan paralel çalışan konektörler. 🙂 Ayrıca konnektörlerin kodlarına da dokunmak istemiyoruz. Şu anda istediğinizi elde etmek için birkaç uzantı mevcut:
swoole - PHP için yüksek performanslı eşzamansız bir çerçeve. Alibaba ve Baidu gibi internet devleri tarafından kullanılıyor. 4.1.0 sürümünden bu yana sihirli bir yöntem ortaya çıktı SwooleRuntime::enableCoroutine(), "senkron PHP ağ kitaplıklarını tek satır kodla eşzamansız olanlara dönüştürmenize" olanak tanır.
Async, yakın zamana kadar PHP'deki asenkron çalışmalar için oldukça umut verici bir eklentiydi. Neden yakın zamana kadar? Maalesef bilmediğim bir nedenden dolayı yazar depoyu sildi ve projenin gelecekteki kaderi belirsiz. onu kullanmam gerekecek biri çatallardan. Swoole gibi, bu uzantı da TCP ve TLS akışlarının standart uygulamasını eşzamansız sürümleriyle değiştirerek eşzamansızlığı etkinleştirmek için bir bilek hareketiyle pantolonunuzu kolayca açmanıza olanak tanır. Bu “seçenek aracılığıyla yapılır”async.tcp = 1".
Paralel - phpdbg, apcu, pthreads, pcov, uopz gibi kitaplıkların yazarı, tanınmış Joe Watkins'ten oldukça yeni bir uzantı. Uzantı, PHP'de çoklu iş parçacığı için bir API sağlar ve pthread'lerin yerini alacak şekilde konumlandırılmıştır. Kütüphanenin önemli bir sınırlaması, yalnızca PHP'nin ZTS (Zend Thread Safe) sürümüyle çalışmasıdır.
Nasıl test edeceğiz?
Yazma öncesi günlük kaydı devre dışı bırakılmış bir Tarantool örneği başlatalım (wal_mode = yok) ve artırılmış ağ arabelleği (ileri okuma = 1 * 1024 * 1024). İlk seçenek diskle çalışmayı ortadan kaldıracak, ikincisi ise işletim sistemi arabelleğinden daha fazla istek okumayı mümkün kılacak ve böylece sistem çağrılarının sayısını en aza indirecektir.
Verilerle çalışan kıyaslamalar için (ekleme, silme, okuma vb.), kıyaslamayı başlatmadan önce, birincil dizin değerlerinin sıralı tamsayı değerlerinin bir oluşturucusu tarafından oluşturulduğu bir memtx alanı (yeniden) oluşturulacaktır (sıra).
DDL alanı şuna benzer:
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}})
Gerekirse, karşılaştırmayı çalıştırmadan önce alan formun 10,000 tuple'ı ile doldurulur.
{id, "tuplе_<id>"}
Tuple'lara rastgele bir anahtar değeri kullanılarak erişilir.
Karşılaştırmanın kendisi, sunucuya yapılan tek bir istektir; bu, 10,000 kez (devir) gerçekleştirilir ve bu da yinelemeler halinde yürütülür. Yinelemeler, 5 yineleme arasındaki tüm zaman sapmaları %3'lük kabul edilebilir bir hata dahilinde olana kadar tekrarlanır*. Bundan sonra ortalama sonuç alınır. İşlemcinin yavaşlamasını önlemek için yinelemeler arasında 1 saniyelik bir duraklama vardır. Lua'nın çöp toplayıcısı her yinelemeden önce devre dışı bırakılır ve tamamlandıktan sonra başlamaya zorlanır. PHP işlemi yalnızca karşılaştırma için gerekli uzantılarla, çıktı arabelleği etkin ve çöp toplayıcı devre dışı bırakılarak başlatılır.
* Benchmark ayarlarında devir sayısı, yinelemeler ve hata eşiği değiştirilebilir.
Test ortamı
Aşağıda yayınlanan sonuçlar, MacBookPro (2015) işletim sistemi - Fedora 30 (çekirdek sürümü 5.3.8-200.fc30.x86_64) üzerinde yapılmıştır. Tarantool docker'da şu parametreyle başlatıldı:--network host".
* Ne yazık ki, resmi bağlayıcı PHP sürüm > 7.2 ile çalışmıyor. Uzantıyı PHP 7.3'te derlemek ve çalıştırmak için kullanmak zorunda kaldım yama.
Bulgular
Senkron mod
Tarantool protokolü ikili bir format kullanır Mesaj Paketi mesajları serileştirmek için. PECL bağlayıcısında serileştirme, kitaplığın derinliklerinde gizlidir ve kullanıcı alanı kodundan kodlama sürecini etkiler mümkün değil. Saf PHP bağlayıcısı ise tam tersine, standart kodlayıcıyı genişleterek veya kendi uygulamanızı kullanarak kodlama sürecini özelleştirme yeteneği sağlar. Kutudan çıkan iki kodlayıcı mevcuttur; biri msgpack/msgpack-php (resmi Mesaj Paketi PECL uzantısı), diğeri açık rybakit/msgpack (saf PHP'de).
Bağlayıcıları karşılaştırmadan önce, PHP bağlayıcısı için Mesaj Paketi kodlayıcılarının performansını ölçeceğiz ve sonraki testlerde en iyi sonucu göstereni kullanacağız:
PHP sürümü (Pure) hız açısından PECL uzantısından daha düşük olmasına rağmen, gerçek projelerde yine de onu kullanmanızı tavsiye ederim rybakit/msgpack, çünkü resmi Mesaj Paketi uzantısında biçim belirtimi yalnızca kısmen uygulanmıştır (örneğin, özel veri türleri için destek yoktur; bu olmadan Tarantool 2.3'te tanıtılan yeni bir veri türü olan Decimal'ı kullanamazsınız) ve diğerlerinin sayısı sorunları (PHP 7.4 ile uyumluluk sorunları dahil). Genel olarak proje terk edilmiş görünüyor.
Şimdi senkron modda konnektörlerin performansını ölçelim:
Grafikten de görülebileceği gibi PECL konektörü (Tarantool), PHP konektörüne (Client) kıyasla daha iyi performans göstermektedir. Ancak ikincisinin daha yavaş bir dilde uygulanmasının yanı sıra aslında daha fazla iş yaptığı göz önüne alındığında bu şaşırtıcı değil: her çağrıda yeni bir nesne yaratılıyor Talep et и yanıt (Seçim durumunda - ayrıca Kriterlerve Güncelleme/Yükseltme durumunda ― Operasyon), ayrı varlıklar Bağlantısı, Paketleme и Handler aynı zamanda ek yük de eklerler. Açıkçası, esnekliğin bir bedeli var. Bununla birlikte, genel olarak PHP yorumlayıcısı iyi bir performans gösterir, ancak bir fark olmasına rağmen bu önemsizdir ve belki de PHP 7.4'te önyükleme kullanılırken daha da az olacaktır, PHP 8'deki JIT'den bahsetmeye bile gerek yok.
Hadi devam edelim. Tarantool 2.0, SQL desteğini ekledi. SQL protokolünü kullanarak Seç, Ekle, Güncelle ve Sil işlemlerini gerçekleştirmeye çalışalım ve sonuçları noSQL (binary) eşdeğerleriyle karşılaştıralım:
SQL sonuçları pek etkileyici değil (hala senkronize modu test ettiğimizi hatırlatmama izin verin). Ancak, bu konuda önceden üzülmezdim; SQL desteği hala aktif olarak geliştirilme aşamasındadır (örneğin, nispeten yakın zamanda destek eklenmiştir) hazırlanan ifadeler) ve listeye bakılırsa sorunlarSQL motoru gelecekte bir dizi optimizasyondan geçecek.
zaman uyumsuz
Şimdi Async uzantısının yukarıdaki sonuçları iyileştirmemize nasıl yardımcı olabileceğini görelim. Eşzamansız programlar yazmak için uzantı, kullanacağımız eşyordamlara dayalı bir API sağlar. Deneysel olarak çevremiz için en uygun eşyordam sayısının 25 olduğunu bulduk:
10,000 işlemi 25 ortak rutine "yayın" ve ne olacağını görün:
Ne yazık ki PECL konektörü ext-async ile başlamadı.
SQL'den ne haber?
Gördüğünüz gibi asenkron modda ikili protokol ile SQL arasındaki fark hata payı dahilinde hale geldi.
swoole
Yine bu sefer Swoole için en uygun eşyordam sayısını bulduk:
25'te duralım. Async uzantısıyla aynı numarayı tekrarlayalım; 10,000 işlemi 25 ortakyordam arasında dağıtalım. Ayrıca tüm işi 2 iki sürece böleceğimiz (yani her bir süreç 5,000 koroutinde 25 işlem gerçekleştirecek) bir test daha ekleyeceğiz. Süreçler kullanılarak oluşturulacak Swoole Süreci.
Sonuçlar:
Swole, tek bir işlemde çalıştırıldığında Async'e kıyasla biraz daha düşük bir sonuç gösteriyor, ancak 2 işlemle resim önemli ölçüde değişiyor (2 sayısı tesadüfen seçilmedi; benim makinemde en iyi sonucu veren 2 işlemdi).
Bu arada, Async uzantısının işlemlerle çalışmak için bir API'si de var, ancak orada bir veya daha fazla işlemde kıyaslama çalıştırmaktan herhangi bir fark görmedim (bir yerde hata yapmış olabilirim).
SQL ve ikili protokol:
Async'te olduğu gibi, asenkron modda ikili ve SQL işlemleri arasındaki fark ortadan kalkar.
Paralel
Paralel uzantı eşyordamlarla değil iş parçacıklarıyla ilgili olduğundan, paralel iş parçacıklarının optimal sayısını ölçelim:
Benim makinemde 16'ya eşit. 16 paralel iş parçacığı üzerinde bağlayıcı kıyaslamaları çalıştıralım:
Gördüğünüz gibi sonuç, eşzamansız uzantılardan bile daha iyidir (2 işlemde çalışan Swoole'u saymazsak). PECL bağlayıcısı için Güncelleme ve Yükseltme işlemlerinin boş olduğunu unutmayın. Bunun nedeni, bu işlemlerin bir hatayla başarısız olmasıdır - bunun ext-parallel, ext-tarantool veya her ikisinin hatası olup olmadığını bilmiyorum.
Şimdi SQL performansını karşılaştıralım:
Eş zamanlı çalışan bağlayıcılara ilişkin grafikle benzerliğe dikkat ettiniz mi?
Birlikte
Son olarak, test edilen uzantıların genel resmini görmek için tüm sonuçları tek bir grafikte özetleyelim. Grafiğe henüz yapmadığımız yeni bir test ekleyelim - Paralel* kullanarak Async eşyordamlarını paralel olarak çalıştıralım. Yukarıdaki uzantıları entegre etme fikri zaten tartışılan yazarlar, ancak fikir birliğine varılamadı, bunu kendiniz yapmanız gerekecek.
* Swoole eşyordamlarını Parallel ile başlatmak mümkün olmadı; bu uzantıların uyumsuz olduğu görülüyor.
Yani, nihai sonuçlar:
Bunun yerine bir sonuca
Bana göre sonuçlar oldukça değerli çıktı ve bazı nedenlerden dolayı bunun sınır olmadığından eminim! Buna gerçek bir projede yalnızca kendiniz için karar vermeniz gerekip gerekmediği, benim için bunun senkronize bir TCP konektöründen minimum çabayla ne kadar "sıkabileceğinizi" değerlendirmenize olanak tanıyan ilginç bir deney olduğunu söyleyeceğim. Karşılaştırma ölçütlerini iyileştirmeye yönelik fikirleriniz varsa, çekme isteğinizi değerlendirmekten memnuniyet duyarım. Başlatma talimatlarını ve sonuçlarını içeren tüm kodlar ayrı bir kitapta yayınlanır depolar.