Halaman ini membahas cara menggunakan pekerja persisten, manfaat, persyaratan, dan pengaruh pekerja terhadap sandbox.
Pekerja persisten adalah proses yang berjalan lama yang dimulai oleh server Bazel, yang berfungsi sebagai wrapper di sekitar alat sebenarnya (biasanya compiler), atau merupakan alat itu sendiri. Untuk mendapatkan manfaat dari pekerja persisten, alat ini harus
mendukung dilakukannya urutan kompilasi, dan wrapper harus menerjemahkan
antara API alat dan format permintaan/respons yang dijelaskan di bawah ini. Pekerja
yang sama mungkin dipanggil dengan dan tanpa tanda --persistent_worker
di
build yang sama, dan bertanggung jawab untuk memulai dan berkomunikasi dengan alat
dengan tepat, serta menonaktifkan pekerja saat keluar. Setiap instance pekerja diberi
(tetapi tidak di-chroot) ke direktori kerja terpisah di
<outputBase>/bazel-workers
.
Menggunakan pekerja persisten adalah strategi eksekusi yang mengurangi overhead awal, memungkinkan lebih banyak kompilasi JIT, dan memungkinkan caching, misalnya, hierarki sintaksis abstrak dalam eksekusi tindakan. Strategi ini mencapai peningkatan tersebut dengan mengirimkan beberapa permintaan ke proses yang berjalan lama.
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 pekerja persisten secara default saat menjalankan build, meskipun eksekusi
jarak jauh lebih diutamakan. Untuk tindakan yang tidak mendukung pekerja persisten,
Bazel akan kembali untuk memulai instance alat untuk setiap tindakan. Anda dapat secara eksplisit
menetapkan build untuk menggunakan pekerja persisten dengan menetapkan
strategi worker
untuk mnemonik alat
yang berlaku. Sebagai praktik terbaik, contoh ini mencakup menentukan local
sebagai
penggantian ke strategi worker
:
bazel build //my:target --strategy=Javac=worker,local
Menggunakan strategi pekerja, bukan strategi lokal, dapat meningkatkan kecepatan kompilasi secara signifikan, bergantung pada implementasinya. 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 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. Untuk mengaktifkan strategi dinamis, teruskan flag --experimental_spawn_scheduler. Strategi ini otomatis mengaktifkan pekerja, sehingga Anda tidak perlu
menentukan strategi worker
, tetapi Anda tetap dapat menggunakan local
atau sandboxed
sebagai
penggantian.
Memilih jumlah pekerja
Jumlah default instance worker per mnemonik adalah 4, tetapi dapat disesuaikan
dengan
tanda
worker_max_instances
. Ada untung-rugi antara memanfaatkan CPU yang tersedia dengan
jumlah kompilasi JIT dan cache yang Anda dapatkan. Dengan lebih banyak pekerja, lebih banyak
target akan membayar biaya awal saat menjalankan kode yang tidak di-JIT dan mencapai
cold cache. Jika target yang akan Anda build sedikit, satu pekerja dapat memberikan
kompromi terbaik antara kecepatan kompilasi dan penggunaan resource (misalnya,
lihat masalah #8586.
Flag worker_max_instances
menetapkan jumlah maksimum instance pekerja per
mnemonik dan flag yang ditetapkan (lihat di bawah), sehingga dalam sistem campuran, Anda mungkin akan menggunakan
banyak memori yang cukup jika Anda mempertahankan nilai default. Untuk build inkremental, manfaat beberapa instance worker bahkan lebih kecil.
Grafik ini menunjukkan waktu kompilasi dari awal untuk Bazel (target
//src:bazel
) pada workstation Linux 6-core Intel Xeon 3,5 GHz
dengan RAM 64 GB. Untuk setiap konfigurasi worker, lima clean build dijalankan dan
rata-rata dari empat build terakhir akan diambil.
Gambar 1. Grafik peningkatan performa dari clean build.
Untuk konfigurasi ini, dua pekerja memberikan kompilasi tercepat, meskipun dengan peningkatan hanya 14% dibandingkan dengan satu pekerja. Satu pekerja adalah opsi yang baik jika Anda ingin menggunakan lebih sedikit memori.
Kompilasi inkremental biasanya lebih menguntungkan. Build bersih relatif jarang terjadi, tetapi mengubah satu file di antara kompilasi adalah hal yang umum, khususnya dalam pengembangan berbasis pengujian. Contoh di atas juga memiliki beberapa tindakan pemaketan non-Java yang dapat menimpa 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 kecepatan 3x lipat (rata-rata 20 build inkremental dengan satu build warmup
dihapus):
Gambar 2. Grafik peningkatan performa build inkremental.
Percepatan tergantung pada perubahan yang dilakukan. Percepatan faktor 6 diukur dalam situasi di atas ketika konstanta yang biasa digunakan diubah.
Mengubah pekerja persisten
Anda dapat meneruskan flag
--worker_extra_flag
untuk menentukan flag start-up ke pekerja, yang dikunci dengan mnemonic. Misalnya,
meneruskan --worker_extra_flag=javac=--debug
akan mengaktifkan proses debug hanya untuk Javac.
Hanya satu flag pekerja yang dapat ditetapkan per penggunaan flag ini, dan hanya untuk satu mnemonik.
Pekerja tidak hanya dibuat secara terpisah untuk setiap mnemonik, tetapi juga untuk
variasi dalam flag start-up mereka. Setiap kombinasi tanda mnemonik dan start-up
digabungkan menjadi WorkerKey
, dan untuk setiap WorkerKey
, maksimal
worker_max_instances
pekerja dapat dibuat. Lihat bagian berikutnya untuk mengetahui cara konfigurasi tindakan juga dapat menentukan flag penyiapan.
Anda dapat menggunakan flag --high_priority_workers
untuk menentukan mnemonik yang harus dijalankan, bukan mnemonik prioritas normal. Hal ini dapat membantu memprioritaskan tindakan yang selalu berada di jalur
penting. Jika ada dua atau beberapa pekerja berprioritas tinggi yang mengeksekusi permintaan, semua
pekerja lainnya tidak akan dapat berjalan. Tanda ini dapat digunakan beberapa kali.
Meneruskan flag --worker_sandboxing
akan membuat setiap permintaan pekerja menggunakan direktori sandbox terpisah untuk semua inputnya. Menyiapkan sandbox memerlukan waktu ekstra,
terutama di macOS, tetapi memberikan jaminan ketepatan yang lebih baik.
Flag
--worker_quit_after_build
sangat berguna untuk proses debug dan pembuatan profil. Flag ini memaksa semua pekerja untuk berhenti setelah build selesai. Anda juga dapat meneruskan
--worker_verbose
untuk
mendapatkan lebih banyak output tentang apa yang dilakukan pekerja. Flag ini tercermin dalam
kolom verbosity
di WorkRequest
, yang memungkinkan implementasi pekerja juga
menjadi lebih panjang.
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 detailnya di halaman Performa Build Android.
Mengimplementasikan pekerja persisten
Lihat halaman membuat pekerja persisten untuk mengetahui informasi selengkapnya tentang cara membuat pekerja.
Contoh ini menampilkan 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
selanjutnya akan terlihat seperti:
CATATAN: Meskipun spesifikasi buffering 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 ketika membahas kolom
terlepas dari protokol.
{
"arguments": [ "-g", "-source", "1.5", "Foo.java" ]
"inputs": [
{ "path": "symlinkfarm/input1", "digest": "d49a..." },
{ "path": "symlinkfarm/input2", "digest": "093d..." },
],
}
Worker menerima ini di stdin
dalam format JSON yang dibatasi baris baru (karena
requires-worker-protocol
ditetapkan ke JSON). Worker kemudian akan melakukan tindakan,
dan mengirimkan WorkResponse
berformat JSON ke Bazel di stdout-nya. Bazel kemudian
mengurai respons ini dan mengonversinya secara manual menjadi proto WorkResponse
. Untuk berkomunikasi dengan pekerja terkait menggunakan protobuf berenkode biner dan bukan JSON, requires-worker-protocol
akan ditetapkan ke proto
, seperti ini:
execution_requirements = {
"supports-workers" : "1" ,
"requires-worker-protocol" : "proto"
}
Jika requires-worker-protocol
tidak disertakan dalam persyaratan eksekusi,
Bazel akan menetapkan secara default komunikasi pekerja agar menggunakan protobuf.
Bazel mengambil WorkerKey
dari mnemonik dan flag bersama, sehingga jika konfigurasi ini memungkinkan perubahan parameter max_mem
, pekerja terpisah akan muncul untuk setiap nilai yang digunakan. Hal ini dapat menyebabkan konsumsi memori yang berlebihan jika
terlalu banyak variasi yang digunakan.
Setiap pekerja saat ini hanya dapat memproses satu permintaan dalam satu waktu. Fitur pekerja multiplex eksperimental memungkinkan penggunaan beberapa thread, jika alat yang mendasarinya adalah multi-thread dan wrapper disiapkan untuk memahami hal ini.
Dalam repo GitHub ini, Anda dapat melihat contoh wrapper worker yang ditulis dalam Java serta Python. Jika Anda menggunakan JavaScript atau TypeScript, paket@bazel/worker dan contoh pekerja nodejs mungkin dapat membantu.
Bagaimana pengaruh pekerja terhadap sandbox?
Penggunaan strategi worker
secara default tidak menjalankan tindakan di
sandbox, mirip dengan strategi local
. Anda dapat menyetel
flag --worker_sandboxing
untuk menjalankan semua pekerja di dalam sandbox, yang memastikan setiap
eksekusi alat hanya melihat file input yang seharusnya dimiliki. Alat ini
mungkin masih membocorkan informasi di antara permintaan secara internal, misalnya melalui
cache. Penggunaan strategi dynamic
mengharuskan pekerja di-sandbox.
Agar cache compiler dapat digunakan dengan benar bersama worker, ringkasan diteruskan bersama setiap file input. Dengan demikian, compiler atau wrapper dapat memeriksa apakah input masih valid tanpa harus membaca file.
Bahkan saat menggunakan ringkasan input untuk mencegah penyimpanan cache yang tidak diinginkan, pekerja dengan sandbox menawarkan sandbox yang kurang ketat dibandingkan sandbox murni, karena alat ini dapat mempertahankan status internal lain yang telah terpengaruh oleh permintaan sebelumnya.
Pekerja multipleks 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 informasi selengkapnya tentang pekerja persisten, lihat: