Halaman ini membahas cara menggunakan pekerja persisten, manfaat, persyaratan, dan cara pekerja memengaruhi sandbox.
Pekerja persisten adalah proses yang berjalan lama yang dimulai oleh server Bazel, yang
berfungsi sebagai wrapper di sekitar alat yang sebenarnya (biasanya compiler), atau merupakan
alat itu sendiri. Untuk mendapatkan manfaat dari pekerja persisten, alat harus mendukung urutan kompilasi, dan wrapper harus menerjemahkan antara API alat dan format permintaan/respons yang dijelaskan di bawah. Worker yang sama dapat dipanggil dengan dan tanpa tanda --persistent_worker
di build yang sama, dan bertanggung jawab untuk memulai dan berinteraksi dengan alat secara tepat, serta mematikan worker saat keluar. Setiap instance pekerja ditetapkan
(tetapi tidak di-chroot ke) direktori kerja terpisah di bawah
<outputBase>/bazel-workers
.
Penggunaan pekerja persisten adalah strategi eksekusi yang mengurangi overhead saat memulai, memungkinkan lebih banyak kompilasi JIT, dan memungkinkan penyiapan cache untuk, misalnya, pohon sintaksis abstrak dalam eksekusi tindakan. Strategi ini mencapai peningkatan ini dengan mengirimkan beberapa permintaan ke proses yang berjalan lama.
Pekerja persisten diimplementasikan untuk beberapa 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 pekerja persisten secara default saat menjalankan build, meskipun eksekusi jarak jauh lebih diutamakan. Untuk tindakan yang tidak mendukung pekerja persisten,
Bazel akan kembali ke memulai instance alat untuk setiap tindakan. Anda dapat secara eksplisit
menetapkan build untuk menggunakan pekerja persisten dengan menetapkan worker
strategi untuk mnemonik alat yang berlaku. Sebagai praktik terbaik, contoh ini mencakup penentuan local
sebagai
fallback untuk strategi worker
:
bazel build //my:target --strategy=Javac=worker,local
Menggunakan strategi pekerja, bukan strategi lokal, dapat meningkatkan kecepatan kompilasi secara signifikan, bergantung pada penerapan. Untuk Java, build dapat 2–4 kali lebih cepat, terkadang lebih untuk kompilasi inkremental. Mengompilasi Bazel sekitar 2,5 kali lebih cepat dengan pekerja. Untuk mengetahui detail selengkapnya, lihat bagian "Memilih jumlah pekerja".
Jika Anda juga memiliki lingkungan build jarak jauh yang cocok dengan lingkungan build lokal, Anda dapat menggunakan strategi dinamis eksperimental, yang menjalankan eksekusi jarak jauh dan eksekusi pekerja secara bersamaan. Untuk mengaktifkan strategi dinamis, teruskan tanda --experimental_spawn_scheduler. Strategi ini otomatis mengaktifkan pekerja, sehingga Anda tidak perlu menentukan strategi worker
, tetapi Anda tetap dapat menggunakan local
atau sandboxed
sebagai pengganti.
Memilih jumlah pekerja
Jumlah instance pekerja default per mnemonik adalah 4, tetapi dapat disesuaikan dengan tanda
worker_max_instances
. Ada kompromi antara penggunaan CPU yang tersedia secara efektif dan jumlah kompilasi JIT serta hit cache yang Anda dapatkan. Dengan lebih banyak pekerja, lebih banyak target akan membayar biaya startup untuk menjalankan kode yang tidak di-JIT dan mencapai cache dingin. Jika Anda memiliki sejumlah kecil target yang akan dibangun, 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
mnemonic dan set tanda (lihat di bawah), jadi dalam sistem campuran, Anda dapat menggunakan
cukup banyak memori jika mempertahankan nilai default. Untuk build inkremental, manfaat beberapa instance pekerja bahkan lebih kecil.
Grafik ini menunjukkan waktu kompilasi dari awal untuk Bazel (target
//src:bazel
) di workstation Linux Intel Xeon 3,5 GHz dengan 6 core hyper-threaded
dengan RAM 64 GB. Untuk setiap konfigurasi pekerja, lima build bersih dijalankan dan
rata-rata dari empat build terakhir diambil.
Gambar 1. Grafik peningkatan performa clean build.
Untuk konfigurasi ini, dua pekerja memberikan kompilasi tercepat, meskipun hanya ada peningkatan 14% dibandingkan dengan satu pekerja. Satu pekerja adalah opsi yang baik jika Anda ingin menggunakan lebih sedikit memori.
Kompilasi inkremental biasanya memberikan manfaat yang lebih besar. Build bersih relatif jarang terjadi, tetapi mengubah satu file di antara kompilasi adalah hal yang umum, terutama dalam pengembangan berbasis pengujian. Contoh di atas juga memiliki beberapa tindakan pengemasan non-Java yang dapat menutupi 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 peningkatan kecepatan 3x (rata-rata 20 build inkremental dengan satu build pemanasan
dihapus):
Gambar 2. Grafik peningkatan performa build inkremental.
Percepatan bergantung pada perubahan yang dilakukan. Percepatan sebesar faktor 6 diukur dalam situasi di atas saat konstanta yang umum digunakan diubah.
Mengubah pekerja persisten
Anda dapat meneruskan tanda
--worker_extra_flag
untuk menentukan tanda mulai bagi pekerja, yang dikelompokkan berdasarkan mnemonik. Misalnya,
meneruskan --worker_extra_flag=javac=--debug
hanya akan mengaktifkan proses debug untuk Javac.
Hanya satu tanda worker yang dapat ditetapkan per penggunaan tanda ini, dan hanya untuk satu mnemonik.
Worker tidak hanya dibuat secara terpisah untuk setiap mnemonik, tetapi juga untuk variasi pada tanda mulai mereka. Setiap kombinasi mnemonik dan flag mulai
digabungkan menjadi WorkerKey
, dan untuk setiap WorkerKey
, hingga
worker_max_instances
pekerja dapat dibuat. Lihat bagian berikutnya untuk mengetahui cara konfigurasi tindakan juga dapat menentukan tanda penyiapan.
Anda dapat menggunakan
flag --high_priority_workers
untuk menentukan mnemonik yang harus dijalankan daripada mnemonik
prioritas normal. Hal ini dapat membantu memprioritaskan tindakan yang selalu berada di jalur
kritis. Jika ada dua atau lebih pekerja berprioritas tinggi yang menjalankan permintaan, semua pekerja lainnya tidak dapat berjalan. Flag ini dapat digunakan beberapa kali.
Penerusan tanda
--worker_sandboxing
membuat setiap permintaan pekerja menggunakan direktori sandbox terpisah untuk semua
inputnya. Menyiapkan sandbox membutuhkan waktu tambahan, terutama di macOS, tetapi memberikan jaminan kebenaran yang lebih baik.
Flag
--worker_quit_after_build
terutama berguna untuk proses debug dan pembuatan profil. Flag ini memaksa semua pekerja
untuk keluar setelah build selesai. Anda juga dapat meneruskan
--worker_verbose
untuk
mendapatkan lebih banyak output tentang aktivitas pekerja. Flag ini tercermin di kolom
verbosity
di WorkRequest
, sehingga memungkinkan penerapan pekerja juga lebih
verbose.
Pekerja menyimpan log mereka di direktori <outputBase>/bazel-workers
, misalnya
/tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log
.
Nama file mencakup ID pekerja dan mnemonik. Karena bisa ada lebih dari satu WorkerKey
per mnemonik, Anda mungkin melihat lebih dari worker_max_instances
file log untuk mnemonik tertentu.
Untuk build Android, lihat detail di halaman Performa Build Android.
Menerapkan pekerja persisten
Lihat halaman membuat pekerja persisten untuk mengetahui informasi selengkapnya tentang cara membuat 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 menjalankan
command line /bin/some_compiler -max_mem=4G --persistent_worker
. Permintaan
untuk mengompilasi Foo.java
akan terlihat seperti:
CATATAN: Meskipun spesifikasi buffer protokol menggunakan "snake case" (request_id
),
protokol JSON menggunakan "camel case" (requestId
). Dalam dokumen ini, kita akan menggunakan
camel case dalam contoh JSON, tetapi snake case saat membahas kolom
terlepas dari protokolnya.
{
"arguments": [ "-g", "-source", "1.5", "Foo.java" ]
"inputs": [
{ "path": "symlinkfarm/input1", "digest": "d49a..." },
{ "path": "symlinkfarm/input2", "digest": "093d..." },
],
}
Pekerja menerima ini di stdin
dalam format JSON yang dibatasi baris baru (karena
requires-worker-protocol
disetel ke JSON). Kemudian, pekerja akan melakukan tindakan,
dan mengirimkan WorkResponse
berformat JSON ke Bazel di stdout-nya. Kemudian, Bazel
mengurai respons ini dan mengonversinya secara manual ke proto WorkResponse
. Untuk berkomunikasi dengan pekerja terkait menggunakan protobuf berenkode biner, bukan JSON, requires-worker-protocol
akan disetel 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 menggunakan protobuf untuk komunikasi pekerja secara default.
Bazel mendapatkan WorkerKey
dari mnemonik dan flag bersama, jadi jika konfigurasi ini memungkinkan perubahan parameter max_mem
, worker terpisah akan dibuat untuk setiap nilai yang digunakan. Hal ini dapat menyebabkan konsumsi memori yang berlebihan jika terlalu banyak variasi yang digunakan.
Saat ini, setiap pekerja hanya dapat memproses satu permintaan dalam satu waktu. Fitur multiplex worker eksperimental memungkinkan penggunaan beberapa thread, jika alat yang mendasarinya bersifat multithread dan wrapper disiapkan untuk memahami hal ini.
Di repositori GitHub ini, Anda dapat melihat contoh wrapper pekerja yang ditulis dalam Java dan Python. Jika Anda bekerja di JavaScript atau TypeScript, paket@bazel/worker dan contoh pekerja nodejs mungkin berguna.
Bagaimana pengaruh pekerja terhadap sandbox?
Menggunakan strategi worker
secara default tidak menjalankan tindakan di
sandbox, mirip dengan strategi local
. Anda dapat menyetel tanda
--worker_sandboxing
untuk menjalankan semua pekerja di dalam sandbox, sehingga memastikan setiap
eksekusi alat hanya melihat file input yang seharusnya dimilikinya. Alat ini
mungkin masih membocorkan informasi di antara permintaan secara internal, misalnya melalui
cache. Menggunakan strategi dynamic
memerlukan worker untuk di-sandbox.
Untuk memungkinkan penggunaan cache compiler yang benar dengan pekerja, ringkasan diteruskan bersama dengan setiap file input. Dengan demikian, compiler atau wrapper dapat memeriksa apakah input masih valid tanpa harus membaca file.
Meskipun menggunakan ringkasan input untuk mencegah caching yang tidak diinginkan, pekerja sandbox menawarkan sandboxing yang tidak terlalu ketat dibandingkan sandbox murni, karena alat dapat menyimpan status internal lain yang telah terpengaruh oleh permintaan sebelumnya.
Pekerja multiplex hanya dapat di-sandbox jika implementasi pekerja mendukungnya, dan sandbox ini harus diaktifkan secara terpisah dengan tanda --experimental_worker_multiplex_sandboxing
. Lihat detail selengkapnya di
dokumen desain).
Bacaan lebih lanjut
Untuk mengetahui informasi selengkapnya tentang pekerja persisten, lihat: