Java'da JIT derlemesinin babası Cliff Click ile harika röportaj

Java'da JIT derlemesinin babası Cliff Click ile harika röportajUçurum Tıklaması — Cratus'un CTO'su (süreç iyileştirme için IoT sensörleri), birçok başarılı çıkışa sahip çeşitli startup'ların (Rocket Realtime School, Neurensic ve H2O.ai dahil) kurucusu ve kurucu ortağı. Cliff ilk derleyicisini 15 yaşında yazdı (TRS Z-80 için Pascal)! En çok Java'daki (Düğümler Denizi IR) C2 üzerine yaptığı çalışmalarla tanınır. Bu derleyici, JIT'in yüksek kaliteli kod üretebileceğini dünyaya gösterdi; bu, Java'nın ana modern yazılım platformlarından biri olarak ortaya çıkışındaki faktörlerden biriydi. Daha sonra Cliff, Azul Systems'in 864 milisaniye içinde 500 gigabaytlık bir yığın üzerinde GC duraklamalarını destekleyen saf Java yazılımına sahip 10 çekirdekli bir ana bilgisayar oluşturmasına yardımcı oldu. Genel olarak Cliff, JVM'nin tüm yönleri üzerinde çalışmayı başardı.

 
Bu habrapost Cliff ile harika bir röportaj. Aşağıdaki konular hakkında konuşacağız:

  • Düşük seviyeli optimizasyonlara geçiş
  • Büyük bir yeniden düzenleme nasıl yapılır?
  • Maliyet modeli
  • Düşük seviyeli optimizasyon eğitimi
  • Performans iyileştirmenin pratik örnekleri
  • Neden kendi programlama dilinizi yaratmalısınız?
  • Performans Mühendisi Kariyeri
  • Teknik Zorluklar
  • Kayıt tahsisi ve çoklu çekirdekler hakkında biraz bilgi
  • Hayattaki en büyük zorluk

Röportaj şu kişiler tarafından gerçekleştirilmektedir:

  • Andrey Satarin Amazon Web Services'ten. Kariyerinde tamamen farklı projelerde çalışmayı başardı: Yandex'de NewSQL dağıtılmış veritabanını, Kaspersky Lab'da bir bulut algılama sistemini, Mail.ru'da çok oyunculu bir oyunu ve Deutsche Bank'ta döviz fiyatlarını hesaplamaya yönelik bir hizmeti test etti. Büyük ölçekli arka uç ve dağıtılmış sistemleri test etmekle ilgileniyorum.
  • Vladimir Sitnikov Netcracker'dan. Telekom operatörleri tarafından ağ ve ağ ekipmanı yönetimi süreçlerini otomatikleştirmek için kullanılan yazılım olan NetCracker OS'nin performansı ve ölçeklenebilirliği üzerinde on yıllık çalışma. Java ve Oracle Database performans sorunlarıyla ilgileniyorum. Resmi PostgreSQL JDBC sürücüsündeki bir düzineden fazla performans iyileştirmesinin yazarı.

Düşük seviyeli optimizasyonlara geçiş

Andrew: JIT derlemesi, Java ve genel olarak performans çalışmaları dünyasında büyük bir isimsiniz, değil mi? 

Uçurum: O gibi!

Andrew: Performans çalışmasına ilişkin bazı genel sorularla başlayalım. CPU seviyesinde çalışmak gibi yüksek seviyeli ve düşük seviyeli optimizasyonlar arasındaki seçim hakkında ne düşünüyorsunuz?

Uçurum: Evet, burada her şey basit. En hızlı kod hiç çalışmayan koddur. Bu nedenle her zaman yüksek bir seviyeden başlamanız, algoritmalar üzerinde çalışmanız gerekir. Yeterince büyük sabitler müdahale etmedikçe, daha iyi bir O notasyonu daha kötü bir O notasyonunu yenecektir. Düşük seviyeli şeyler en sona kalır. Tipik olarak, yığınınızın geri kalanını yeterince iyi optimize ettiyseniz ve hala bazı ilginç şeyler kaldıysa, bu düşük bir seviyedir. Peki yüksek bir seviyeden nasıl başlanır? Yeterli düzeyde üst düzey çalışmanın yapıldığını nasıl anlarsınız? Kesinlikle... mümkün değil. Hazır tarifler yok. Sorunu anlamanız, ne yapacağınıza karar vermeniz (gelecekte gereksiz adımlar atmamak için) ve ardından yararlı bir şeyler söyleyebilecek profil oluşturucuyu ortaya çıkarabilmeniz gerekir. Bir noktada, gereksiz şeylerden kurtulduğunuzu ve düşük düzeyde ince ayar yapmanın zamanının geldiğini kendiniz fark edersiniz. Bu kesinlikle özel bir sanat türüdür. Gereksiz şeyler yapan ama o kadar hızlı hareket eden pek çok insan var ki üretkenlik konusunda endişelenecek zamanları yok. Ancak bu, soru açıkça ortaya çıkana kadar geçerlidir. Genellikle %99 oranında kimse benim ne yaptığımı umursamaz, ta ki kritik yolda kimsenin umursamadığı önemli bir şey ortaya çıkana kadar. Ve burada herkes "neden en başından beri mükemmel bir şekilde çalışmadı" diye dırdır etmeye başlıyor. Genel olarak performansta her zaman iyileştirilecek bir şeyler vardır. Ancak %99 oranında hiçbir ipucunuz yok! Sadece bir şeyin işe yaramasını sağlamaya çalışıyorsunuz ve bu süreçte neyin önemli olduğunu anlıyorsunuz. Bu parçanın mükemmel olması gerektiğini asla önceden bilemezsiniz, dolayısıyla aslında her şeyde mükemmel olmanız gerekiyor. Ama bu imkansızdır ve siz bunu yapmazsınız. Her zaman düzeltilmesi gereken pek çok şey vardır ve bu tamamen normaldir.

Büyük bir yeniden düzenleme nasıl yapılır?

Andrew: Bir performans üzerinde nasıl çalışıyorsunuz? Bu, kesişen bir sorundur. Örneğin, mevcut birçok işlevselliğin kesişiminden kaynaklanan sorunlar üzerinde hiç çalışmak zorunda kaldınız mı?

Uçurum: Bundan kaçınmaya çalışıyorum. Performansın bir sorun olacağını biliyorsam, özellikle veri yapılarıyla kodlamaya başlamadan önce bunu düşünürüm. Ancak çoğu zaman tüm bunları çok sonra keşfedersiniz. Ve sonra aşırı önlemlere başvurmanız ve benim "yeniden yazma ve fethetme" dediğim şeyi yapmanız gerekir: yeterince büyük bir parça almanız gerekir. Performans sorunları veya başka bir nedenden dolayı kodun bir kısmının yeniden yazılması gerekecektir. Kodu yeniden yazmanın nedeni ne olursa olsun, daha büyük bir parçayı yeniden yazmak, daha küçük bir parçayı yeniden yazmaktan neredeyse her zaman daha iyidir. O anda herkes korkudan titremeye başlıyor: “Aman Tanrım, bu kadar koda dokunamazsın!” Ancak aslında bu yaklaşım neredeyse her zaman çok daha iyi sonuç verir. Hemen büyük bir sorunu ele almanız, etrafına büyük bir daire çizmeniz ve şunu söylemeniz gerekiyor: Her şeyi dairenin içine yeniden yazacağım. Kenarlık, içindeki değiştirilmesi gereken içerikten çok daha küçüktür. Ve eğer sınırların bu şekilde çizilmesi içerideki işi mükemmel bir şekilde yapmanıza izin veriyorsa, elleriniz serbesttir, istediğinizi yapın. Sorunu anladığınızda yeniden yazma süreci çok daha kolay olur, o yüzden büyük bir ısırık alın!
Aynı zamanda, büyük bir yeniden yazma işlemi yaptığınızda ve performansın bir sorun olacağını fark ettiğinizde, bu konuda hemen endişelenmeye başlayabilirsiniz. Bu genellikle “verileri kopyalamayın, verileri olabildiğince basit yönetin, küçültün” gibi basit şeylere dönüşüyor. Büyük yeniden yazmalarda performansı iyileştirmenin standart yolları vardır. Ve neredeyse her zaman veriler etrafında dönerler.

Maliyet modeli

Andrew: Podcast'lerden birinde verimlilik bağlamında maliyet modellerinden bahsettiniz. Bununla ne demek istediğinizi açıklayabilir misiniz?

Uçurum: Kesinlikle. İşlemci performansının son derece önemli olduğu bir dönemde doğdum. Ve bu çağ yeniden geri dönüyor; kader ironisiz değil. Sekiz bitlik makinelerin olduğu günleri yaşamaya başladım; ilk bilgisayarım 256 baytla çalışıyordu. Kesinlikle bayt. Her şey çok küçüktü. Talimatların sayılması gerekiyordu ve programlama dili yığınında yukarı doğru ilerlemeye başladıkça, diller giderek daha fazla ilgi görmeye başladı. Assembler vardı, sonra Basic, ardından C ve C, kayıt tahsisi ve talimat seçimi gibi birçok ayrıntıyla ilgilendi. Ancak orada her şey oldukça açıktı ve eğer bir değişkenin örneğine işaret edersem, o zaman yük alırdım ve bu talimatın maliyeti biliniyor. Donanım belirli sayıda makine döngüsü üretir, böylece farklı şeylerin yürütme hızı, çalıştıracağınız tüm talimatların toplanmasıyla kolayca hesaplanabilir. Her karşılaştırma/test/şube/çağrı/yükleme/depolama eklenebilir ve şöyle söylenebilir: bu sizin için yürütme süresidir. Performansı iyileştirmeye çalışırken hangi sayıların küçük sıcak döngülere karşılık geldiğine kesinlikle dikkat edeceksiniz. 
Ancak Java, Python ve benzeri şeylere geçtiğinizde düşük seviyeli donanımlardan çok hızlı bir şekilde uzaklaşıyorsunuz. Java'da alıcı çağırmanın maliyeti nedir? HotSpot'taki JIT doğruysa satır içiyüklenecektir, ancak bunu yapmadıysa bir işlev çağrısı olacaktır. Çağrı aktif bir döngüde olduğundan, o döngüdeki diğer tüm optimizasyonları geçersiz kılacaktır. Dolayısıyla gerçek maliyet çok daha yüksek olacaktır. Ve bir kod parçasına bakma yeteneğinizi anında kaybedersiniz ve onu işlemci saat hızı, bellek ve kullanılan önbellek açısından çalıştırmamız gerektiğini anlarsınız. Bütün bunlar ancak gerçekten performansın içine girerseniz ilginç hale gelir.
Şimdi kendimizi işlemci hızlarının on yıldır neredeyse hiç artmadığı bir durumda buluyoruz. Eski günler geri döndü! Artık tek iş parçacıklı performansın iyi olduğuna güvenemezsiniz. Ancak aniden paralel hesaplamaya başlarsanız, bu inanılmaz derecede zordur, herkes size James Bond gibi bakar. Buradaki on kat hızlanma genellikle birisinin bir şeyi berbat ettiği yerlerde meydana gelir. Eşzamanlılık çok fazla çalışma gerektirir. Bu XNUMX kat hızlanmayı elde etmek için maliyet modelini anlamanız gerekir. Maliyeti nedir ve ne kadardır? Bunu yapmak için dilin temel donanıma nasıl uyduğunu anlamanız gerekir.
Martin Thompson blogu için harika bir kelime seçti Mekanik Sempati! İlk etapta donanımın ne yapacağını, bunu tam olarak nasıl yapacağını ve yaptığını neden yaptığını anlamalısınız. Bunu kullanarak talimatları saymaya başlamak ve yürütme süresinin nereye gittiğini bulmak oldukça kolaydır. Eğer uygun eğitime sahip değilseniz, karanlık bir odada kara bir kedi arıyorsunuz demektir. Ne yaptıkları hakkında hiçbir fikri olmayan, performansı optimize eden insanları her zaman görüyorum. Çok acı çekiyorlar ve fazla ilerleme kaydedemiyorlar. Ve aynı kod parçasını alıp birkaç küçük hile yaptığımda ve beş veya on kat hızlanma elde ettiğimde şöyle oluyorlar: Bu adil değil, senin daha iyi olduğunu zaten biliyorduk. İnanılmaz. Neden bahsediyorum... maliyet modeli, ne tür kod yazdığınız ve büyük resimde ortalama ne kadar hızlı çalıştığıyla ilgilidir.

Andrew: Peki böyle bir sesi nasıl kafanda tutabiliyorsun? Bu daha fazla deneyimle mi elde edilir, yoksa? Böyle bir deneyim nereden geliyor?

Uçurum: Ben deneyimimi en kolay yoldan edinemedim. Her bir talimatı anlayabildiğiniz günlerde Assembly'de programladım. Aptalca gelebilir ama o zamandan beri Z80 komut seti hep kafamda, hafızamda kaldı. Bir dakika konuştuktan sonra insanların isimlerini hatırlamıyorum ama 40 yıl önce yazılan kodları hatırlıyorum. Komik, bir sendroma benziyor"aptal bilim adamı'.

Düşük seviyeli optimizasyon eğitimi

Andrew: İçeri girmenin daha kolay bir yolu var mı?

Uçurum: Evet ve hayır. Hepimizin kullandığı donanım zaman içinde pek değişmedi. Arm akıllı telefonlar dışında herkes x86 kullanıyor. Eğer bir çeşit sıkı yerleştirme yapmıyorsanız, aynı şeyi yapıyorsunuz demektir. Tamam, sıradaki. Talimatlar da yüzyıllardır değişmedi. Meclis'e gidip bir şeyler yazmanız gerekiyor. Çok değil ama anlamaya başlamak için yeterli. Sen gülümsüyorsun ama ben tamamen ciddi konuşuyorum. Dil ve donanım arasındaki uyumu anlamalısınız. Bundan sonra gidip biraz yazmanız ve küçük bir oyuncak dili için küçük bir oyuncak derleyici yapmanız gerekiyor. Oyuncak benzeri, makul bir sürede yapılması gerektiği anlamına gelir. Çok basit olabilir ama talimatlar oluşturması gerekir. Talimat oluşturma eylemi, herkesin yazdığı üst düzey kod ile donanım üzerinde çalışan makine kodu arasındaki köprünün maliyet modelini anlamanıza yardımcı olacaktır. Bu yazışma derleyici yazıldığı anda beyne kazınacaktır. En basit derleyici bile. Bundan sonra, Java'nın anlamsal uçurumunun çok daha derin olduğu ve üzerinde köprüler kurmanın çok daha zor olduğu gerçeğine bakmaya başlayabilirsiniz. Java'da köprümüzün iyi mi kötü mü olduğunu, neyin yıkılmasına neden olacağını ve neyin olmayacağını anlamak çok daha zordur. Ancak koda bakıp şunu anlayabileceğiniz bir tür başlangıç ​​noktasına ihtiyacınız var: "evet, bu alıcı her zaman satır içi olmalıdır." Ve sonra, yöntemin çok büyüdüğü ve JIT'in her şeyi satır içine almaya başladığı durum dışında, bazen bunun gerçekleştiği ortaya çıktı. Bu tür yerlerin performansı anlık olarak tahmin edilebilmektedir. Genellikle alıcılar iyi çalışır, ancak daha sonra büyük sıcak döngülere bakıyorsunuz ve orada ne yaptıklarını bilmeyen bazı işlev çağrılarının dolaştığını fark ediyorsunuz. Alıcıların yaygın kullanımındaki sorun budur, satır içi olmamalarının nedeni alıcı olup olmadıklarının net olmamasıdır. Eğer süper küçük bir kod tabanınız varsa, onu basitçe hatırlayabilir ve sonra şöyle diyebilirsiniz: bu bir alıcı ve bu bir ayarlayıcıdır. Geniş bir kod tabanında, her işlev, genel olarak kimsenin bilmediği kendi geçmişini yaşar. Profil oluşturucu, bazı döngülerde zamanın %24'ünü kaybettiğimizi ve bu döngünün ne yaptığını anlamak için içindeki her fonksiyona bakmamız gerektiğini söylüyor. İşlevini incelemeden bunu anlamak imkansızdır ve bu da anlama sürecini ciddi şekilde yavaşlatır. Bu yüzden alıcı ve ayarlayıcıları kullanmıyorum, yeni bir seviyeye ulaştım!
Maliyet modeli nereden alınır? Elbette bir şeyler okuyabilirsiniz... Ama bence en iyi yol harekete geçmek. Küçük bir derleyici yapmak maliyet modelini anlayıp kendi kafanıza oturtmanın en iyi yolu olacaktır. Mikrodalga programlamaya uygun küçük bir derleyici, yeni başlayanlar için bir görevdir. Yani, zaten programlama becerileriniz varsa bu yeterli olacaktır. Bir çeşit cebirsel ifade olarak sahip olduğunuz bir diziyi ayrıştırmak, oradan matematiksel işlemler için talimatları doğru sırayla çıkarmak, kayıtlardan doğru değerleri almak gibi tüm bunlar bir anda yapılır. Ve siz bunu yaparken beyninize kazınacaktır. Sanırım herkes bir derleyicinin ne yaptığını biliyor. Bu da maliyet modelinin anlaşılmasını sağlayacaktır.

Performans iyileştirmenin pratik örnekleri

Andrew: Verimlilik üzerine çalışırken başka nelere dikkat etmelisiniz?

Uçurum: Veri yapıları. Bu arada evet uzun zamandır bu dersleri vermiyorum... Roket Okulu. Eğlenceliydi ama çok çaba gerektiriyordu ve benim de bir hayatım var! TAMAM. Büyük ve ilgi çekici derslerden birinde, "Performansınız nereye gidiyor?" dersinde öğrencilere bir örnek verdim: CSV dosyasından iki buçuk gigabaytlık fintech verisi okundu ve ardından satılan ürün sayısını hesaplamaları gerekiyordu. . Düzenli kene piyasası verileri. UDP paketleri 70'li yıllardan beri metin formatına dönüştürülüyor. Chicago Ticaret Borsası - tereyağı, mısır, soya fasulyesi gibi her türlü şey. Bu ürünleri, işlem sayısını, fon ve malların ortalama hareket hacmini vb. saymak gerekiyordu. Oldukça basit bir ticaret matematiği var: Ürün kodunu bulun (hash tablosunda 1-2 karakter), tutarı alın, ticari setlerden birine ekleyin, hacim ekleyin, değer ekleyin ve birkaç şey daha yapın. Çok basit bir matematik. Oyuncak uygulaması çok basitti: her şey bir dosyada, dosyayı okuyorum ve içinde hareket ediyorum, bireysel kayıtları Java dizelerine bölüyorum, içlerinde gerekli şeyleri arıyorum ve bunları yukarıda açıklanan matematiğe göre topluyorum. Ve biraz düşük hızda çalışıyor.

Bu yaklaşımla neler olduğu çok açık ve paralel hesaplamanın bir faydası olmayacak, değil mi? Doğru veri yapılarının seçilmesiyle performansta beş kat artışın sağlanabileceği ortaya çıktı. Ve bu deneyimli programcıları bile şaşırtıyor! Benim özel durumumda işin püf noktası, sıcak döngüde bellek ayırmaları yapmamanız gerektiğiydi. Doğrunun tamamı bu değil, ancak genel olarak - X yeterince büyük olduğunda "X'te bir kez" seçeneğini vurgulamamalısınız. X iki buçuk gigabayt olduğunda, “harf başına bir kez”, “satır başına bir kez” veya “alan başına bir kez” gibi bir şey ayırmamalısınız. Burası zamanın harcandığı yerdir. Bu nasıl çalışıyor? Bir arama yaptığımı hayal et String.split() veya BufferedReader.readLine(). Readline yüz milyonlarca satırın her biri için, her satır için bir kez olmak üzere ağ üzerinden gelen bir bayt kümesinden bir dize oluşturur. Bu satırı alıyorum, ayrıştırıyorum ve atıyorum. Neden onu atıyorum - zaten işledim, hepsi bu. Yani bu 2.7G'den okunan her bayt için satıra iki karakter yazılacak yani zaten 5.4G ve daha fazlası için onlara ihtiyacım yok o yüzden atılıyorlar. Bellek bant genişliğine bakarsanız, işlemcideki bellek ve bellek veriyolundan geçen 2.7G'yi yüklüyoruz ve ardından bellekte yatan hatta iki katı kadar gönderiliyor ve her yeni hat oluşturulduğunda tüm bunlar yıpranıyor. Ama okumam gerekiyor, sonradan her şey yıpransa bile donanım okuyor. Ve bunu bir yere yazmam gerekiyor çünkü bir satır oluşturdum ve önbellekler dolu - önbellek 2.7G'yi barındıramıyor. Yani okuduğum her bayt için iki bayt daha okuyorum ve iki bayt daha yazıyorum ve sonuçta 4:1 oranına sahipler - bu oranda bellek bant genişliğini boşa harcıyoruz. Ve sonra eğer bunu yaparsam ortaya çıkıyor String.split() – bunu son kez yapmıyorum, içeride 6-7 alan daha olabilir. Dolayısıyla, CSV'yi okuma ve ardından dizeleri ayrıştırma şeklindeki klasik kod, gerçekte sahip olmak istediğinize göre yaklaşık 14:1'lik bir bellek bant genişliği israfına neden olur. Bu seçimleri atarsanız beş kat hızlanma elde edebilirsiniz.

Ve bu o kadar da zor değil. Koda doğru açıdan bakarsanız, sorunun farkına vardığınızda her şey oldukça basit hale gelir. Belleği ayırmayı tamamen bırakmamalısınız: Tek sorun, bir şeyi ayırdığınızda hemen ölmesi ve yol boyunca önemli bir kaynağı yakmasıdır; bu durumda bellek bant genişliğidir. Ve tüm bunlar verimliliğin düşmesine neden oluyor. X86'da genellikle işlemci döngülerini aktif olarak yazmanız gerekir, ancak burada tüm belleği çok daha erken yaktınız. Çözüm deşarj miktarını azaltmaktır. 
Sorunun diğer kısmı, profil oluşturucuyu bellek şeridi bittiğinde çalıştırırsanız, tam da bu gerçekleştiğinde, genellikle önbelleğin geri gelmesini beklersiniz çünkü burası az önce ürettiğiniz çöplerle, tüm bu satırlarla doludur. Bu nedenle, her yükleme veya depolama işlemi yavaşlar çünkü bunlar önbellek eksikliklerine yol açar - tüm önbellek yavaşlar ve çöpün onu terk etmesini bekler. Bu nedenle, profil oluşturucu yalnızca tüm döngü boyunca yayılmış sıcak rastgele gürültüyü gösterecektir - kodda ayrı bir sıcak talimat veya yer olmayacaktır. Sadece gürültü. Ve eğer GC döngülerine bakarsanız, bunların hepsinin Genç Nesil olduğunu ve süper hızlı olduklarını görürsünüz; maksimum mikrosaniye veya milisaniye. Sonuçta tüm bu anılar anında ölüyor. Milyarlarca gigabayt ayırıyorsunuz ve o bunları kesiyor, kesiyor ve tekrar kesiyor. Bütün bunlar çok hızlı gerçekleşir. Görünüşe göre ucuz GC döngüleri var, tüm döngü boyunca sıcak gürültü var, ancak biz 5 kat hızlanma elde etmek istiyoruz. Şu anda kafanıza bir şey kapanmalı ve ses çıkmalı: “neden bu?!” Bellek şeridi taşması klasik hata ayıklayıcıda görüntülenmez; donanım performans sayacı hata ayıklayıcısını çalıştırmanız ve bunu kendiniz ve doğrudan görmeniz gerekir. Ancak bu üç semptomdan doğrudan şüphelenilemez. Üçüncü belirti, vurguladığınız şeye baktığınızda, profil oluşturucuya şunu sormanızdır: "Bir milyar satır oluşturdunuz, ancak GC bedava çalıştı." Bu gerçekleştiği anda çok fazla nesne yarattığınızı ve tüm hafıza şeridini yaktığınızı fark edersiniz. Bunu anlamanın bir yolu var ama açık değil. 

Sorun veri yapısında: olup biten her şeyin altında yatan çıplak yapı, çok büyük, diskte 2.7G var, bu nedenle bu şeyin bir kopyasını oluşturmak çok istenmeyen bir durum - onu hemen ağ bayt arabelleğinden yüklemek istiyorsunuz Satırda beş kez ileri geri okuma-yazma yapmamak için kayıtlara. Ne yazık ki Java, varsayılan olarak JDK'nın bir parçası olarak size böyle bir kitaplık vermez. Ama bu önemsiz, değil mi? Temel olarak bunlar, temeldeki bayt arabelleğinin etrafında bir sarmalayıcı görevi görürken dize sınıfının davranışını tekrarlayan, kendi arabelleğe alınmış dize yükleyicinizi uygulamak için kullanılacak 5-10 satırlık koddur. Sonuç olarak, neredeyse dizelerle çalışıyormuşsunuz gibi görünüyor, ancak aslında arabelleğe işaret eden işaretçiler oraya hareket ediyor ve ham baytlar hiçbir yere kopyalanmıyor ve böylece aynı arabellekler tekrar tekrar kullanılıyor ve işletim sistemi, bu bayt arabelleklerinin gizli çift ara belleğe alınması gibi, tasarlandığı şeyleri kendiniz üstlenmekten mutluluk duyar ve artık sonsuz bir gereksiz veri akışıyla uğraşmazsınız. Bu arada, GC ile çalışırken, son GC döngüsünden sonra her bellek tahsisinin işlemci tarafından görülmeyeceğinin garanti edildiğini anlıyor musunuz? Bu nedenle, tüm bunların önbellekte olması mümkün değildir ve bu durumda %100 garantili bir ıskalama meydana gelir. Bir işaretçi ile çalışırken, x86'da, bir kaydı bellekten çıkarmak 1-2 saat döngüsü alır ve bu olur olmaz, ödersiniz, ödersiniz, ödersiniz, çünkü bellek tamamen açıktır DOKUZ önbellek – ve bu, hafıza tahsisinin maliyetidir. Gerçek değer.

Başka bir deyişle veri yapıları değiştirilmesi en zor şeydir. Ve daha sonra performansı düşürecek yanlış veri yapısını seçtiğinizi fark ettiğinizde, genellikle yapılması gereken çok iş vardır, ancak bunu yapmazsanız işler daha da kötüleşecektir. Öncelikle veri yapılarını düşünmeniz gerekiyor, bu önemli. Buradaki asıl maliyet, “Y'nin şeklini daha çok sevdiğim için X veri yapısını Y veri yapısına kopyaladım” tarzında kullanılmaya başlayan şişman veri yapılarına düşüyor. Ancak kopyalama işlemi (ucuz görünen) aslında bellek bant genişliğini boşa harcar ve boşa harcanan yürütme süresinin tamamı burada gömülür. Devasa bir JSON dizem varsa ve onu POJO'lardan oluşan yapılandırılmış bir DOM ağacına veya buna benzer bir şeye dönüştürmek istersem, bu dizeyi ayrıştırma ve POJO'yu oluşturma ve ardından POJO'ya daha sonra tekrar erişme işlemi gereksiz maliyetle sonuçlanacaktır - bu ucuz değil. POJO'ların etrafında bir ipin etrafında koşmaktan çok daha sık koşmanız dışında. Bunun yerine, hazırlıksız olarak dizenin şifresini çözmeyi deneyebilir ve onu herhangi bir POJO'ya dönüştürmeden yalnızca ihtiyacınız olan şeyi oradan çıkarabilirsiniz. Tüm bunlar maksimum performansın gerekli olduğu bir yolda gerçekleşirse, sizin için POJO yok, bir şekilde doğrudan çizgiye girmeniz gerekir.

Neden kendi programlama dilinizi yaratmalısınız?

Andrew: Maliyet modelini anlamak için kendi küçük dilinizi yazmanız gerektiğini söylemiştiniz...

Uçurum: Bir dil değil, bir derleyici. Dil ve derleyici iki farklı şeydir. En önemli fark kafanızdadır. 

Andrew: Bu arada bildiğim kadarıyla kendi dillerinizi yaratma denemeleri yapıyorsunuz. Ne için?

Uçurum: Çünkü yapabilirim! Yarı emekliyim, dolayısıyla bu benim hobim. Hayatım boyunca başkalarının dillerini uyguladım. Ayrıca kodlama stilim üzerinde de çok çalıştım. Ve ayrıca diğer dillerde de sorunlar görüyorum. Tanıdık şeyleri yapmanın daha iyi yollarının olduğunu görüyorum. Ve onları kullanırdım. Kendimde, Java'da, Python'da veya başka herhangi bir dilde sorun görmekten yoruldum. Artık React Native, JavaScript ve Elm'de emeklilikle ilgili değil aktif çalışmayla ilgili bir hobi olarak yazıyorum. Ayrıca Python'da yazıyorum ve büyük olasılıkla Java arka uçları için makine öğrenimi üzerinde çalışmaya devam edeceğim. Pek çok popüler dil var ve hepsinin ilginç özellikleri var. Herkes kendine göre iyidir ve tüm bu özellikleri bir araya getirmeye çalışabilirsiniz. Bu yüzden ilgimi çeken şeyleri, dilin davranışını inceliyorum, makul bir anlambilim bulmaya çalışıyorum. Ve şu ana kadar başardım! Şu anda bellek semantiğiyle uğraşıyorum çünkü C ve Java'daki gibi olmasını ve yükler ve depolar için güçlü bir bellek modeli ve bellek semantiği elde etmek istiyorum. Aynı zamanda Haskell'deki gibi otomatik tür çıkarımına da sahip olabilirsiniz. Burada Haskell benzeri tür çıkarımını hem C hem de Java'da bellek çalışmasıyla karıştırmaya çalışıyorum. Mesela son 2-3 aydır yaptığım şey bu.

Andrew: Diğer dillerden daha iyi yönleri olan bir dil oluşturursanız, birisinin tam tersini yapıp fikirlerinizi alıp kullanacağını mı düşünüyorsunuz?

Uçurum: Yeni diller tam olarak böyle ortaya çıkıyor! Java neden C'ye benzer? Çünkü C'nin herkesin anlayabileceği iyi bir sözdizimi vardı ve Java bu sözdiziminden esinlenerek tür güvenliği, dizi sınırları denetimi, GC ekledi ve ayrıca C'den bazı şeyleri geliştirdiler. Kendilerini de eklediler. Ama oldukça ilham aldılar, değil mi? Herkes sizden önce gelen devlerin omuzlarında duruyor; ilerleme böyle sağlanır.

Andrew: Anladığım kadarıyla diliniz hafızada güvende olacak. Rust'tan ödünç alma denetleyicisi gibi bir şey uygulamayı düşündünüz mü? Ona baktın mı, onun hakkında ne düşünüyorsun?

Uçurum: Uzun zamandır C yazıyorum, tüm bu malloc ve bedava ve kullanım ömrünü manuel olarak yönetiyorum. Biliyorsunuz manuel olarak kontrol edilen ömrün %90-95'i aynı yapıya sahiptir. Ve bunu manuel olarak yapmak çok ama çok acı verici. Derleyicinin size basitçe orada neler olup bittiğini ve eylemlerinizle neyi başardığınızı anlatmasını istiyorum. Bazı şeyler için, ödünç alma denetleyicisi bunu kutunun dışında yapar. Ve bilgileri otomatik olarak görüntülemeli, her şeyi anlamalı ve hatta bu anlayışı sunma konusunda bana yük oluşturmamalı. En azından yerel kaçış analizi yapması gerekir ve yalnızca başarısız olursa, ömrünü tanımlayacak tür açıklamaları eklemesi gerekir - ve böyle bir şema, bir ödünç alma denetleyicisinden veya mevcut herhangi bir bellek denetleyicisinden çok daha karmaşıktır. "Her şey yolunda" ile "Hiçbir şey anlamıyorum" arasındaki seçim - hayır, daha iyi bir şey olmalı. 
Yani C dilinde çok fazla kod yazmış biri olarak otomatik ömür boyu kontrol desteğine sahip olmanın en önemli şey olduğunu düşünüyorum. Ayrıca Java'nın belleği ne kadar kullandığından da bıktım ve ana şikayet GC'dir. Java'da bellek ayırdığınızda, son GC döngüsünde yerel olan belleği geri alamazsınız. Bellek yönetiminin daha hassas olduğu dillerde ise durum böyle değildir. Malloc'u çağırırsanız, genellikle az önce kullanılan hafızayı hemen alırsınız. Genellikle hafızayla bazı geçici şeyler yaparsınız ve hemen geri getirirsiniz. Ve hemen malloc havuzuna geri döner ve bir sonraki malloc döngüsü onu tekrar dışarı çeker. Bu nedenle, gerçek bellek kullanımı, belirli bir zamandaki canlı nesneler artı sızıntılara indirgenir. Ve her şey tamamen uygunsuz bir şekilde sızdırılmazsa, belleğin çoğu önbelleklerde ve işlemcide kalır ve hızlı çalışır. Ancak malloc ve free ile doğru sırada, doğru yerde çağrılmasıyla çok sayıda manuel bellek yönetimi gerektirir. Rust bunu kendi başına düzgün bir şekilde halledebilir ve çoğu durumda daha da iyi performans sağlar; çünkü bellek tüketimi, belleği boşaltmak için bir sonraki GC döngüsünü beklemek yerine yalnızca mevcut hesaplamayla sınırlandırılır. Sonuç olarak performansı artırmanın çok ilginç bir yolunu bulduk. Ve oldukça güçlü, yani fintech için veri işlerken böyle şeyler yaptım ve bu da yaklaşık beş kat hızlanmamı sağladı. Bu, özellikle işlemcilerin hızlanmadığı ve hala iyileştirmeler beklediğimiz bir dünyada oldukça büyük bir artış.

Performans Mühendisi Kariyeri

Andrew: Ayrıca genel olarak kariyer hakkında da soru sormak istiyorum. HotSpot'ta JIT çalışmalarınızla ön plana çıktınız ve ardından yine bir JVM şirketi olan Azul'a taşındınız. Ancak biz zaten yazılımdan çok donanım üzerinde çalışıyorduk. Daha sonra aniden Büyük Veri ve Makine Öğrenimine, ardından da dolandırıcılık tespitine geçtiler. Bu nasıl oldu? Bunlar çok farklı gelişim alanlarıdır.

Uçurum: Uzun zamandır programlama yapıyorum ve birçok farklı ders almayı başardım. Ve insanlar şöyle dediğinde: "Ah, Java için JIT'i yapan sensin!", bu her zaman komiktir. Ancak ondan önce, Apple'ın bir zamanlar lazer yazıcıları için kullandığı dil olan PostScript'in bir kopyası üzerinde çalışıyordum. Ve ondan önce Dördüncü dilin bir uygulamasını yaptım. Benim için ortak temanın araç geliştirme olduğunu düşünüyorum. Hayatım boyunca başkalarının harika programlarını yazabilecekleri araçlar yaptım. Ancak aynı zamanda işletim sistemlerinin, sürücülerin, çekirdek düzeyinde hata ayıklayıcıların, işletim sistemi geliştirme dillerinin geliştirilmesinde de yer aldım; bunlar başlangıçta önemsizdi, ancak zamanla giderek daha karmaşık hale geldi. Ancak asıl konu hâlâ araçların geliştirilmesidir. Hayatımın büyük bir kısmı Azul ve Sun arasında geçti ve bu Java ile ilgiliydi. Ancak Büyük Veri ve Makine Öğrenimine girdiğimde süslü şapkamı tekrar taktım ve şöyle dedim: "Ah, artık önemsiz olmayan bir sorunumuz var ve pek çok ilginç şey oluyor ve insanlar bir şeyler yapıyor." Bu, izlenmesi gereken harika bir gelişim yoludur.

Evet, dağıtılmış bilişimi gerçekten seviyorum. İlk işim C öğrencisiyken bir reklam projesinde yer almaktı. Bu, gerçek bir analog analizör tarafından üretilen analog OCR için veri toplayan Zilog Z80 yongaları üzerinde dağıtılmış hesaplamaydı. Harika ve tamamen çılgın bir konuydu. Ama sorunlar vardı, bir kısmı doğru tanınmıyordu, bu yüzden bir fotoğraf çıkarıp onu zaten gözleriyle okuyabilen ve ne yazdığını rapor edebilen birine göstermeniz gerekiyordu ve bu nedenle veri içeren işler vardı ve bu işler kendi dilleri vardı. Tüm bunları işleyen bir arka uç vardı - Z80'ler paralel olarak çalışan vt100 terminalleri ile çalışıyordu - kişi başına bir tane ve Z80'de paralel bir programlama modeli vardı. Yıldız konfigürasyonunda tüm Z80'ler tarafından paylaşılan bazı ortak bellek parçaları; Arka panel de paylaşılıyordu ve RAM'in yarısı ağ içinde paylaşılıyordu, diğer yarısı ise özeldi veya başka bir şeye gidiyordu. Paylaşılan, yarı paylaşılan belleğe sahip, anlamlı derecede karmaşık, paralel dağıtılmış bir sistem. Bu ne zamandı... 80'lerin ortasında bir yerlerde, hatırlamıyorum bile. Oldukça uzun zaman önce. 
Evet, farz edelim ki 30 yıl çok uzun bir zaman önce, dağıtık bilişimle ilgili sorunlar oldukça uzun süredir var; insanlar uzun süredir birbirleriyle savaş halindeler. Beowulf-kümeler. Bu tür kümeler şöyle görünür... Örneğin: Ethernet var ve hızlı x86'nız bu Ethernet'e bağlı ve şimdi sahte paylaşımlı bellek almak istiyorsunuz, çünkü o zamanlar kimse dağıtılmış hesaplama kodlaması yapamıyordu, bu çok zordu ve bu nedenle oradaydı. x86'da koruma hafızası sayfalarına sahip sahte paylaşılan hafızaydı ve bu sayfaya yazdıysanız, diğer işlemcilere aynı paylaşılan hafızaya erişirlerse bunun sizden yüklenmesi gerektiğini ve dolayısıyla destek protokolü gibi bir şey söylediğimizi söyledik. önbellek tutarlılığı ortaya çıktı ve bunun için yazılım. İlginç bir konsept. Asıl sorun elbette başka bir şeydi. Bütün bunlar işe yaradı, ancak hızlı bir şekilde performans sorunlarıyla karşılaştınız, çünkü hiç kimse performans modellerini yeterince iyi bir seviyede anlamadı - hangi bellek erişim modellerinin mevcut olduğu, düğümlerin birbirlerine sonsuz şekilde ping atmadıklarından nasıl emin olunacağı vb.

H2O'da ortaya çıkardığım şey, paralelliğin nerede gizli olduğunu ve nerede olmadığını belirlemekten geliştiricilerin kendilerinin sorumlu olduğuydu. Yüksek performanslı kod yazmayı kolay ve basit hale getiren bir kodlama modeli buldum. Ancak yavaş çalışan kod yazmak zordur, kötü görünecektir. Yavaş kod yazmayı ciddi şekilde denemeniz gerekiyor, standart olmayan yöntemler kullanmanız gerekecek. Fren kodu bir bakışta görülebilir. Sonuç olarak, genellikle hızlı çalışan kodlar yazarsınız ancak paylaşılan bellek durumunda ne yapacağınızı bulmanız gerekir. Bütün bunlar büyük dizilere bağlıdır ve buradaki davranış, paralel Java'daki geçici olmayan büyük dizilere benzer. Yani iki thread'in paralel bir diziye yazdığını, biri kazandığını, diğerinin buna göre kaybettiğini ve hangisinin hangisi olduğunu bilmediğinizi hayal edin. Eğer değişken değillerse, o zaman emir istediğiniz gibi olabilir ve bu gerçekten işe yarar. İnsanlar işlem sırasını gerçekten önemsiyorlar, uçucuları doğru yere koyuyorlar ve bellekle ilgili performans sorunlarının doğru yerlere gelmesini bekliyorlar. Aksi takdirde, tüm karmaşık durumların otomatik olarak paralel hale gelmesi umuduyla, N'nin birkaç trilyon olduğu 1'den N'ye kadar döngüler şeklinde kod yazarlardı ve bu burada işe yaramazdı. Ama H2O'da bu ne Java ne de Scala; isterseniz “Java eksi eksi” olarak düşünebilirsiniz. Bu çok açık bir programlama stilidir ve döngüler ve dizilerle basit C veya Java kodu yazmaya benzer. Ancak aynı zamanda hafıza terabaytlarca işlenebilir. Halen H2O kullanıyorum. Zaman zaman farklı projelerde kullanıyorum ve hala en hızlı şey, rakiplerinden onlarca kat daha hızlı. Sütunsal verilerle Büyük Veri yapıyorsanız H2O'yu yenmek çok zordur.

Teknik Zorluklar

Andrew: Tüm kariyeriniz boyunca karşılaştığınız en büyük zorluk neydi?

Uçurum: İşin teknik kısmını mı, teknik olmayan kısmını mı tartışıyoruz? En büyük zorlukların teknik sorunlar olmadığını söyleyebilirim. 
Teknik zorluklara gelince. Onları basitçe yendim. En büyüğünün ne olduğunu bile bilmiyorum ama epeyce zaman alan, zihinsel çaba gerektiren oldukça ilginç olanlar da vardı. Sun'a gittiğimde hızlı bir derleyici olacağımdan emindim ve bir grup son sınıf öğrencisi buna yanıt olarak asla başarılı olamayacağımı söyledi. Ama ben bu yolu izledim, kayıt ayırıcıya bir derleyici yazdım ve oldukça hızlıydı. Modern C1 kadar hızlıydı, ancak o zamanlar tahsis edici çok daha yavaştı ve geriye dönüp bakıldığında bunun büyük bir veri yapısı sorunu olduğu ortaya çıktı. Grafiksel bir kayıt ayırıcı yazmak için buna ihtiyacım vardı ve o dönemde var olan ve çok önemli olan kod ifadesi ile hız arasındaki ikilemi anlamadım. Veri yapısının genellikle o zamanın x86'larındaki önbellek boyutunu aştığı ortaya çıktı ve bu nedenle, başlangıçta kayıt ayırıcının toplam titreşim süresinin yüzde 5-10'unu hesaplayacağını varsaydım, o zaman gerçekte öyle olduğu ortaya çıktı yüzde 50.

Zaman geçtikçe derleyici daha temiz ve verimli hale geldi, çoğu durumda berbat kodlar üretmeyi bıraktı ve performans giderek bir C derleyicisinin ürettiğine benzemeye başladı.Tabii ki C'nin bile hızlandıramayacağı saçmalıklar yazmadığınız sürece . C gibi kod yazarsanız daha çok durumda C gibi performans alırsınız. Ve ne kadar ileri giderseniz, asimptotik olarak C seviyesiyle çakışan kodla o kadar sık ​​karşılaşıyorsunuz, kayıt ayırıcısı tam bir şey gibi görünmeye başladı... kodunuzun hızlı ya da yavaş çalışmasına bakılmaksızın. Daha iyi seçimler yapabilmesi için ayırıcı üzerinde çalışmaya devam ettim. Gittikçe yavaşladı ama kimsenin baş edemeyeceği durumlarda giderek daha iyi performans gösterdi. Bir kayıt ayırıcıya dalabilirdim, bir aylık çalışmayı oraya gömebilirdim ve birdenbire kodun tamamı %5 daha hızlı çalışmaya başlardı. Bu defalarca oldu ve kayıt tahsisi bir sanat eserine dönüştü - herkes onu sevdi ya da nefret etti ve akademiden insanlar "neden her şey bu şekilde yapılıyor", neden olmasın gibi sorular sordular. satır taramasıve fark nedir? Cevap hala aynı: grafik renklendirmeye dayalı bir tahsis edici artı tampon koduyla çok dikkatli çalışmak, bir zafer silahına eşittir, kimsenin yenemeyeceği en iyi kombinasyon. Ve bu oldukça açık olmayan bir şey. Derleyicinin yaptığı diğer her şey oldukça iyi çalışılmış şeylerdir, ancak bunlar aynı zamanda sanat düzeyine de getirilmiştir. Her zaman derleyiciyi bir sanat eserine dönüştürmesi gereken şeyler yaptım. Ancak kayıt tahsisi dışında bunların hiçbiri olağanüstü değildi. İşin sırrı dikkatli olmaktır kesmek yük altında ve eğer bu gerçekleşirse (eğer ilgilenirsem daha ayrıntılı olarak açıklayabilirim), bu, performans çizelgesinde bir aksaklığa düşme riski olmadan daha agresif bir şekilde satır içi yapabileceğiniz anlamına gelir. O günlerde, kayıt ayırıcıları olan, süs eşyaları ve ıslıklarla asılı bir grup tam ölçekli derleyici vardı, ancak bunu başka kimse yapamazdı.

Sorun şu ki, satır içi yapmaya, satır içi alanı artırmaya ve artırmaya tabi yöntemler eklerseniz, kullanılan değerler kümesi anında kayıt sayısını aşar ve bunları kesmek zorunda kalırsınız. Kritik seviye genellikle dağıtımcı vazgeçtiğinde gelir ve bir dökülme için iyi bir aday diğerine değerse, bazı genel olarak çılgın şeyler satarsınız. Buradaki satır içi yazmanın değeri, ek yükün bir kısmını kaybetmeniz, arama ve kaydetme ek yükünü kaybetmeniz, içindeki değerleri görebilmeniz ve bunları daha da optimize edebilmenizdir. Satır içi yerleştirmenin maliyeti, çok sayıda canlı değerin oluşmasıdır ve kayıt ayırıcınız gereğinden fazla yanarsa hemen kaybedersiniz. Bu nedenle çoğu dağıtımcının bir sorunu var: Satır içi belirli bir çizgiyi geçtiğinde, dünyadaki her şey kesilmeye başlıyor ve üretkenlik tuvalete atılabiliyor. Derleyiciyi uygulayanlar bazı buluşsal yöntemler eklerler: örneğin, yeterince büyük bir boyuttan başlayarak satır içi yazmayı durdurmak, çünkü tahsisler her şeyi mahveder. Performans grafiğinde bir bükülme bu şekilde oluşur - satır içi, satır içi, performans yavaş yavaş büyür - ve sonra patlama! – çok fazla dizildiğin için hızlı bir kriko gibi düşüyor. Java'nın ortaya çıkışından önce her şey böyle çalışıyordu. Java çok daha fazla satır içi oluşturmayı gerektiriyor, bu yüzden ayırıcımı çok daha agresif hale getirmek zorunda kaldım, böylece çökmek yerine düzleşiyor ve çok fazla satır içi yaparsanız dökülmeye başlıyor, ancak sonra "artık dökülmeme" anı yine de geliyor. Bu ilginç bir gözlem ve birdenbire aklıma geldi, belli değil ama işe yaradı. Agresif satır içi kullanmaya başladım ve bu beni Java ve C performansının yan yana çalıştığı yerlere götürdü. Gerçekten yakınlar; C kodundan önemli ölçüde daha hızlı Java kodu yazabiliyorum ve bunun gibi şeyler, ancak ortalama olarak, büyük resimde kabaca karşılaştırılabilirler. Sanırım bu değerin bir kısmı, mümkün olduğunca aptalca satır içi yapmamı sağlayan kayıt ayırıcıdır. Gördüğüm her şeyi satır içine alıyorum. Buradaki soru, ayırıcının iyi çalışıp çalışmadığı, sonucun akıllıca çalışan kod olup olmadığıdır. Bu büyük bir zorluktu: Bütün bunları anlamak ve işe yaramasını sağlamak.

Kayıt tahsisi ve çoklu çekirdekler hakkında biraz bilgi

vladimir: Kasa tahsisi gibi sorunlar bitmeyecek bir konu gibi görünüyor. Hiç umut verici görünen ve sonra pratikte başarısızlığa uğrayan bir fikir oldu mu acaba?

Uçurum: Kesinlikle! Kayıt tahsisi, NP tamamlama problemini çözmek için bazı buluşsal yöntemler bulmaya çalıştığınız bir alandır. Ve asla mükemmel bir çözüme ulaşamazsınız, değil mi? Bu kesinlikle imkansızdır. Bakın, Vaktinden Önce derleme - aynı zamanda kötü çalışıyor. Buradaki konuşma bazı ortalama vakalarla ilgili. Tipik performans hakkında, iyi bir tipik performans olduğunu düşündüğünüz bir şeyi gidip ölçebilirsiniz - sonuçta onu geliştirmek için çalışıyorsunuz! Kayıt tahsisi tamamen performansla ilgili bir konudur. İlk prototipi aldıktan sonra çalışır ve ihtiyaç olanı boyar, performans çalışması başlar. İyi ölçmeyi öğrenmeniz gerekir. Neden önemlidir? Eğer net verileriniz varsa, farklı alanlara bakıp şunu görebilirsiniz: evet, burada yardımcı oldu ama her şey burada bozuldu! Bazı iyi fikirler ortaya çıkıyor, yeni buluşsal yöntemler ekliyorsunuz ve aniden her şey ortalama olarak biraz daha iyi çalışmaya başlıyor. Veya başlamıyor. Gelişimimizi önceki tahsisatçıdan farklılaştıran yüzde beşlik performans için mücadele ettiğimiz birçok durumla karşılaştım. Ve her seferinde şuna benziyor: bir yerde kazanıyorsun, bir yerde kaybediyorsun. İyi performans analizi araçlarına sahipseniz, kaybedilen fikirleri bulabilir ve neden başarısız olduklarını anlayabilirsiniz. Belki her şeyi olduğu gibi bırakmaya değer, ya da belki ince ayar yapmak için daha ciddi bir yaklaşım benimsemek ya da dışarı çıkıp başka bir şeyi tamir etmeye değer. Bir sürü şey var! Bu harika tüyoyu yaptım ama aynı zamanda buna, buna ve buna da ihtiyacım var ve bunların toplam kombinasyonu bazı iyileştirmeler sağlıyor. Ve yalnızlar başarısız olabilir. Bu, NP-tam problemler üzerindeki performans çalışmasının doğasıdır.

vladimir: Tahsisatçılarda resim yapma gibi şeylerin zaten çözülmüş bir sorun olduğu hissine kapılıyoruz. Peki, söylediklerine bakılırsa, buna senin adına karar verildi, o zaman buna değer mi...

Uçurum: Bu şekilde çözülmez. Bunu “çözülmüş” hale getirmesi gereken sizsiniz. Zor sorunlar var ve bunların çözülmesi gerekiyor. Bu yapıldıktan sonra üretkenlik üzerinde çalışmanın zamanı geldi. Bu işe buna göre yaklaşmanız gerekiyor - kıyaslamalar yapın, ölçümler toplayın, önceki bir sürüme geri döndüğünüzde eski hack'inizin yeniden çalışmaya başladığı (veya tam tersi durduğu) durumları açıklayın. Ve bir şeyi başarana kadar pes etmeyin. Daha önce de söylediğim gibi, işe yaramayan harika fikirler varsa, ancak fikir kayıtlarının tahsisi alanında bu neredeyse sonsuzdur. Örneğin bilimsel yayınları okuyabilirsiniz. Gerçi artık bu alan çok daha yavaş ilerlemeye başladı ve gençliğine göre daha belirgin hale geldi. Ancak bu alanda çalışan sayısız insan var ve onların tüm fikirleri denemeye değer, hepsi de kenarda bekliyor. Ve denemeden ne kadar iyi olduklarını anlayamazsınız. Tahsis edicinizdeki diğer her şeyle ne kadar iyi entegre oluyorlar, çünkü bir tahsisatçı pek çok şey yapar ve bazı fikirler sizin spesifik tahsisatçınızda çalışmaz, ancak başka bir tahsis edicide kolayca işe yarayacaktır. Dağıtımcı için kazanmanın ana yolu, yavaş şeyleri ana yolun dışına çekmek ve onu yavaş yolların sınırları boyunca bölünmeye zorlamaktır. Yani bir GC çalıştırmak, yavaş yolu seçmek, optimizasyonu kaldırmak, bir istisna atmak, tüm bunlar gibi şeyler istiyorsanız, bunların nispeten nadir olduğunu bilirsiniz. Ve gerçekten nadir olduklarını kontrol ettim. Fazladan iş yaparsınız ve bu, bu yavaş yollardaki birçok kısıtlamayı ortadan kaldırır, ancak bu aslında önemli değildir çünkü yavaştırlar ve nadiren seyahat edilirler. Örneğin, bir boş gösterici - bu asla olmaz, değil mi? Farklı şeyler için birkaç yola sahip olmanız gerekir, ancak bunlar ana yola müdahale etmemelidir. 

vladimir: Aynı anda binlerce çekirdek varken çoklu çekirdekler hakkında ne düşünüyorsunuz? Bu yararlı bir şey mi?

Uçurum: GPU'nun başarısı oldukça kullanışlı olduğunu gösteriyor!

vladimir: Oldukça uzmandırlar. Genel amaçlı işlemciler ne olacak?

Uçurum: Bu Azul'un iş modeliydi. Cevap, insanların öngörülebilir performansı gerçekten sevdiği bir dönemde geldi. O zamanlar paralel kod yazmak zordu. H2O kodlama modeli yüksek oranda ölçeklenebilir ancak genel amaçlı bir model değildir. Belki GPU kullanmaya göre biraz daha genel. Böyle bir şeyi geliştirmenin karmaşıklığından mı, yoksa onu kullanmanın karmaşıklığından mı bahsediyoruz? Örneğin, Azul bana ilginç bir ders verdi, oldukça açık olmayan bir ders: küçük önbellekler normaldir. 

Hayattaki en büyük zorluk

vladimir: Peki ya teknik olmayan zorluklar?

Uçurum: En büyük zorluk... insanlara karşı nazik ve nazik olmamaktı. Sonuç olarak kendimi sürekli olarak aşırı çatışma durumlarının içinde buldum. İşlerin ters gittiğini bildiğim ama bu sorunlarla nasıl ilerleyeceğimi bilmediğim ve onlarla baş edemediğim durumlar. Onlarca yıl süren birçok uzun vadeli sorun bu şekilde ortaya çıktı. Java'nın C1 ve C2 derleyicilerine sahip olması bunun doğrudan bir sonucudur. On yıl boyunca Java'da çok düzeyli derlemenin olmaması da bunun doğrudan bir sonucudur. Böyle bir sisteme ihtiyacımız olduğu açık ama neden olmadığı belli değil. Bir mühendisle ya da bir grup mühendisle sorun yaşadım. Bir zamanlar, Sun'da çalışmaya başladığımda ben... Tamam, sadece o zaman değil, genel olarak her şey hakkında her zaman kendi fikrim vardır. Ben de senin bu gerçeğini alıp doğrudan anlatabileceğinin doğru olduğunu düşündüm. Özellikle çoğu zaman şaşırtıcı derecede haklı olduğum için. Ve eğer bu yaklaşımdan hoşlanmıyorsanız... özellikle de açıkça hatalıysanız ve saçma sapan şeyler yapıyorsanız... Genel olarak çok az kişi bu iletişim biçimini tolere edebilir. Gerçi benim gibi bazıları bunu yapabilir. Tüm hayatımı meritokratik ilkeler üzerine kurdum. Bana yanlış bir şey gösterirsen hemen dönüp şöyle diyeceğim: saçmalık söyledin. Aynı zamanda elbette özür dilerim ve varsa esaslarını not edeceğim ve diğer doğru adımları atacağım. Öte yandan, toplam sürenin şaşırtıcı derecede büyük bir yüzdesi konusunda şaşırtıcı derecede haklıyım. Ve insanlarla ilişkilerde pek işe yaramıyor. Kibar olmaya çalışmıyorum ama soruyu açıkça soruyorum. "Bu asla işe yaramayacak çünkü bir, iki ve üç." Onlar da "Ah!" dediler. Muhtemelen görmezden gelinmesi daha iyi olan başka sonuçlar da vardı: örneğin, karımdan boşanmaya ve ondan sonra on yıl süren depresyona yol açanlar.

Mücadele, insanlarla, onların neyi yapıp yapamayacağınıza, neyin önemli neyin önemsiz olduğuna dair algılarıyla bir mücadeledir. Kodlama stiliyle ilgili birçok zorluk vardı. Hâlâ çok fazla kod yazıyorum ve o günlerde yavaşlamak zorunda bile kaldım çünkü çok fazla paralel görev yapıyordum ve tek bir şeye odaklanmak yerine bunları kötü yapıyordum. Geriye dönüp baktığımda Java JIT komutunun yani C2 komutunun kodunun yarısını yazdım. Bir sonraki en hızlı kodlayıcı yarı yarıya yavaş, sonraki yarısını da yavaş yazıyordu ve bu üstel bir düşüştü. Bu sıradaki yedinci kişi çok ama çok yavaştı; bu her zaman olur! Çok fazla koda dokundum. Kimin ne yazmış olduğuna baktım, istisnasız kodlarına baktım, her birini inceledim ve yine de hepsinden daha çok kendim yazmaya devam ettim. Bu yaklaşım insanlarda pek işe yaramıyor. Bazı insanlar bundan hoşlanmaz. Ve bunu kaldıramadıklarında her türlü şikayet başlar. Örneğin, bir keresinde bana kodlamayı bırakmam söylendi çünkü çok fazla kod yazıyordum ve bu takımı tehlikeye atıyordu ve tüm bunlar bana şaka gibi geldi: ahbap, eğer takımın geri kalanı kaybolursa ve ben kod yazmaya devam edersem, sen Takımların sadece yarısını kaybedeceğiz. Öte yandan, eğer ben kod yazmaya devam edersem ve siz ekibin yarısını kaybederseniz, bu çok kötü bir yönetim gibi görünüyor. Bunun hakkında hiç düşünmedim, bunun hakkında hiç konuşmadım ama hâlâ kafamın bir yerindeydi. Aklımın bir köşesinde şu düşünce dönüyordu: "Hepiniz benimle dalga mı geçiyorsunuz?" Yani en büyük sorun ben ve insanlarla ilişkilerimdi. Artık kendimi çok daha iyi anlıyorum, uzun süre programcıların takım liderliğini yaptım ve şimdi insanlara doğrudan şunu söylüyorum: Bilirsiniz, ben buyum ve benimle uğraşmak zorunda kalacaksınız - ayakta durmam sorun olur mu? Burada? Ve bununla uğraşmaya başladıklarında her şey işe yaradı. Aslında ne kötüyüm ne de iyiyim, kötü bir niyetim ya da bencil arzularım yok, bu sadece benim özüm ve bir şekilde bununla yaşamam gerekiyor.

Andrew: Son zamanlarda herkes içedönükler için kişisel farkındalık ve genel olarak sosyal beceriler hakkında konuşmaya başladı. Bu konuda ne söyleyebilirsiniz?

Uçurum: Evet, karımdan boşanmamdan öğrendiğim içgörü ve ders buydu. Boşanmadan öğrendiğim şey kendimi anlamaktı. Böylece diğer insanları anlamaya başladım. Bu etkileşimin nasıl çalıştığını anlayın. Bu da birbiri ardına keşiflerin yapılmasına yol açtı. Kim olduğuma ve neyi temsil ettiğime dair bir farkındalık vardı. Ne yapıyorum: Ya görevle meşgulüm ya da çatışmadan kaçınıyorum ya da başka bir şey - ve bu düzeydeki öz farkındalık gerçekten kendimi kontrol altında tutmama yardımcı oluyor. Bundan sonra her şey çok daha kolay oluyor. Yalnızca kendimde değil, diğer programcılarda da keşfettiğim bir şey, duygusal stres halindeyken düşünceleri söze dökememe yeteneğidir. Mesela orada oturup akış halinde kodlıyorsunuz ve sonra koşarak yanınıza geliyorlar ve histerik bir şekilde bir şeylerin bozulduğunu ve artık size karşı aşırı önlemler alınacağını bağırmaya başlıyorlar. Ve tek kelime edemiyorsun çünkü duygusal stres içindesin. Edinilen bilgi, bu ana hazırlanmanıza, hayatta kalmanıza ve bir geri çekilme planına geçmenize olanak tanır, ardından bir şeyler yapabilirsiniz. Yani evet, her şeyin nasıl çalıştığını anlamaya başladığınızda, bu hayatınızı değiştirecek çok büyük bir olaydır. 
Ben kendim doğru kelimeleri bulamadım ama eylemlerin sırasını hatırladım. Mesele şu ki, bu tepki sözlü olduğu kadar fiziksel de oluyor ve alana ihtiyacınız var. Zen anlamında böyle bir alan. Açıklanması gereken tam olarak budur ve hemen kenara çekilin - tamamen fiziksel olarak uzaklaşın. Sözlü olarak sessiz kaldığımda duygusal olarak durumu işleyebiliyorum. Adrenalin beyninize ulaştığında, sizi savaş ya da kaç moduna geçirdiğinde, artık hiçbir şey söyleyemezsiniz, hayır - artık bir aptalsınız, müthiş bir mühendissiniz, düzgün bir tepki veremezsiniz, hatta saldırıyı durduramazsınız ve saldırgan özgürdür. tekrar tekrar saldırmak. Önce yeniden kendin olmalısın, kontrolü yeniden kazanmalı, “savaş ya da kaç” modundan çıkmalısın.

Bunun için de sözlü alana ihtiyacımız var. Sadece boş alan. Herhangi bir şey söylerseniz, tam olarak bunu söyleyebilirsiniz ve sonra gidip kendinize gerçekten bir "alan" bulun: parkta yürüyüşe çıkın, kendinizi duşa kilitleyin - farketmez. Önemli olan bu durumdan geçici olarak bağlantıyı kesmektir. En az birkaç saniyeliğine kapattığınızda kontrol geri gelir, ayık düşünmeye başlarsınız. "Tamam, ben bir tür aptal değilim, aptalca şeyler yapmam, oldukça faydalı bir insanım." Kendinizi ikna edebildikten sonra bir sonraki aşamaya geçmenin zamanı gelmiştir: ne olduğunu anlamak. Saldırıya uğradınız, saldırı beklemediğiniz yerden geldi, hain, alçak bir pusuydu. Bu kötü. Bir sonraki adım saldırganın buna neden ihtiyaç duyduğunu anlamaktır. Gerçekten neden? Belki kendisi öfkeli olduğu için? Neden kızgın? Mesela kendini batırdığı ve sorumluluğu kabul edemediği için mi? Tüm durumu dikkatlice ele almanın yolu budur. Ancak bu, manevra alanı ve sözlü alan gerektirir. İlk adım sözlü teması kesmektir. Kelimelerle tartışmaktan kaçının. İptal edin, olabildiğince çabuk uzaklaşın. Eğer bu bir telefon görüşmesiyse, telefonu kapatın; bu, eski karımla iletişim kurarken öğrendiğim bir beceridir. Konuşma iyi bir yere gitmiyorsa "hoşçakal" deyin ve telefonu kapatın. Telefonun diğer tarafından: “blah blah blah”, siz cevap veriyorsunuz: “evet, hoşça kalın!” ve telefonu kapatın. Siz sadece konuşmayı sonlandırın. Beş dakika sonra mantıklı düşünme yeteneği size geri döndüğünde, biraz sakinleşirsiniz, her şeyi, olanları ve bundan sonra ne olacağını düşünmek mümkün hale gelir. Ve sadece duyguyla tepki vermek yerine, düşünceli bir yanıt formüle etmeye başlayın. Benim için kişisel farkındalıktaki ilerleme tam olarak duygusal stres durumunda konuşamamamdı. Bu durumdan çıkmak, nasıl tepki vereceğinizi ve sorunları nasıl telafi edeceğinizi düşünmek ve planlamak - bunlar konuşamadığınız durumlarda doğru adımlardır. En kolay yol, duygusal stresin kendini gösterdiği durumdan kaçmak ve bu strese katılmayı bırakmaktır. Bundan sonra düşünebilir hale gelirsiniz, düşünebildiğinizde konuşabilir hale gelirsiniz, vb.

Bu arada, mahkemede karşı tarafın avukatı bunu size yapmaya çalışıyor - şimdi nedeni açık. Çünkü sizi, örneğin adınızı bile telaffuz edemeyecek kadar baskılama yeteneğine sahip. Gerçek anlamda konuşamayacaksınız. Eğer başınıza bu geliyorsa ve kendinizi sözlü kavgaların yaşandığı, mahkeme gibi bir yerde bulacağınızı biliyorsanız o zaman avukatınızla gelebilirsiniz. Avukat sizin için ayağa kalkıp sözlü saldırıyı durduracak ve bunu tamamen yasal bir şekilde yapacak ve kaybolan Zen alanı size geri dönecektir. Mesela birkaç kez ailemi aramak zorunda kaldım, hakim bu konuda oldukça arkadaş canlısıydı ama karşı tarafın avukatı bana bağırıp bağırdı, tek kelime bile edemedim. Bu durumlarda arabulucu kullanmak benim için en iyisi. Arabulucu, üzerinize sürekli bir akış halinde yağan tüm bu baskıyı durdurur, gerekli Zen alanını bulursunuz ve bununla birlikte konuşma yeteneği geri döner. Bu, üzerinde çalışılacak çok şeyin olduğu, kendi içinizde keşfedilecek çok şeyin olduğu bir bilgi alanıdır ve tüm bunlar, farklı insanlar için farklı olan üst düzey stratejik kararlara dönüşür. Bazı kişilerde yukarıda açıklanan sorunlar görülmez; genellikle profesyonel satış elemanı olan kişilerde bu sorunlar görülmez. Hayatını kelimelerle kazanan tüm bu insanların, ünlü şarkıcıların, şairlerin, dini liderlerin ve politikacıların her zaman söyleyecek bir şeyleri vardır. Onların böyle sorunları yok ama benim var.

Andrew: Bu beklenmedikti. Harika, zaten çok konuştuk ve bu röportajı bitirmenin zamanı geldi. Konferansta mutlaka buluşacağız ve bu diyaloğu devam ettirebileceğiz. Hydra'da görüşürüz!

Cliff ile sohbetinize 2019-11 Temmuz 12 tarihlerinde St. Petersburg'da düzenlenecek olan Hydra 2019 konferansında devam edebilirsiniz. Raporla gelecek "Azul Donanım İşlemsel Bellek deneyimi". Biletler satın alınabilir resmi web sitesinde.

Kaynak: habr.com

Yorum ekle