Python'un GC mekanizmasını devre dışı bıraktıktan sonra Instagram performansı% 10 arttı

Yazar Liu Zhiyong Compile

Düzenle Xiaozhi

Python çöp toplama (Çöp Toplama, GC) mekanizmasını kapatarak (kullanılmayan verileri geri alarak ve serbest bırakarak belleği geri alma), Instagram'ın performansı% 10 artırılabilir. Evet, doğru duydunuz! GC'yi devre dışı bırakarak, bellek ayak izini azaltabilir ve CPU LLC önbellek isabet oranını artırabiliriz. Nedenini öğrenmek istiyorsanız, gelin ve Chenyang Wu ve Min Ni tarafından yazılan makaleyi okuyun.

Yazar Chenyang Wu, Instagram'da bir yazılım mühendisi ve Min Ni, Instagram'da bir teknik yöneticidir.

Web sunucusunu nasıl yönetiriz

Instagram'ın web sunucusu çok işlemli bir modda Django üzerinde çalışır. Ana işlem, gelen kullanıcı isteklerini almak için düzinelerce çalışan işlem oluşturmaya çatallar. Uygulama sunucusu için, ana işlem ile çalışan süreç arasındaki bellek paylaşımından yararlanmak için ön uç modu ile uWSGI kullanıyoruz.

Django sunucusunun OOM'a çalışmasını önlemek için, ana uWSGI işlemi, RSS belleği eşiği aştığında çalışan işlemi yeniden başlatmak için bir mekanizma sağlar.

Hafızayı anlamak

Ana süreç tarafından oluşturulduktan sonra RSS belleğinin neden hızla büyüdüğünü incelemeye başladık. Bir gözlem, RSS belleği 250MB'de başlasa bile, paylaşılan hafızasının çok hızlı bir şekilde düştüğüdür: birkaç saniye içinde 250MB'den yaklaşık 140MB'ye (paylaşılan belleğin boyutu / proc / PID / smaps'den okunabilir). Buradaki sayılar sıkıcı çünkü sürekli değişiyorlar, ancak paylaşılan bellek atmalarının ölçeği ilginç: toplam belleğin yaklaşık 1 / 3'ü. Daha sonra, paylaşılan hafızanın neden işçi neslinin başlangıcında her sürecin özel hafızası haline geldiğini anlamak istiyoruz.

Teorimiz: kopya okuma

Linux çekirdeği, fork sürecini optimize etmek için kullanılan Copy-on-Write (CoW) adlı bir mekanizmaya sahiptir. Alt süreç, her bellek sayfasını kendi üst süreciyle paylaşarak başlar. Yalnızca sayfa yazılırken alt bellek alanına kopyalanan bir sayfa (ayrıntılar için Wikipedia'daki Copy_on_Write girişine bakın).

Ancak Python'da, referans sayımı nedeniyle işler ilginçleşir. Bir Python nesnesini her okuduğumuzda, yorumlayıcı referans sayısını artıracaktır, bu da esasen temeldeki veri yapısına bir yazmadır. Bu, CoW'a yol açtı. Bu nedenle, Python ile, Okumaya Kopyala (CoR) yapıyoruz!

O halde soru şudur: Yazarken değişmez nesneleri (kod nesneleri gibi) kopyalıyor muyuz? PyCodeObject'in gerçekten de PyObject'in bir "alt sınıfı" olduğu göz önüne alındığında, cevap açıktır: evet. İlk düşüncemiz PyCodeObject için referans sayımını devre dışı bırakmaktır.

Deneme 1: Kod nesnelerinin referans sayımını devre dışı bırakın

Instagram'da önce basit şeyler yapıyoruz. Bunun bir deney olduğunu düşünerek, CPython yorumlayıcısında bazı küçük değişiklikler yaptık, referans sayısının kod nesnesini değiştirmediğini doğruladık ve ardından CPython'u üretim sunucularımızdan birine uyguladık.

Sonuçlar hayal kırıklığı yaratıyor çünkü paylaşılan bellekte bir değişiklik yok. Sebebini bulmaya çalıştığımızda, analizin doğru olup olmadığını kanıtlayacak güvenilir bir gösterge olmadığını ve paylaşılan bellek ile kod nesnelerinin kopyaları arasındaki ilişkiyi kanıtlayamayacağını fark ettik. Açıkçası, burada bir şeyler eksik. Bundan kazanılan deneyim şudur: Kullanmadan önce teorinizi kanıtlayın.

Sayfa hatalarını analiz edin

Google'da Copy-on-Write hakkında bilgi aradığımızda, Copy-on-Write'ın sistemdeki sayfa hataları ile ilgili olduğunu öğrendik. Her CoW, işlemde bir sayfa hatasını tetikler. Linux ile birlikte gelen Perf aracı, sayfa hataları dahil olmak üzere donanım / yazılım sistemi olaylarının günlüğe kaydedilmesine izin verir ve hatta yığın izleri sağlayabilir!

Bu yüzden bir prod sunucusu çalıştırdık, sunucuyu yeniden başlattık, çatallanmasını bekledik, bir çalışan işlemin PID'sini aldık ve sonra aşağıdaki komutu çalıştırdık:

performans kaydı -e sayfa-hataları -g -p < PID >

Yığın izleme sırasında bir sayfa hatası oluştuğunda ne olacağını görmek için yeni bir fikrimiz var.

Sonuç beklenmedikti ve kod nesnesi kopyalanmadı. En büyük şüpheli, gcmodule.c'ye ait olan ve çöp toplama tetiklendiğinde çağrılan Collect'tir. CPython'da GC'nin çalışma prensibini okuduktan sonra, aşağıdaki teoriyi bulduk:

Eşiğe bağlı olarak CPython'un GC'sini belirleyici olarak tetikleyin. Varsayılan eşik çok düşük olduğundan çok erken bir aşamada başladı. Nesnelerin nesle bağlı bir listesini tutar ve GC sırasında bağlantılı liste karıştırılır. Bağlantılı liste yapısı nesnenin kendisiyle birlikte mevcut olduğundan (ob_refcount gibi), bu nesnelerin bağlantılı listede yeniden yazılması sayfanın CoW olmasına neden olur, bu da talihsiz bir yan etki.

Deneme 2: GC'yi devre dışı bırakmayı deneyin

GC bizi bıçakladığından beri, devre dışı bırakın!

Bootstrap komut dosyamız bir gc.disable çağrısı ekledi ve ardından sunucuyu yeniden başlattı. Sunucuyu yeniden başlattık ama maalesef! Perf'e tekrar bakarsak, gc.collect'in hala çağrıldığını ve hafızanın hala kopyalandığını göreceğiz. GDB'nin bazı hata ayıklamalarını kullanarak, onu geri yüklemek için gc.enable adlı üçüncü taraf bir kitaplığın (msgpack) kullanıldığını ve bu nedenle gc.disable'ın önyükleme sırasında temizlendiğini gördük.

Msgpack'i yamalamak, yapmamız gereken son şeydir, çünkü bu, gelecekte diğer kütüphanelerin de aynısını yapacağını fark etmediğimiz anlamına gelir. Öncelikle, GS'yi devre dışı bırakmanın gerçekten çok yardımcı olduğunu doğrulamamız gerekiyor. Cevap gcmodule.c içindedir. Gc.disable'a alternatif olarak gc.set_threshold (0) yaptık. Bu sefer hiçbir kitaplık geri yüklenmedi.

Bu şekilde, her bir çalışan işlemin paylaşılan belleğini başarıyla 140 MB'tan 225 MB'a yükselttik ve ana bilgisayardaki her makinenin toplam bellek kullanımını 8 GB azalttık. Bu, tüm Django kümesi için% 25 bellek tasarrufu sağlar. Böylesine geniş bir baş alanıyla, daha fazla işlem çalıştırabilir veya daha yüksek bir RSS bellek eşiğiyle çalıştırabiliriz. Aslında, bu tür iyileştirmeler Django katmanının verimini% 10'dan fazla artırdı.

Deneme 3: GC tamamen yasaklanmalıdır

Bir dizi ayarı denedikten sonra, daha büyük bir ölçekte denemeye karar verdik: kümeler. Geri bildirim oldukça hızlıydı çünkü GC devre dışı bırakıldıktan sonra web sunucusunu yeniden başlatmak o kadar yavaşladı ki sürekli dağıtımımız kesintiye uğradı. Genellikle yeniden başlatmak 10 saniyeden az sürer, ancak GC devre dışı bırakıldıktan sonra bazen 60 saniyeden fazla sürer.

Bu hatayı yeniden üretmek çok zahmetli çünkü deterministik değil. Çok sayıda deneyden sonra, en üstte gerçek bir yeniden tepe gösterilir. Bu olduğunda, ana bilgisayardaki kullanılabilir bellek neredeyse sıfıra düşer ve geri atlayarak tüm önbelleği boşaltmaya zorlar. Daha sonra tüm kodun / verilerin diskten okunması gerektiğinde (DSK% 100), her şey yavaştır.

Python'un yorumlayıcıyı kapatmadan önce son GC'yi yapması garip geliyor, bu da kısa sürede bellek kullanımında büyük bir sıçramaya neden olacak. Dahası, önce bunu kanıtlamak ve sonra nasıl doğru şekilde idare edileceğini bulmak istiyorum. Bu nedenle, uWSGI'nin python eklentisindeki Py_Finalize çağrısını yorumladım ve sorun ortadan kalktı.

Ama belli ki, Py_Finalize'ı yasaklayamayız. Bir sürü önemli temizlememiz olduğu için, buna bağlı atexit kancalarını kullanmamız gerekiyor. Son olarak, yaptığımız şey, GC'yi tamamen devre dışı bırakmak için CPython'a bir çalışma zamanı bayrağı eklemekti.

Sonunda, bu uygulamayı daha geniş bir ölçekte genişletmeye başladık. Bundan sonra tüm kümede denedik, ancak sürekli dağıtım tekrar kesintiye uğradı. Ancak, bu sefer yalnızca eski CPU modeli (Sandybridge) makinesinde kesintiye uğradı ve yeniden üretilmesi daha da zordu. Alınan ders: daha eski müşterileri / eski modelleri daha fazla test edin çünkü kesintiye uğrama olasılığı daha yüksektir.

Sürekli dağıtımımız oldukça hızlı bir süreç olduğundan, olanları gerçekten yakalamak için kullanıma sunma komutunun üstüne ayrı bir şey ekledim. Bu şekilde, önbelleğin gerçekten düşük olduğu bir anı yakalayabiliriz. Tüm uWSGI işlemleri çok sayıda MINFLT'yi (küçük sayfa hataları) tetikler.

Perf ile elde edilen özet üzerinden bir kez daha Py_Finalize'yi görüyoruz. Kapatma sırasında, son GC'ye ek olarak, Python, tür nesnelerini yok etme ve modülleri kaldırma gibi bir dizi temizleme işlemi yapar. Bu, paylaşılan belleğe tekrar zarar verir.

Deneme 4: GC'yi kapatmanın son adımı: temizleme yok

Neden temizlemeye ihtiyacımız var? Bu süreç ölecek ve başka bir yedek alacağız. Gerçekten önemsediğimiz şey, uygulamanın ateşleme kancasını temizlemek. Python'un temizlenmesine gelince, bunu yapmak zorunda değiliz. Önyükleme betiğinin sonu:

Bu gerçeğe dayanarak atexit işlevi kayıt defterinin ters sırasına göre çalışır. Atexit işlevi diğer temizlemeleri tamamlar ve ardından son adımın geçerli işleminden çıkmak için os._exit (0) öğesini çağırır.

Bu iki hattın değişmesiyle nihayet tüm kümenin tanıtımını tamamladık. Bellek eşiğini dikkatlice ayarladıktan sonra,% 10'luk bir genel performans artışı elde ettik!

Hadi gözden geçirelim

Bu performans iyileştirmesini gözden geçirirken iki sorumuz var.

Öncelikle, çöp toplama yoksa, tüm bellek ayırma serbest bırakılmayacağı için Python belleği patlamaz mı? (Unutmayın, Python belleğinde gerçek bir yığın yoktur, çünkü tüm nesneler öbek üzerinde tahsis edilmiştir.)

Neyse ki bu doğru değil. Python'da nesneleri serbest bırakmanın ana mekanizması hala referans saymadır. Bir nesnenin referansı kaldırıldığında (Py_DECREF olarak adlandırılır), Python çalışma zamanı her zaman referans sayısının sıfıra düşüp düşmediğini kontrol eder. Bu durumda, nesnenin serbest bırakıcısı çağrılacaktır. Çöp toplamanın temel amacı, referans sayımı çalışmadığında referans döngüsünü kırmaktır.

Mola kazancı

İkinci soru: Kazanç nereden geliyor?

GC'yi devre dışı bırakmanın kazancı iki katına çıkar:

  • Belleğe bağlı sunucu üretimi için daha fazla iş süreci oluşturmak veya CPU'ya bağlı sunucular tarafından oluşturulan iş programlarının yenileme hızını azaltmak amacıyla her sunucu için yaklaşık 8 GB RAM serbest bıraktık;

  • Döngü başına CPU talimatları (IPC) yaklaşık% 10 arttıkça, CPU verimi de artar.

GC devre dışı bırakıldığında, esas olarak IPC'deki% 10'luk bir artışa bağlı olarak, önbellek kaçırma oranı% 2 ila% 3 düşer. Bir CPU önbelleğini kaçırmanın maliyeti çok yüksektir çünkü CPU işlem hattını geciktirir. CPU önbellek isabet oranındaki küçük iyileştirmeler genellikle IPC'yi önemli ölçüde iyileştirebilir. Daha az CoW ile, farklı sanal adreslere sahip daha fazla CPU önbellek satırı (farklı iş süreçlerinde) aynı fiziksel bellek adresini işaret eder ve bu da daha iyi bir önbellek isabet oranıyla sonuçlanır.

Her bileşenin beklendiği gibi çalışmadığını görebiliriz ve bazen sonuçlar çok şaşırtıcı olabilir. Bu yüzden kazmaya ve etrafa bakmaya devam edin, işlerin nasıl yürüdüğüne şaşıracaksınız!

Bugünün Tavsiyesi

Okumak için aşağıdaki resme tıklayın

Tao Kardeş: 29 aylık Ali'ye döndüğüm ve kariyerimle ilgili 6 düşüncem

Bir zamanlar Chow Yun-fat'tan borç alan o reddedildi, 65 yaşındaydı ve şeker hastalığına yakalandı.
önceki
Maliyet performansının ardından Xiaomi POCO F1 Hindistan'da piyasaya sürüldü
Sonraki
Hainan Film Festivali yıldızı kırmızı halıya dokunulmamış: Zhu Yilong hala siyah takım elbiseli yakışıklı, Wen Bixia harika
Gray, Song Don'a aşık mı? "Ginger Canteen 2" geri geliyor
"Sound of Light", "Infernal Purgatory" ve diğer yeni film pozlama fotoğrafları, Natalie Portman, Nicole Kidman amacı
Asya'nın en zengin adamının kızı evlenir ve Beyoncé'yi icra etmeye davet etmek için 130 milyon harcır. 600 hizmetçisi vardır.
20 yıllık Han Ran, dokuz ay boyunca hamile kaldı ve sabahın erken saatlerinde bir belge yayınladı ve kocasının onu hamileliğin üçüncü üç aylık döneminde aldığını söyledi.
Objective-C ile güzel bir DSL nasıl yazılır?
Panasonic, Lumix LX100 II kamerayı piyasaya sürdü: yükseltilmiş dokunmatik ekran / 17 milyon piksel
"Grup adına" finalleri, 16 kişi iki takım halinde ilk kez sahneye çıktı; Wang Yuan, dayak sahnesini restore etti.
pişman! "SpongeBob SquarePants" ın babası donmadan öldü! Eskiden "sünger hazinesi" yaratan bir oşinograf olarak
"Bir kişi gözyaşlarını tadıyor" Yu Xiaotong, endişeleri hakkında gece geç saatlerde bir yazı yazdı, ancak ikiyüzlü olduğundan şikayet edildi.
Hem iş hem de platform açısından daha güvenilir mikro hizmetler nasıl tasarlanır?
Yeni 360 N7 Pro ile değiştirilen fiyat hala aynı
To Top