Bu sayfada, etkili Bazel kuralları yazmayla ilgili belirli sorunlara ve zorluklara üst düzey bir genel bakış sunulmaktadır.
Özet Gereksinimleri
- Varsayım: Doğruluğu, İşleme Hızı, Kullanım kolaylığı ve Gecikmeyi hedefleyin
- Varsayım: Büyük Ölçekli Depolar
- Varsayım: DERLEME Açıklama Dili
- Geçmiş: Yükleme, Analiz ve Yürütme arasındaki ayrım güncel değil ancak API'yi etkilemeye devam ediyor
- Yerleşik: Uzaktan Yürütme ve Önbelleğe Alma Zor
- Yerleşik: Doğru ve Hızlı Artımlı Derlemeler için Değişiklik Bilgilerinin Kullanılması, Olağandışı Kod Kalıpları gerektirir
- Yerleşik: İkinci dereceden zaman ve bellek tüketiminden kaçınmak zordur
Varsayımlar
Derleme sistemiyle ilgili bazı varsayımlar (ör. doğruluğun, kullanım kolaylığının, işleme hızının ve büyük ölçekli depoların gerekliliği) Aşağıdaki bölümlerde bu varsayımlar ele alınmakta ve kuralların etkili bir şekilde yazılmasını sağlayan yönergeler sunulmaktadır.
Doğruluğu, işleme hızını, kullanım kolaylığını ve gecikmeyi hedefleyin
Derleme sisteminin öncelikli olarak her şeyden önce doğru olması gerektiğini varsayarız. Belirli bir kaynak ağacı için, çıkış ağacının görünüşünden bağımsız olarak aynı derlemenin çıkışı her zaman aynı olmalıdır. İlk yaklaşımda, Bazel'ın belirli bir derleme adımına giren tüm girişleri bilmesi gerekir. Böylece, herhangi bir giriş değişirse söz konusu adım yeniden çalıştırılabilir. Binanın tarihi / saati gibi bazı bilgileri sızdıran ve dosya özelliklerinde yapılan değişiklikler gibi belirli değişiklik türlerini yoksaydığı için Bazel'ın doğru şekilde alması konusunda sınırlamalar vardır. Korumalı alan, tanımlanmamış giriş dosyalarının okunmasını engelleyerek doğruluğun sağlanmasına yardımcı olur. Sistemin iç sınırları dışında, bilinen bazı doğruluk sorunları vardır. Bunların çoğu Dosya Grubu veya C++ kurallarıyla ilgilidir ve bunlar da her zaman zordur. Bu sorunu düzeltmek için uzun süre devam ediyoruz.
Derleme sisteminin ikinci hedefi, yüksek işleme hızına sahip olmaktır. Bir uzaktan yürütme hizmeti için mevcut makine ayırmada neler yapılabileceğini sürekli olarak sınırlarız. Uzaktan yürütme hizmeti aşırı yüklenirse hiç kimse iş yapamaz.
Kullanım kolaylığı sıradaki sırada. Uzaktan yürütme hizmetinin aynı (veya benzer) ayak izine sahip birden fazla doğru yaklaşımın kullanımı daha kolay olanı seçeriz.
Gecikme, derlemenin başlatılmasından istenen sonucun alınmasına kadar geçen süreyi belirtir. Bu, başarılı veya başarısız bir testten gelen bir test günlüğü ya da bir BUILD
dosyasının yazım hatası olduğunu belirten bir hata mesajıdır.
Bu hedeflerin genellikle çakıştığını unutmayın. Gecikme, kullanım kolaylığı açısından doğruluk açısından uzaktan yürütme hizmetinin işleme hızı kadar işlevseldir.
Büyük ölçekli depolar
Derleme sisteminin büyük depolarla geniş ölçekte çalışması gerekir. Bu ölçek, büyük ölçekli sistemin tek bir sabit diske sığmayacağı anlamına gelir. Bu nedenle neredeyse tüm geliştirici makinelerinde tam ödeme yapmak imkansızdır. Orta boyutlu bir binanın on binlerce BUILD
dosyasını okuyup ayrıştırması ve yüz binlerce glob'u değerlendirmesi gerekir. Teorik olarak tüm BUILD
dosyalarını tek bir makinede okumak mümkün olsa da bu işlemi makul bir süre ve bellek içinde yapamadık. Bu nedenle, BUILD
dosyalarının tek başına yüklenip ayrıştırılabilmesi çok önemlidir.
DERLEME açıklama dili
Bu bağlamda, kitaplık ve ikili program kuralları beyanında birbirine bağlı ve birbirine bağımlı olan BUILD
dosyalarına benzer bir yapılandırma dili kullanırız. BUILD
dosyaları bağımsız olarak okunabilir ve ayrıştırılabilir. Ayrıca mümkün olduğunda (varlık haricinde) kaynak dosyalara bakmaktan kaçınırız.
Tarihi
Zorluklara neden olan Bazel sürümleri arasında farklılıklar vardır. Bunlardan bazıları aşağıdaki bölümlerde açıklanmıştır.
Yükleme, analiz ve yürütme arasındaki ayrılık eski ancak API'yi etkilemeye devam ediyor
Teknik olarak, bir kuralın uzaktan yürütmeye gönderilmeden hemen önce bir işlemin giriş ve çıkış dosyalarını bilmesi yeterlidir. Bununla birlikte, orijinal Bazel kod tabanı, paketleri yükleme ve ardından bir yapılandırma (komut satırı işaretleri) kullanarak analiz etme ve ardından herhangi bir işlem yapma gibi kuralları birbirinden belirgin şekilde ayırıyordu. Bu fark, Bazel'ın temelinin artık gerekli olmamasına rağmen bugünkü kurallar API'sinin bir parçasıdır (aşağıda daha fazla ayrıntı verilmiştir).
Bu, kuralların API'sinin, kural arayüzü için bildirimli bir açıklama gerektirdiği (özellikleri, özellik türleri) anlamına gelir. API'nin, yükleme aşamasında dolaylı kodların ve özelliklerin dolaylı değerlerini hesaplamak için özel kodun çalıştırılmasına izin verdiği bazı istisnalar vardır. Örneğin, "foo" adlı bir java_library kuralı, dolaylı olarak "libfoo.jar" adlı bir çıkış oluşturur. Bu çıktı, derleme grafiğindeki diğer kurallardan başvurulabilir.
Ayrıca, bir kuralın analizi herhangi bir kaynak dosyayı okuyamaz veya bir işlemin çıkışını inceleyemez. Bunun yerine, yalnızca kuralın kendisi ve bağımlıları tarafından belirlenen derleme adımları ve çıkış dosyası adlarının kısmi, çift taraflı bir grafiğini oluşturması gerekir.
Gerçek
Yazma kurallarını zorlaştıran bazı doğuştan gelen özellikler vardır ve en sık karşılaşılan kurallardan bazıları aşağıdaki bölümlerde açıklanmıştır.
Uzaktan yürütme ve önbelleğe alma zordur
Uzaktan yürütme ve önbelleğe alma, büyük depolardaki derleme sürelerini derlemeyi tek bir makinede çalıştırmaya kıyasla yaklaşık iki büyüklükte artırır. Ancak, çalışması gereken ölçek şaşırtıcıdır: Google'ın uzaktan yürütme hizmeti, saniye başına çok sayıda isteği işleyecek şekilde tasarlanmıştır. Ayrıca protokol, gereksiz gidiş dönüşlerin yanı sıra hizmet tarafında gereksiz çalışmalar yapılmasını önler.
Şu anda protokol, derleme sisteminin belirli bir işleme önceden tüm girişleri bilmesini gerektirir. Derleme sistemi daha sonra benzersiz bir işlem parmak izini hesaplayıp planlayıcıdan bir önbellek isabetini ister. Önbellek isabeti bulunursa, planlayıcı çıkış dosyalarının özetleriyle yanıt verir; dosyaların daha sonraki bir kısmı özetle ele alınır. Ancak bu işlem tüm giriş dosyalarının önceden bildirilmesi gereken Bazel kurallarına kısıtlamalar getirir.
Doğru ve hızlı artımlı derlemeler için değişiklik bilgilerini kullanmak, olağan dışı kodlama kalıpları gerektirir
Yukarıda, Bazel'ın doğru olması için bir derleme adımına giren tüm giriş dosyalarını bilmesi gerektiğini, ancak söz konusu derleme adımının hâlâ güncel olup olmadığını tespit edebileceğini iddia ettik. Aynısı paket yükleme ve kural analizi için geçerlidir. Genel olarak bunu yapmak için Skyframe'i tasarladık. Skyframe, bir hedef düğümü (ör. "//foo with bu seçeneklerle") alıp bu modülü bileşenlerine ayırarak bu sonucu elde etmek için değerlendirilip birleştirilen bir grafik kitaplığı ve değerlendirme çerçevesidir. Skyframe bu sürecin bir parçası olarak paketleri okur, kuralları analiz eder ve işlemler yürütür.
Her bir düğümde Skyframe, belirli bir düğümün kendi çıkışını hesaplamak için kullandığı düğümleri tam olarak takip eder. Hedef düğümden giriş dosyalarına (aynı zamanda Skyframe düğümleridir) kadar tüm düğümleri izler. Bu grafiğin bellekte açık şekilde temsil edilmesi, derleme sisteminin, bir giriş dosyasında yapılan belirli bir değişiklikten (giriş dosyasının oluşturulması veya silinmesi de dahil) hangi düğümlerin tam olarak etkilendiğini belirlemesine olanak verir. Böylece, çıkış ağacını amaçlanan durumuna geri yüklemek için minimum miktarda çaba sarf edilir.
Bunun bir parçası olarak her düğüm, bir bağımlı bulma işlemi gerçekleştirir. Her düğüm bağımlılıkları bildirebilir ve daha sonra, daha da fazla bağımlıları bildirmek için bu bağımlılıkların içeriğini kullanabilir. Esas olarak, bu, düğüm başına bir modelle iyi bir şekilde eşlenir. Ancak orta ölçekli binalar yüz binlerce Skyframe düğümü içerir. Bu sayı mevcut Java teknolojisiyle kolayca sağlanamaz.
Bazel, bunun yerine sabit boyutlu bir ileti dizisi havuzu kullanır. Bununla birlikte, bir düğüm henüz kullanılamayan bir bağımlı olduğunu bildirirse bu değerlendirmeyi iptal edip bağımlılığı kullanılabilir olduğunda yeniden başlatmamız gerekebilir (muhtemelen başka bir ileti dizisinde). Bu da düğümlerin çok fazla işlem yapmaması gerektiği anlamına gelir. N bağımlılarını seri şekilde tanımlayan bir düğüm potansiyel olarak N kez yeniden başlatılabilir ve bu da O(N^2) maliyetine neden olur. Bunun yerine, yeniden başlatma sayısını sınırlandırmak için kodun yeniden organize edilmesini veya hatta bir düğümün birden fazla düğüme bölünmesini gerektiren bağımsız bağımlılıkların önceden bildirilmesi amaçlanır.
Bu teknolojinin şu anda kural API'sinde kullanılamadığını unutmayın. Bunun yerine kural API'si eski yükleme, analiz ve yürütme aşamalarıyla tanımlanmaya devam eder. Ancak temel kısıtlama, diğer bağımlılıklara karşılık gelen bağımlıların izlenebilmesi için tüm düğümlerin çerçeveden geçmesidir. Kural oluşturma işleminin uygulandığı dil veya kuralların yazıldığı dil ne olursa olsun (kuralların aynı olması gerekmez) kural yazarları, Skyframe'i atlayan standart kitaplıkları veya kalıpları kullanmamalıdır. Java için bu, java.io.File, yanı sıra her türlü düşünme biçimi ve her ikisini de yapan herhangi bir kitaplıktan kaçınmak anlamına gelir. Bu alt düzey arayüzlerin bağımlı bağımlılığını destekleyen kitaplıkların yine de Skyframe için doğru şekilde ayarlanması gerekir.
Bu, kural yazarlarının en baştan tam dil çalışma zamanına maruz kalmasını önlemenizi önemle önerir. Bu tür API'lerin yanlışlıkla kullanılma riski son derece yüksektir. Geçmişte bazı kurallar Bazel ekibi veya diğer alan uzmanları tarafından yazılmış olsa da güvenli olmayan API'ler kullanan kurallardan kaynaklanmaktadır.
İkinci dereceden zaman ve bellek tüketiminden kaçınmak zordur
İşleri daha da kötüleştirmek için Skyframe'in uyguladığı koşullar, Java kullanmanın geçmiş kısıtlamaları ve kuralların API'sinin eskiliği, yanlışlıkla kitaplık ve ikili program kurallarına dayalı olarak tüm derleme sistemlerinde temel bir sorun teşkil ediyor. İkinci dereceden bellek tüketimini (ve bu nedenle ikinci dereceden zaman tüketimini) başlatan iki çok yaygın kalıp vardır.
Kitaplık Kuralları Zinciri - A kitaplık kuralları zincirinin B'ye, C'ye bağlı vb. olduğunu düşünün. Ardından, Java çalışma zamanı sınıf yolu veya her kitaplık için C++ bağlayıcı komutu gibi bazı kuralların geçici olarak kapatılmasıyla ilgili bazı özellikleri hesaplamak istiyoruz. Doğal olarak, standart bir liste uygulaması uygulayabiliriz. Ancak bu, ikinci dereceden bellek tüketimini de beraberinde getiriyor: İlk kitaplıkta sınıf yolunda bir giriş, ikincisi, üçüncü üçü vb. toplam 1+2+3+...+N = O(N^2) girişi içerir.
Aynı Kitaplık Kurallarına Dayalı İkili Kurallar - Aynı kitaplık kurallarına uyan bir dizi test kuralınız olması gibi, aynı kitaplık kurallarına dayalı bir ikili program grubu olması gerekir. N kural içinden, kuralların yarısının ikili kurallar, diğer yarısının ise kitaplık kuralları olduğunu varsayalım. Şimdi her ikili programın Java çalışma zamanı sınıf yolu veya C++ bağlayıcı komut satırı gibi kitaplık kurallarının geçici olarak kapatılmasıyla hesaplanan bazı özelliklerin kopyasını oluşturacağını düşünün. Örneğin, C++ bağlantı işleminin komut satırı dizesi temsilini genişletebilir. N/2 öğelerinin N/2 kopyası O(N^2) bellektir.
İkinci dereceden karmaşıklığı önlemek için özel koleksiyon sınıfları
Bazel bu senaryolardan ikisini de büyük ölçüde etkilediğinden, her adımda kopyadan kaçınarak bellekteki bilgileri etkili bir şekilde sıkıştıran bir dizi özel koleksiyon sınıfını kullanıma sunduk. Bu veri yapılarının neredeyse tamamı semantik olarak belirlendi. Biz de bu yapıyı deptik olarak adlandırdık (dahili uygulamada NestedSet
olarak da bilinir). Son birkaç yılda Bazel'ın bellek tüketimini azaltmak için yapılan değişikliklerin çoğu, daha önce kullanılanlar yerine hamleleri kullanmaya yönelik değişikliklerdi.
Ne yazık ki depset kullanımı tüm sorunları otomatik olarak çözmez. Özellikle, her kuralda teker teker değişiklik yapmak bile ikinci dereceden zaman tüketimine neden olur. Dahili olarak NestedSets'in, normal koleksiyon sınıflarıyla birlikte çalışabilirliği kolaylaştırmak için de bazı yardımcı yöntemleri vardır. Maalesef bir NestedSet'in yanlışlıkla bu yöntemlerden birine geçirilmesi, davranışın kopyalanmasına neden olur ve ikinci dereceden bellek tüketimini yeniden beraberinde getirir.