Hip hop sanal makinesi (HHVM) teorik olarak PHP çalışma zamanı performansını nasıl geliştirir?


9

Yüksek bir seviyeden, Facebook, et. Hip Hop Sanal Makine ile PHP performansını artırmak için kullanın?

Geleneksel zend motorunu kullanarak kod yürütmekten farkı nedir? Türler isteğe bağlı olarak ön optimizasyon tekniklerine izin veren kesmek ile tanımlandığından mı?

Bu makaleyi okuduktan sonra merakım ortaya çıktı, HHVM'nin benimsenmesi .

Yanıtlar:


7

TranslatorX64'ün traceletlerini yeni HipHop Orta Temsilciliği (hhir) ve aslında aynı adla hhir olarak adlandırılan hhir oluşturmak için mantığın bulunduğu yeni bir dolaylama katmanı ile değiştirdiler.

Yüksek seviyeden, burada belirtildiği gibi daha önce 9 talimatı yapmak için 6 talimat kullanıyor: "Aynı daktilolarla başlar, ancak çevirinin gövdesi TranslatorX64'teki 9'dan önemli ölçüde daha iyi olan 6 talimattır"

http://hhvm.com/blog/2027/faster-and-cheaper-the-evolution-of-the-hhvm-jit

Bu çoğunlukla sistemin nasıl tasarlandığına dair bir eserdir ve sonunda temizlemeyi planladığımız bir şeydir. TranslatorX64'te kalan tüm kodlar, kod yayınlamak ve çevirileri birbirine bağlamak için gerekli olan makinedir; tek tek bayt kodlarının nasıl çevrileceğini anlayan kod TranslatorX64'ten alınmıştır.

Hhir TranslatorX64'ün yerini aldığında, kabaca% 5 daha hızlı ve manuel incelemede önemli ölçüde daha iyi görünen kod üretiyordu. Üretim çıkışını bir başka mini kilitlemeyle takip ettik ve bunun üzerine performans kazancında% 10 ek aldık. Bu geliştirmelerden bazılarını çalışırken görmek için addPositive işlevine ve çevirisinin bir parçasına bakalım.

function addPositive($arr) {
      $n = count($arr);
      $sum = 0;
      for ($i = 0; $i < $n; $i++) {
        $elem = $arr[$i];
        if ($elem > 0) {
          $sum = $sum + $elem;
        }
      }
      return $sum;
    }

Bu işlev bir çok PHP koduna benziyor: bir dizi üzerinde döngü yapıyor ve her elemanla bir şeyler yapıyor. Şimdilik bayt kodlarıyla birlikte 5. ve 6. satırlara odaklanalım:

    $elem = $arr[$i];
    if ($elem > 0) {
  // line 5
   85: CGetM <L:0 EL:3>
   98: SetL 4
  100: PopC
  // line 6
  101: Int 0
  110: CGetL2 4
  112: Gt
  113: JmpZ 13 (126)

Bu iki satır, bir diziden bir öğe yükler, yerel bir değişkente saklar, ardından o yerelin değerini 0 ile karşılaştırır ve sonuca göre bir yere koşullu olarak atlar. Bayt kodunda neler olup bittiğiyle ilgili daha fazla ayrıntıyla ilgileniyorsanız, bytecode.specification aracılığıyla gözden geçirebilirsiniz. TranslatorX64 günlerinde hem şimdi hem de geri dönen JIT, bu kodu iki tracelete ayırır: biri sadece CGetM ile, sonra diğeri talimatların geri kalanıyla (bunun neden olduğunu tam olarak açıklıyor, ancak çoğunlukla derleme sırasında dizi öğesinin türünün ne olacağını bilmiyoruz). CGetM'in çevirisi bir C ++ yardımcı işlevine yapılan çağrıya dayanır ve çok ilginç değildir, bu yüzden ikinci tracelet'e bakacağız. Bu taahhüt TranslatorX64'ün resmi emekliliğiydi,

  cmpl  $0xa, 0xc(%rbx)
  jnz 0x276004b2
  cmpl  $0xc, -0x44(%rbp)
  jnle 0x276004b2
101: SetL 4
103: PopC
  movq  (%rbx), %rax
  movq  -0x50(%rbp), %r13
104: Int 0
  xor %ecx, %ecx
113: CGetL2 4
  mov %rax, %rdx
  movl  $0xa, -0x44(%rbp)
  movq  %rax, -0x50(%rbp)
  add $0x10, %rbx    
  cmp %rcx, %rdx    
115: Gt
116: JmpZ 13 (129)
  jle 0x7608200

İlk dört satır, $ elem içindeki değerin ve yığının üstündeki değerin beklediğimiz türler olduğunu doğrulayan dakik denetimlerdir. Bunlardan herhangi biri başarısız olursa, farklı türde uzmanlaşmış bir makine kodu yığını oluşturmak için yeni türleri kullanarak traceletin yeniden çevirisini tetikleyen koda atlayacağız. Çevirinin eti takip eder ve kodun iyileştirilmesi için bolca yer vardır. 8. satırda ölü bir yük, 12 satırındaki hareketi kaydetmek için kolayca önlenebilir bir kayıt ve 10 ve 16 satırları arasında sürekli yayılma fırsatı vardır. Bunların hepsi TranslatorX64 tarafından kullanılan bir kerede bayt kod yaklaşımının sonuçlarıdır. Hiçbir saygın derleyici böyle bir kod yayınlamaz, ancak bundan kaçınmak için gereken basit optimizasyonlar TranslatorX64 modeline uymaz.

Şimdi, aynı hhvm revizyonunda, hhir kullanılarak tercüme edilen aynı traceleti görelim:

  cmpl  $0xa, 0xc(%rbx)
  jnz 0x276004bf
  cmpl  $0xc, -0x44(%rbp)
  jnle 0x276004bf
101: SetL 4
  movq  (%rbx), %rcx
  movl  $0xa, -0x44(%rbp)
  movq  %rcx, -0x50(%rbp)
115: Gt    
116: JmpZ 13 (129)
  add $0x10, %rbx
  cmp $0x0, %rcx    
  jle 0x76081c0

Aynı daktilo ile başlar, ancak çevirinin gövdesi 6 talimattır, TranslatorX64'ten 9'dan daha iyidir. Hareketleri kaydetmek için ölü yük veya kayıt olmadığına ve Int 0 bayt kodundan hemen 0 satırının 12 satırındaki cmp'ye yayıldığına dikkat edin. İşte tracelet ve bu çeviri arasında oluşturulan hhir:

  (00) DefLabel    
  (02) t1:FramePtr = DefFP
  (03) t2:StkPtr = DefSP<6> t1:FramePtr
  (05) t3:StkPtr = GuardStk<Int,0> t2:StkPtr
  (06) GuardLoc<Uncounted,4> t1:FramePtr
  (11) t4:Int = LdStack<Int,0> t3:StkPtr
  (13) StLoc<4> t1:FramePtr, t4:Int
  (27) t10:StkPtr = SpillStack t3:StkPtr, 1
  (35) SyncABIRegs t1:FramePtr, t10:StkPtr
  (36) ReqBindJmpLte<129,121> t4:Int, 0

Bayt kodu talimatları daha küçük ve daha basit işlemlere ayrılmıştır. Belirli bayt kodların davranışlarında gizlenen birçok işlem, hL'de SetL'nin bir parçası olan 6. satırdaki LdStack gibi açıkça temsil edilir. Değerlerin akışını temsil etmek için fiziksel kayıtlar yerine adsız geçiciler (t1, t2, vb.) Kullanarak, her bir değerin tanımını ve kullanımını kolayca izleyebiliriz. Bu, bir yükün hedefinin gerçekten kullanılıp kullanılmadığını veya bir talimatın girdilerinden birinin gerçekten 3 bayt kod önce sabit bir değer olup olmadığını görmeyi önemsiz kılar. Hhir'in ne olduğu ve nasıl çalıştığı hakkında çok daha ayrıntılı bir açıklama için ir.specification bölümüne bakınız.

Bu örnek hhir'in TranslatorX64 üzerinde yaptığı iyileştirmelerden sadece birkaçını gösterdi. Hhir'in Mayıs 2013'te üretime dağıtılması ve emekliye ayrılması, vurulması gereken büyük bir dönüm noktasıydı, ama sadece başlangıçtı. O zamandan beri, TranslatorX64'te neredeyse imkansız olacak daha fazla optimizasyon uyguladık ve hhvm'yi bu süreçte neredeyse iki kat daha verimli hale getirdik. Ayrıca, yeniden uygulamaya koymamız gereken mimariye özgü kod miktarını izole ederek ve azaltarak ARM işlemcilerinde hhvm'yi çalıştırmak için çabalarımızda çok önemliydi. Daha fazla bilgi için ARM limanımıza ayrılmış bir gönderiyi izleyin! "


1

Kısacası: rasgele bellek erişimini en aza indirmeye çalışırlar ve CPU önbelleğiyle güzel oynamak için bellekteki kod parçaları arasında atlarlar.

HHVM Performans Durumu'na göre , rastgele bellek erişimini en aza indirmek için dizeler ve diziler olan en sık kullanılan veri türlerini optimize ettiler. Buradaki fikir, birlikte kullanılan veri parçalarını (dizideki öğeler gibi) bellekte, ideal olarak doğrusal bir şekilde birbirine olabildiğince yakın tutmaktır. Bu şekilde, veriler CPU L2 / L3 önbelleğine sığarsa, büyüklük siparişlerini RAM'de olduğundan daha hızlı işleyebilir.

Bahsedilen diğer bir teknik, bir kodda en sık kullanılan yolları, derlenmiş versiyonun olabildiğince doğrusal (ei'nin en az miktarda "atlama" olduğu) ve verileri mümkün olduğunca nadiren belleğe / belleğe yükleyeceği şekilde derlemesidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.