Kararlı Çalışanlar

Sorun bildirin Kaynağı göster

Bu sayfada kalıcı çalışanların nasıl kullanılacağı, avantajları, gereksinimleri ve çalışanların korumalı alanı nasıl etkilediği açıklanmaktadır.

Kalıcı çalışan, Bazel sunucusu tarafından başlatılan uzun süreli bir işlemdir. Bu süreç, gerçek aracın (genellikle bir derleyici) etrafında sarmalayıcı olarak görev yapar veya söz konusu aracın kendisidir. Aracın kalıcı çalışanlardan yararlanabilmesi için bir dizi derleme işlemini desteklemesi ve sarmalayıcının, aracın API'si ile aşağıda açıklanan istek/yanıt biçimi arasında çeviri yapması gerekir. Aynı çalışan, aynı derlemede --persistent_worker işaretiyle ve olmadan çağrılabilir. Bu çalışan, aracı uygun bir şekilde başlatmak ve araçla konuşmaktan ve çıkışta çalışanları kapatmaktan sorumludur. Her çalışan örneğine <outputBase>/bazel-workers altında ayrı bir çalışma dizini atanır (ancak bu dizin başlatılmaz).

Kalıcı çalışanları kullanmak, başlatma ek yükünü azaltan, daha fazla JIT derlemesine olanak tanıyan ve işlem yürütmede örnek için soyut söz dizimi ağaçlarının önbelleğe alınmasını sağlayan bir yürütme stratejisidir. Bu strateji, uzun süreli bir sürece birden fazla istek göndererek bu iyileştirmeleri gerçekleştirir.

Kalıcı çalışanlar Java, Scala, Kotlin ve daha birçok dilde uygulanır.

NodeJS çalışma zamanı kullanan programlar, çalışan protokolünü uygulamak için @bazel/worker yardımcı kitaplığını kullanabilir.

Kalıcı çalışanları kullanma

Bazel 0.27 ve sonraki sürümler, derlemeleri yürütürken varsayılan olarak kalıcı çalışanları kullanır. Bununla birlikte, uzaktan yürütme önceliklidir. Kalıcı çalışanları desteklemeyen işlemlerde, Bazel her işlem için bir araç örneği başlatır. Geçerli araç anımsatıcıları için worker stratejisini ayarlayarak derlemenizi kalıcı çalışanları kullanacak şekilde açıkça ayarlayabilirsiniz. En iyi uygulama olarak bu örnekte, worker stratejisinin yedeği olarak local belirtilmesi dahildir:

bazel build //my:target --strategy=Javac=worker,local

Yerel strateji yerine çalışan stratejisi kullanmak, uygulamaya bağlı olarak derleme hızını önemli ölçüde artırabilir. Java için derlemeler 2-4 kat daha hızlı olabilir, bazen de artımlı derleme için daha hızlı olabilir. Çalışanlara kıyasla Bazel'i derlemek yaklaşık 2,5 kat daha hızlıdır. Daha fazla bilgi için "Çalışan sayısını seçme" bölümüne bakın.

Yerel derleme ortamınızla eşleşen bir uzaktan derleme ortamınız varsa uzaktan yürütme ve çalışan yürütme yöntemini kullanan deneysel dinamik stratejisini kullanabilirsiniz. Dinamik stratejiyi etkinleştirmek için --experimental_spawn_scheduler işaretini geçin. Bu strateji, çalışanları otomatik olarak etkinleştirir. Bu nedenle, worker stratejisini belirtmenize gerek yoktur ancak local veya sandboxed stratejisini yedek olarak kullanmaya devam edebilirsiniz.

Çalışan sayısını seçme

Anımsatıcı başına varsayılan çalışan örneği sayısı 4'tür ancak worker_max_instances işaretiyle ayarlanabilir. Mevcut CPU'lardan iyi bir şekilde yararlanmak ve JIT derlemesi ile önbellek isabetlerinin miktarı arasında bir denge vardır. Daha fazla çalışanla, daha fazla hedef, JIT'e tabi olmayan kod çalıştırma ve soğuk önbelleğe almanın başlangıç maliyetlerini karşılar. Oluşturulacak az sayıda hedefiniz varsa derleme hızı ile kaynak kullanımı arasındaki en iyi dengeyi tek bir çalışan verebilir (örneğin, 8586 numaralı soruna bakın). worker_max_instances işareti, anımsatıcı ve işaret grubu başına maksimum çalışan örneği sayısını belirler (aşağıya bakın). Bu nedenle, karma bir sistemde varsayılan değeri korursanız oldukça fazla miktarda bellek kullanabilirsiniz. Artımlı derlemelerde birden fazla çalışan örneğinin avantajı daha da azdır.

Bu grafikte, 64 GB RAM'e sahip 6 çekirdekli hiper iş parçacıklı Intel Xeon 3,5 GHz Linux iş istasyonunda Bazel'in (hedef //src:bazel) sıfırdan derleme süreleri gösterilmektedir. Her çalışan yapılandırması için beş tane temiz derleme çalıştırılır ve son dördünün ortalaması alınır.

Temiz derlemelerin performans iyileştirmeleri grafiği

Şekil 1. Temiz derlemelerin performans iyileştirmeleri grafiği.

Bu yapılandırmada, iki çalışan en hızlı derlemeyi sağlar. Bir çalışana kıyasla yalnızca %14 iyileşme görülür. Daha az bellek kullanmak istiyorsanız bir çalışan iyi bir seçenektir.

Artımlı derleme genellikle daha da fazla fayda sağlar. Temiz derlemeler nispeten nadirdir ancak özellikle test odaklı geliştirmede, derlemeler arasında tek bir dosyanın değiştirilmesi yaygın karşılaşılan bir durumdur. Yukarıdaki örnekte artımlı derleme süresini gölgede bırakabilecek bazı Java dışı paketleme işlemleri de vardır.

AbstractContainerizingSandboxedSpawn.java içinde bir dahili dize sabit değeri değiştirildikten sonra yalnızca Java kaynaklarını (//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar) yeniden derlediğinizde, 3 kat hızlandırılmış (bir ısınma derlemesi silinmiş ortalama 20 artımlı derleme) elde edilir:

Artımlı derlemelerin performans iyileştirmeleri grafiği

Şekil 2. Artımlı derlemelerin performans iyileştirmelerinin grafiği.

Bu hız, yapılan değişikliğe bağlıdır. Yaygın olarak kullanılan bir sabit değer değiştirildiğinde, yukarıdaki durumda faktör 6'nın hızı ölçülür.

Kalıcı çalışanları değiştirme

--worker_extra_flag işaretini, çalışanlara başlatma bayraklarını belirtmek için mnemonic ile anahtarlanmıştır. Örneğin, --worker_extra_flag=javac=--debug aktarıldığında yalnızca Javac için hata ayıklama etkinleştirilir. Bu işaretin kullanımı başına ve yalnızca bir anımsatıcı için yalnızca bir çalışan işareti ayarlanabilir. Çalışanlar her anımsatıcı için ayrı ayrı değil, aynı zamanda başlangıç bayraklarındaki varyasyonlar için de oluşturulur. Her bir anımsatıcı ve başlangıç işareti kombinasyonu bir WorkerKey olarak birleştirilir ve her WorkerKey için en fazla worker_max_instances çalışan oluşturulabilir. İşlem yapılandırmasının kurulum işaretlerini nasıl belirtebileceğini öğrenmek için sonraki bölüme bakın.

Normal öncelikli anımsamalara göre çalışması gereken bir hatırlatıcı belirtmek için --high_priority_workers işaretini kullanabilirsiniz. Bu sayede her zaman kritik yolda olan eylemlere öncelik verebilirsiniz. İstekleri yürüten iki veya daha fazla yüksek öncelikli çalışan varsa diğer tüm çalışanların çalışması engellenir. Bu işaret birden fazla kez kullanılabilir.

--worker_sandboxing işaretini geçmek, her çalışan isteğinin tüm girişleri için ayrı bir korumalı alan dizini kullanmasına neden olur. sandbox oluşturulması özellikle macOS'te biraz daha zaman alır ancak daha iyi doğruluk garantisi verir.

--worker_quit_after_build işareti, temel olarak hata ayıklama ve profil oluşturma işlemleri için kullanışlıdır. Bu işaret, derleme tamamlandığında tüm çalışanları işi bırakmaya zorlar. Çalışanların neler yaptığı hakkında daha fazla çıktı almak için --worker_verbose aracını da iletebilirsiniz. Bu işaret, WorkRequest dilindeki verbosity alanına yansıtılarak çalışan uygulamalarının daha ayrıntılı olmasına da olanak tanır.

Çalışanlar, günlüklerini <outputBase>/bazel-workers dizininde depolar (ör. /tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log). Dosya adı, çalışan kimliğini ve anımsatıcıyı içerir. Anımsatıcı başına birden fazla WorkerKey olabileceğinden belirli bir anımsatıcı için worker_max_instances'dan fazla günlük dosyası görebilirsiniz.

Android derlemeleriyle ilgili ayrıntıları Android Derleme Performansı sayfasında bulabilirsiniz.

Kalıcı çalışanları hayata geçirme

Çalışan oluşturma hakkında daha fazla bilgi için Kalıcı çalışanlar oluşturma sayfasına bakın.

Bu örnekte, JSON kullanan bir çalışan için Starlark yapılandırması gösterilmektedir:

args_file = ctx.actions.declare_file(ctx.label.name + "_args_file")
ctx.actions.write(
    output = args_file,
    content = "\n".join(["-g", "-source", "1.5"] + ctx.files.srcs),
)
ctx.actions.run(
    mnemonic = "SomeCompiler",
    executable = "bin/some_compiler_wrapper",
    inputs = inputs,
    outputs = outputs,
    arguments = [ "-max_mem=4G",  "@%s" % args_file.path],
    execution_requirements = {
        "supports-workers" : "1", "requires-worker-protocol" : "json" }
)

Bu tanıma göre, bu işlemin ilk kullanımı /bin/some_compiler -max_mem=4G --persistent_worker komut satırını çalıştırmakla başlar. Bu durumda, Foo.java derlemesi için gönderilen istek aşağıdaki gibi görünür:

NOT: Protokol arabelleği spesifikasyonu "yılan büyük/küçük harf" (request_id) kullanırken, JSON protokolünde "deve büyük/küçük harf" (requestId) kullanılır. Bu dokümanda, JSON örneklerinde deve büyük/küçük harf kullanımı kullanılır. Protokolden bağımsız olarak alan hakkında konuşurken yılan şeklinde büyük harf kullanılır.

{
  "arguments": [ "-g", "-source", "1.5", "Foo.java" ]
  "inputs": [
    { "path": "symlinkfarm/input1", "digest": "d49a..." },
    { "path": "symlinkfarm/input2", "digest": "093d..." },
  ],
}

Çalışan, bunu stdin üzerinde yeni satırlarla ayrılmış JSON biçiminde (requires-worker-protocol, JSON olarak ayarlandığından) alır. Daha sonra çalışan, işlemi gerçekleştirir ve stdout'unda Bazel'e JSON biçimli bir WorkResponse gönderir. Daha sonra Bazel bu yanıtı ayrıştırır ve manuel olarak WorkResponse protosuna dönüştürür. İlişkili çalışanla JSON yerine ikili kodlu protobuf kullanarak iletişim kurmak için requires-worker-protocol, proto olarak şu şekilde ayarlanır:

  execution_requirements = {
    "supports-workers" : "1" ,
    "requires-worker-protocol" : "proto"
  }

Yürütme şartlarına requires-worker-protocol öğesini dahil etmezseniz Bazel varsayılan olarak çalışan iletişimini protobuf kullanacak şekilde ayarlar.

Bazel, WorkerKey öğesini anımsatıcılardan ve paylaşılan işaretlerden türetir. Dolayısıyla bu yapılandırma, max_mem parametresinin değiştirilmesine izin veriyorsa kullanılan her değer için ayrı bir çalışan oluşturulur. Çok fazla varyasyon kullanılırsa bu durum, aşırı bellek tüketimine neden olabilir.

Her çalışan şu anda bir seferde yalnızca bir isteği işleyebilir. Temel araç çok iş parçacıklıysa ve sarmalayıcı bunu anlayacak şekilde ayarlanmışsa deneysel Multiplex çalışanları özelliği, birden çok iş parçacığının kullanılmasına olanak tanır.

Bu GitHub deposunda, Java ve Python'da yazılan örnek çalışan sarmalayıcılarını görebilirsiniz. JavaScript veya TypeScript üzerinde çalışıyorsanız @bazel/worker paketi ve nodejs çalışan örneği faydalı olabilir.

Çalışanlar korumalı alanı nasıl etkiler?

Varsayılan olarak worker stratejisi kullanıldığında işlem, local stratejisine benzer şekilde bir sandbox çalıştırılmaz. --worker_sandboxing işaretini korumalı alan içindeki tüm çalışanları çalıştıracak şekilde ayarlayabilirsiniz. Böylece, aracın her yürütmesinin yalnızca olması gereken giriş dosyalarını gördüğünden emin olursunuz. Araç, istekler arasında dahili olarak (örneğin, bir önbellek aracılığıyla) bilgi sızdırabilir. dynamic stratejisinin kullanılması çalışanların korumalı alana alınmasını gerektirir.

Derleyici önbelleklerinin çalışanlarla doğru şekilde kullanılmasını sağlamak için her giriş dosyasıyla birlikte bir özet iletilir. Böylece derleyici veya sarmalayıcı, dosyayı okumak zorunda kalmadan girişin hâlâ geçerli olup olmadığını kontrol edebilir.

İstenmeyen önbelleğe almayı korumak için giriş özetleri kullanılırken bile, korumalı alana alınan çalışanlar yalnızca korumalı alana göre daha az katı korumalı alan sunar. Bunun nedeni, araç önceki isteklerden etkilenen diğer dahili durumları koruyabilmesidir.

Multiplex çalışanları, yalnızca çalışan uygulaması destekliyorsa korumalı alana alınabilir. Bu korumalı alan, --experimental_worker_multiplex_sandboxing işaretiyle ayrı olarak etkinleştirilmelidir. Daha fazla ayrıntıyı tasarım belgesinde bulabilirsiniz.)

Daha fazla bilgi

Kalıcı çalışanlar hakkında daha fazla bilgi için bkz: