Halaman ini membahas cara menggunakan pekerja persisten, manfaat, persyaratan, dan bagaimana pekerja memengaruhi sandboxing.
Pekerja persisten adalah proses yang berjalan lama
yang dimulai oleh server Bazel, yang
berfungsi sebagai wrapper untuk alat aktual (biasanya compiler), atau
alat itu sendiri. Untuk mendapatkan manfaat dari pekerja yang gigih, alat ini harus
menjalankan urutan kompilasi, dan wrapper perlu menerjemahkan
antara API alat dan format permintaan/respons yang dijelaskan di bawah. Hal yang sama
pekerja dapat dipanggil dengan dan tanpa flag --persistent_worker
dalam
yang sama, dan bertanggung jawab untuk
memulai dan berkomunikasi dengan
serta mematikan pekerja saat keluar. Setiap instance pekerja ditetapkan
(tetapi tidak di-chroot ke) direktori kerja terpisah yang berada di
<outputBase>/bazel-workers
.
Menggunakan pekerja persisten adalah strategi eksekusi yang mengurangi {i>start-up<i} tambahan, memungkinkan lebih banyak kompilasi JIT, dan memungkinkan contoh hierarki sintaksis abstrak dalam eksekusi tindakan. Strategi ini akan mencapai peningkatan ini dengan mengirimkan beberapa permintaan ke {i>checkout<i}.
Pekerja persisten diimplementasikan untuk berbagai bahasa, termasuk Java, Scala, Kotlin, dan lainnya.
Program yang menggunakan runtime NodeJS dapat menggunakan library helper @bazel/worker untuk mengimplementasikan protokol pekerja.
Menggunakan pekerja persisten
Bazel 0.27 dan yang lebih tinggi
menggunakan worker persisten secara default saat mengeksekusi build, meskipun jarak jauh
dan eksekusi akan diprioritaskan. Untuk tindakan yang tidak mendukung pekerja persisten,
Bazel kembali memulai instance alat untuk setiap tindakan. Anda dapat secara eksplisit
tetapkan build Anda untuk menggunakan pekerja persisten dengan menyetel worker
strategi untuk alat yang berlaku
mnemonik. Sebagai praktik terbaik, contoh ini mencakup penetapan local
sebagai
kembali ke strategi worker
:
bazel build //my:target --strategy=Javac=worker,local
Menggunakan strategi pekerja, bukan strategi lokal, dapat meningkatkan kompilasi jauh lebih cepat, tergantung pada implementasinya. Untuk Java, build dapat berupa 2–4 kali lebih cepat, terkadang lebih untuk kompilasi inkremental. Kompilasi Bazel adalah sekitar 2,5 kali lebih cepat dengan pekerja. Untuk mengetahui detail selengkapnya, lihat "Memilih jumlah pekerja" bagian.
Jika Anda juga memiliki lingkungan build jarak jauh yang cocok dengan build lokal Anda
lingkungan, Anda dapat menggunakan
strategi dinamis,
yang memacu eksekusi jarak jauh
dan eksekusi pekerja. Untuk mengaktifkan fitur
strategi, meneruskan
--experimental_spawn_scheduler
penanda. Strategi ini secara otomatis memungkinkan pekerja, sehingga mereka tidak perlu
menentukan strategi worker
, tetapi Anda tetap dapat menggunakan local
atau sandboxed
sebagai
penggantian.
Memilih jumlah pekerja
Jumlah default worker instance per mnemonik adalah 4, tetapi dapat disesuaikan
dengan
worker_max_instances
penanda. Ada imbal balik antara pemanfaatan CPU yang tersedia dengan baik dan
kompilasi JIT dan hit cache yang Anda dapatkan. Dengan lebih banyak pekerja, lebih
target akan membayar biaya awal untuk menjalankan kode non-JIT dan menekan cold
di cache oleh pengguna. Jika Anda memiliki sedikit target untuk dibuat, satu pekerja dapat memberikan
kompromi terbaik antara kecepatan kompilasi dan penggunaan resource (misalnya,
lihat masalah #8586.
Tanda worker_max_instances
menetapkan jumlah maksimum instance pekerja per
mnemonik dan penanda (lihat di bawah), jadi dalam sistem campuran, Anda akhirnya dapat menggunakan
cukup banyak memori jika Anda
tetap menggunakan nilai {i>default<i}. Untuk build inkremental,
manfaat dari beberapa instance pekerja bahkan lebih kecil lagi.
Grafik ini menunjukkan waktu kompilasi dari awal untuk Bazel (target
//src:bazel
) di workstation Linux Intel Xeon 3,5 GHz hyper-thread 6 core
dengan RAM 64 GB. Untuk setiap konfigurasi pekerja, lima clean build dijalankan dan
yang diambil rata-rata dari
empat terakhir.
Gambar 1. Grafik peningkatan performa clean build.
Untuk konfigurasi ini, dua pekerja memberikan kompilasi tercepat, meskipun hanya 14% meningkat dibandingkan dengan satu pekerja. Satu pekerja adalah pilihan yang baik jika Anda ingin menggunakan lebih sedikit memori.
Kompilasi inkremental biasanya lebih bermanfaat. Build yang bersih adalah relatif jarang, tetapi mengubah satu file antar kompilasi adalah hal biasa, di khususnya dalam pengembangan berbasis pengujian. Contoh di atas juga memiliki beberapa aplikasi non-Java tindakan pengemasan padanya yang dapat membayangi waktu kompilasi inkremental.
Mengompilasi ulang sumber Java saja
(//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar
)
setelah mengubah konstanta string internal di
AbstractContainerizingSandboxedSpawn.java
memberikan percepatan 3x lipat (rata-rata 20 build inkremental dengan satu build pemanasan
dihapus):
Gambar 2. Grafik peningkatan performa build inkremental.
Percepatan tergantung pada perubahan yang dilakukan. Percepatan dari faktor 6 adalah dalam situasi di atas ketika konstanta yang umum digunakan diubah.
Memodifikasi pekerja persisten
Anda dapat meneruskan
--worker_extra_flag
untuk menentukan flag start-up bagi pekerja, yang terkunci dengan mnemonik. Contohnya,
meneruskan --worker_extra_flag=javac=--debug
akan mengaktifkan proses debug untuk Javac saja.
Hanya satu tanda pekerja yang dapat disetel per penggunaan tanda ini, dan hanya untuk satu mnemonik.
Pekerja tidak hanya dibuat secara terpisah untuk setiap mnemonik, tetapi juga untuk
variasi di penanda awal. Setiap kombinasi mnemonik dan start-up
flag digabungkan menjadi WorkerKey
, dan untuk setiap WorkerKey
hingga
worker_max_instances
pekerja dapat dibuat. Lihat bagian berikutnya untuk mengetahui
konfigurasi tindakan juga dapat menentukan
tanda penyiapan.
Anda dapat menggunakan
--high_priority_workers
untuk menentukan mnemonik yang harus dijalankan dengan preferensi pada prioritas normal
mnemonik. Hal ini dapat membantu memprioritaskan tindakan
yang selalu dalam tahap penting
. Jika ada dua atau lebih pekerja prioritas tinggi yang
menjalankan permintaan, semua
pekerja lain dicegah
untuk berjalan. Tanda ini dapat digunakan beberapa kali.
Meneruskan
--worker_sandboxing
membuat setiap permintaan pekerja menggunakan direktori {i>sandbox<i} terpisah untuk semua
input. Menyiapkan sandbox memerlukan waktu tambahan,
terutama di macOS, tetapi memberikan jaminan ketepatan yang lebih baik.
Tujuan
--worker_quit_after_build
umumnya berguna untuk proses debug dan pembuatan profil. Penanda ini memaksa
semua pekerja
berhenti setelah build selesai. Anda juga bisa meneruskan
--worker_verbose
ke
mendapatkan lebih banyak {i>output<i} dari apa
yang dilakukan pekerja itu. Penanda ini tercermin dalam
Kolom verbosity
di WorkRequest
, yang memungkinkan implementasi pekerja juga
yang lebih panjang.
Pekerja menyimpan log mereka di direktori <outputBase>/bazel-workers
, sebagai
contoh
/tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log
.
Nama file mencakup ID pekerja dan mnemonik. Karena mungkin ada lebih banyak
dari satu WorkerKey
per mnemonik, Anda mungkin melihat lebih dari worker_max_instances
file log untuk mnemonik tertentu.
Untuk build Android, lihat detailnya di Halaman Android Build Performance.
Mengimplementasikan pekerja persisten
Lihat halaman membuat pekerja persisten untuk informasi selengkapnya informasi tentang cara membuat seorang pekerja.
Contoh ini menunjukkan konfigurasi Starlark untuk pekerja yang menggunakan JSON:
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" }
)
Dengan definisi ini, penggunaan pertama
tindakan ini akan dimulai dengan mengeksekusi
command line /bin/some_compiler -max_mem=4G --persistent_worker
. Permintaan
untuk mengompilasi Foo.java
akan terlihat seperti ini:
CATATAN: Meskipun spesifikasi buffering protokol menggunakan "snake case" (request_id
),
protokol JSON menggunakan "camel case" (requestId
). Dalam dokumen ini, kita akan menggunakan
{i>camel case<i} dalam contoh JSON, tetapi {i>snake case<i} ketika berbicara tentang
terlepas dari protokol.
{
"arguments": [ "-g", "-source", "1.5", "Foo.java" ]
"inputs": [
{ "path": "symlinkfarm/input1", "digest": "d49a..." },
{ "path": "symlinkfarm/input2", "digest": "093d..." },
],
}
Worker akan menerima ini di stdin
dalam format JSON newline-delimited (karena
requires-worker-protocol
disetel ke JSON). Pekerja kemudian melakukan tindakan,
dan mengirimkan WorkResponse
berformat JSON ke Bazel pada stdout-nya. Bazel lalu
menguraikan respons ini dan mengonversinya secara manual menjadi proto WorkResponse
. Kepada
berkomunikasi dengan pekerja terkait menggunakan protobuf yang dienkode biner, bukan
JSON, requires-worker-protocol
akan ditetapkan ke proto
, seperti ini:
execution_requirements = {
"supports-workers" : "1" ,
"requires-worker-protocol" : "proto"
}
Jika Anda tidak menyertakan requires-worker-protocol
dalam persyaratan eksekusi,
Bazel akan menetapkan komunikasi pekerja secara default agar menggunakan protobuf.
Bazel memperoleh WorkerKey
dari mnemonik dan flag yang dibagikan, jadi jika ini
memungkinkan perubahan parameter max_mem
, worker terpisah akan
muncul untuk setiap nilai yang digunakan. Hal ini dapat menyebabkan
penggunaan memori yang berlebihan jika
terlalu banyak variasi yang digunakan.
Setiap pekerja saat ini hanya dapat memproses satu permintaan dalam satu waktu. Versi eksperimental Fitur multiplex worker memungkinkan penggunaan beberapa , jika alat yang mendasarinya adalah multi-thread dan wrapper disiapkan untuk memahami hal ini.
Di beberapa repositori GitHub ini, Anda dapat melihat contoh wrapper pekerja yang ditulis di Java serta di Python. Jika Anda berfungsi dalam JavaScript atau TypeScript, @bazel/paket pekerja dan contoh worker nodejs mungkin dapat membantu.
Bagaimana pengaruh pekerja terhadap sandbox?
Menggunakan strategi worker
secara default tidak menjalankan tindakan dalam
sandbox, mirip dengan strategi local
. Anda dapat menyetel
--worker_sandboxing
untuk menjalankan semua pekerja di dalam sandbox, memastikan setiap pekerja
eksekusi alat ini hanya melihat file
input yang seharusnya dimilikinya. Alat
mungkin masih membocorkan informasi antarpermintaan secara internal, misalnya melalui
di cache oleh pengguna. Menggunakan strategi dynamic
mengharuskan pekerja untuk di-sandbox.
Untuk memungkinkan penggunaan cache compiler yang benar dengan pekerja, ringkasan diteruskan dengan setiap file input. Dengan demikian, compiler atau wrapper dapat memeriksa apakah input tetap valid tanpa harus membaca file.
Bahkan saat menggunakan ringkasan input untuk mencegah caching yang tidak diinginkan, proses sandbox pekerja menawarkan sandboxing yang kurang ketat dibandingkan sandbox murni, karena alat ini mempertahankan status internal lain yang telah dipengaruhi oleh permintaan sebelumnya.
Pekerja multipleks hanya dapat di-sandbox
jika implementasi pekerja mendukungnya,
dan sandboxing ini harus diaktifkan secara terpisah dengan
--experimental_worker_multiplex_sandboxing
. Lihat detail selengkapnya di
dokumen desain).
Bacaan lebih lanjut
Untuk informasi selengkapnya tentang pekerja persisten, lihat:
- Postingan blog pekerja persisten asli
- Deskripsi penerapan yang memiliki {: .external}
- Postingan blog oleh Mike Morearty {: .external}
- Pengembangan Front End dengan Bazel: Angular/TypeScript dan Pekerja Persisten bersama Asana {: .external}
- Penjelasan strategi Bazel {: .external}
- Diskusi strategi pekerja yang informatif di milis bazel-discuss {: .external}