Yazma Kurallarının Zorlukları

Sorun bildirin Kaynağı göster Gece · 7,4 , 7.3 · 7,2 · 7,1 · 7,0 · 6,5

Bu sayfa, belirli sorunlar ve zorluklarla ilgili üst düzey bir genel bakış sunar etkili Bazel kurallarından sorumlu olacaksınız.

Özet Şartlar

  • Varsayım: Doğruluk, İşleme Hızı, Kullanım Kolaylığı ve Gecikme
  • Varsayım: Büyük Ölçekli Depolar
  • Varsayım: BUILD benzeri açıklama dili
  • Geçmiş: Yükleme, Analiz ve Yürütme Arasındaki Kesin Ayrım Güncel ancak API'yi etkilemeye devam ediyor
  • Doğal: Uzaktan yürütme ve önbelleğe alma zordur
  • Doğal: Doğru ve Hızlı Artımlı Derlemeler İçin Değişiklik Bilgilerini Kullanmak, Olağandışı Kodlama Kalıpları Gerektirir
  • Doğal: Dörtlü Zaman ve Bellek Tüketiminin Önlenmesi Zordur

Varsayımlar

Derleme sistemi hakkında yapılan bazı varsayımlar, doğruluk, kullanım kolaylığı, işleme hızı ve büyük ölçekli depolar. İlgili içeriği oluşturmak için kullanılan aşağıdaki bölümlerde bu varsayımlar ele alınmakta ve projenin kalite standartlarını etkili bir şekilde yazılmasını sağlar.

Doğruluk, işleme hızı, kullanım kolaylığı ve gecikme

Derleme sisteminin öncelikle artımlı derlemeler açısından doğru olması gerektiğini varsayıyoruz. Belirli bir kaynak ağacın çıktısı çıkış ağacının görünümünden bağımsız olarak aynı derleme her zaman aynı olmalıdır. beğeniyor. İlk yaklaşımda bu, Bazel'in belirli bir derleme adımına giren her girişi bilmesi gerektiği anlamına gelir. Böylece, girişlerden herhangi biri değişirse bu adımı yeniden çalıştırabilir. Sızdırdığı için Bazel'in doğru şekilde üretilmesiyle ilgili sınırlamalar var tarihi / saati gibi bazı bilgileri verir ve belirli türdeki dosya özelliklerinde yapılan değişiklikler gibi değişiklikler olabilir. Korumalı alan, tanımlanmamış giriş dosyalarının okunmasını engelleyerek doğruluğu sağlamaya yardımcı olur. Ayrıca bilinen birkaç doğruluk sorunu vardır, ve bunların çoğu, ikisi de zor olan Fileset veya C++ kurallarıyla ilgilidir neden olabilir. Bu sorunları düzeltmek için uzun vadeli çalışmalar yapmaktayız.

Derleme sisteminin ikinci hedefi yüksek verim elde etmektir. Uzak yürütme hizmeti için mevcut makine tahsisine göre neler yapılabileceğinin sınırlarını sürekli olarak zorluyoruz. Uzaktan yürütme hizmet aşırı yüklendiğinden kimse işlerini tamamlayamaz.

Ardından kullanım kolaylığı gelir. Aynı (veya uzaktan yürütme hizmetinin yerleşkesini görmek için daha kolay kullanılır.

Gecikme, bir derlemenin başlatılmasından istenen sonuca ulaşmaya kadar geçen süreyi belirtir Bu, başarılı veya başarısız bir teste ait test günlüğü ya da hata mesajı olabilir. BUILD dosyasında yazım hatası olduğunu belirten bir ileti görürsünüz.

Bu hedeflerin genellikle çakıştığını unutmayın. Gecikme, uzaktan yürütme hizmetinin veri hızının bir işlevi olduğu kadar, kullanım kolaylığıyla ilgili doğruluk da bir işlevidir.

Büyük ölçekli depolar

Derleme sisteminin büyük depolama alanı boyutunda çalışması gerekir. Büyük boyut, tek bir sabit diske sığmadığı anlamına gelir. Bu nedenle, neredeyse tüm geliştirici makinelerinde tam bir ödeme yapılması mümkün değildir. Orta büyüklükte bir derleme için on binlerce BUILD dosyasının okunup ayrıştırılması ve yüz binlerce glob'un değerlendirilmesi gerekir. Tek bir makinede tüm BUILD dosyalarını okumak teorik olarak mümkün olsa da bunu makul bir süre ve bellek içinde henüz başaramadık. Bu nedenle, BUILD dosyalarının bağımsız olarak yüklenmesi ve ayrıştırılması önemlidir.

BUILD benzeri açıklama dili

Bu bağlamda, en az 24 saat uzunluğunda Kitaplık ve ikili program kurallarındaki BUILD dosyalarına hemen hemen benzer ve birbirlerine bağımlılıklarını konuşacağız. BUILD dosyaları bağımsız olarak okunup ayrıştırılabilir ve mümkün olduğunda kaynak dosyalara bakmaktan bile kaçınıyoruz (yalnızca varlık).

Tarihi

Bazel sürümleri arasında zorluklara neden olan bazı farklar vardır. edinilen bilgiler aşağıdaki bölümlerde özetlenmiştir.

Yükleme, analiz ve yürütme arasındaki kesin ayrım eski olsa da API'yi etkilemeye devam eder

Teknik olarak bir kuralın giriş ve çıkış dosyalarını bilmesi için uzaktan yürütmeye gönderilmeden hemen önce bir işlem yapılması gerekir. Ancak orijinal Bazel kod tabanı, paketleri yükleme konusunda sıkı bir ayrım uyguluyordu, daha sonra Bir yapılandırma kullanarak kuralları analiz etme (esasen komut satırı işaretleri) ve herhangi bir işlem yapması gerekir. Bazel'in çekirdeği artık bunu gerektirmese de bu ayrım, kurallar API'sinin hâlâ bir parçasıdır (ayrıntılar aşağıdadır).

Bu nedenle, rules API için kuralın bildirim temelli bir açıklaması gerekir. arayüzü (neleri olduğu, özellik türleri). API'nin, çıkış dosyalarının gizli adlarını ve özelliklerin gizli değerlerini hesaplamak için yükleme aşamasında özel kodun çalıştırılmasına izin verdiği bazı istisnalar vardır. Örneğin, "foo" adlı bir java_library kuralı, derleme grafiğindeki diğer kurallardan referans verilebilen "libfoo.jar" adlı bir çıkışı dolaylı olarak oluşturur.

Ayrıca, bir kuralın analizi kaynak dosyaları okuyamaz veya bir eylemin çıktısı; bunun yerine kısmi yönlendirilmiş iki taraflı yalnızca kurala göre belirlenen derleme adımlarının ve çıkış dosya adlarının grafiği ve bağımlılıklarını anlamasına yardımcı olur.

Gerçek

Kuralları yazmada zorlayıcı ve etkili olan bazı doğuştan gelen özellikler vardır en yaygın olanlardan bazıları aşağıdaki bölümlerde açıklanmıştır.

Uzaktan yürütme ve önbelleğe alma sabittir

Uzak yürütme ve önbelleğe alma, derlemeyi tek bir makinede çalıştırmaya kıyasla büyük depolardaki derleme sürelerini yaklaşık iki kat artırır. Ancak bu protokolün performans göstermesi gereken ölçek oldukça büyüktür: Google'ın uzaktan yürütme hizmeti, saniye başına çok sayıda isteği işlemek için tasarlanmıştır ve protokol, gereksiz gidiş dönüşlerin yanı sıra hizmet tarafında gereksiz çalışmalardan dikkatlice kaçınır.

Şu anda protokol, derleme sisteminin karar vermeniz gerekir. derleme sistemi, daha sonra bu işlemden sonra izin verir ve planlayıcıdan önbellek isabeti ister. Önbelleğe isabet bulunursa planlayıcı, çıkış dosyalarının özetlerini göndererek yanıt verir. Dosyalar daha sonra özet aracılığıyla ele alınır. Ancak, bu değişiklik Bazel'e yönelik kısıtlamalar tüm giriş dosyalarını önceden bildirmesi gerekir.

Doğru ve hızlı artımlı derlemeler için değişiklik bilgilerini kullanmak alışılmadık kodlama kalıpları gerektirir

Yukarıda, Bazel'in doğru olması için, öncelikle yapılması gerekenleri derleme adımının başarıyla tamamlanıp tamamlanmadığını belirlemek için hâlâ güncel durumda. Paket yükleme ve kural analizi için de aynı durum geçerlidir. Skyframe'ı genel olarak bu işlemleri gerçekleştirecek şekilde tasarladık. Skyframe, bir hedef düğümünü ("bu seçeneklerle //foo derle" gibi) alıp bileşenlerine ayıran ve bu bileşenleri değerlendirip birleştirerek sonucu veren bir grafik kitaplığı ve değerlendirme çerçevesidir. Bu süreç kapsamında Skyframe paketleri okur, kuralları analiz eder ve işlemleri yürütür.

Skyframe, her düğümde hedef düğümden giriş dosyalarına (aynı zamanda Skyframe düğümleridir) kadar belirli bir düğümün kendi çıkışını hesaplamak için tam olarak hangi düğümleri kullandığını izler. Bu grafiğin bellekte açıkça gösterilmesi, derleme sisteminin bir giriş dosyasında yapılan belirli bir değişiklikten (giriş dosyası oluşturma veya silme dahil) tam olarak hangi düğümlerin etkilendiğini belirlemesine olanak tanır. Böylece, çıkış ağacını istenen duruma geri yüklemek için minimum düzeyde çalışma yapılır.

Bunun bir parçası olarak her düğüm bir bağımlılık keşif işlemi gerçekleştirir. Her düğüm bağımlılıkları tanımlayabilir ve daha sonra bu bağımlılıkların içeriklerini kullanarak daha da fazla bağımlılığı tanımlayabilir. Bu, prensipte düğüm başına iş parçacığı modeline iyi bir şekilde eşlenir. Ancak orta büyüklükteki derlemeler yüz binlerce Skyframe düğümü içerir. Bu, mevcut Java teknolojisiyle kolayca mümkün değildir (ve geçmiş nedenlerden dolayı şu anda Java'yı kullanmak zorundayız. Bu nedenle hafif iş parçacıkları ve devam ettirmeler kullanılamaz).

Bazel bunun yerine sabit boyutlu bir iş parçacığı havuzu kullanır. Ancak bu, bir düğüm henüz mevcut olmayan bir bağımlılık belirtirse bu değerlendirmeyi iptal etmemiz ve bağımlılık kullanılabilir olduğunda (muhtemelen başka bir iş parçacığında) yeniden başlatmamız gerekebileceği anlamına gelir. Bu da, düğümlerin bunu aşırı derecede yapmaması gerektiği anlamına gelir. N bağımlılığı seri olarak açıklayan bir düğüm, N kez yeniden başlatılabilir ve bu işlem O(N^2) zaman alır. Bunun yerine, sözleşmenin önceden toplu olarak Bu da bazen kodun yeniden düzenlenmesini, hatta her parçanın bir yeniden başlatma sayısını sınırlandırmak için bir düğümü birden çok düğüme bağlayabilirsiniz.

Bu teknolojinin şu anda kurallar API'sinde kullanılamadığını unutmayın. Bunun yerine kurallar API'si, yükleme, analiz ve yürütme aşamaları gibi eski kavramlar kullanılarak tanımlanmaya devam etmektedir. Ancak temel bir kısıtlama, diğer düğümlere yapılan tüm erişimlerin, ilgili bağımlılıkları izleyebilmesi için çerçeveden geçmesi gerektiğidir. Derleme sisteminin kullandığı dilden bağımsız olarak kuralların yazıldığından emin olun (bunların, uygulamanızın aynı), kural yazarları bu yöntemi atlayan standart kitaplıklar veya kalıplar kullanmamalıdır Gökyüzü çerçevesi. Java için bunun anlamı, java.io.File dosyasının yanı sıra içeren bir kitaplıktan da yararlanır. Bu düşük düzey arayüzlerin bağımlılık eklemeyi destekleyen kitaplıkların Skyframe için doğru şekilde ayarlanması gerekir.

Bu, kural yazarlarının tam bir dil çalışma zamanına maruz kalmasını önlemenizi önemle tavsiye eder. en başta sunun. Bu tür API'lerin yanlışlıkla kullanılma riski çok büyüktür - geçmişteki bazı Bazel hataları, güvenli olmayan API'ler kullanan kurallardan kaynaklanmıştı. ancak kurallar Bazel ekibi veya diğer alan uzmanları tarafından yazılmıştır.

İkinci dereceden zaman ve bellek tüketiminden kaçınmak zordur

Daha da kötüsü, Skyframe'ın zorunlu kıldığı koşullar, Java'yı kullanmanın geçmişteki kısıtlamaları ve kurallar API'sinin güncel olmamasının yanı sıra, yanlışlıkla ikinci dereceden zaman veya bellek tüketimi sunmak, kitaplık ve ikili kurallara dayalı herhangi bir derleme sisteminde temel bir sorundur. Kare şeklinde bellek tüketimi (ve dolayısıyla kare şeklinde zaman tüketimi) sağlayan iki yaygın kalıp vardır.

  1. Kitaplık Kural Zincirleri - A'nın B'ye, C'ye bağlı ve bu şekilde devam eder. Ardından, alanın geçişli kapanması üzerinden bazı özellikleri veya JavaScript'in çalıştırıldığı C++ linker komutu gibi kullanabilirsiniz. Naif olarak, standart bir liste uygulaması alabiliriz; ancak, Bu da ikinci dereceden bellek tüketimini zaten beraberinde getiriyor: ilk kitaplık. ve bu girişlerin her biri sınıf yolunda bir giriş, ikinci iki, üçüncü üçünü ve + N = O(N^2) girişlerinin toplamıdır.

  2. Aynı Kitaplık Kurallarına Bağlı İkili Kurallar: Aynı kitaplık kurallarına bağlı bir ikili program grubunun olduğu durumu düşünün (ör. aynı kitaplık kodunu test eden birkaç test kuralınız varsa). N kuraldan kuralların yarısının ikili kurallar olduğunu ve kitaplığının diğer yarısına da uyduğundan emin olun. Şimdi her ikili programın, kitaplık kurallarının geçişli olarak kapatılması üzerinden hesaplanan bazı özellikler; örneğin, veya C++ bağlayıcı komut satırını kullanarak dosyaları geçici olarak düzenleyebilirsiniz. Örneğin, C++ bağlantı işleminin komut satırı dizesi gösterimini genişletebilir. N/2 öğenin N/2 kopyası O(N^2) bellektir.

İkinci dereceden karmaşıklığı önlemek için özel koleksiyon sınıfları

Bazel, bu iki senaryodan çok etkileniyordu. O yüzden, ekibimle birlikte bellekteki bilgileri etkili bir şekilde sıkıştıran özel koleksiyon sınıfları her adımda kopyalamayın. Bu veri yapılarının neredeyse tamamında küme semantiği olduğundan bu yapıya depset (dahili uygulamada NestedSet olarak da bilinir) adını verdik. Bazel'in bellek tüketimini azaltmak için geçtiğimiz birkaç yıl içinde yapılan değişikliklerin çoğu, daha önce kullanılanlar yerine depsetlerin kullanılmasıyla ilgiliydi.

Ne yazık ki, Depsets kullanımı tüm sorunları otomatik olarak çözmüyor; Özellikle, her kuralın yeniden ekleme işleminde bir düşüş üzerinde yineleme bile ikinci dereceden zaman tüketimi. NestedSets'in kendi içinde bazı yardımcı yöntemleri de vardır. Normal koleksiyon sınıflarıyla birlikte çalışabilirliği kolaylaştırmak; maalesef, NestedSet'i bu yöntemlerden birine yanlışlıkla aktarmak, ve ikinci dereceden bellek tüketimini tekrar devreye sokar.