Halaman ini membahas cara menggunakan pekerja persisten, manfaat, persyaratan, dan bagaimana pekerja memengaruhi sandbox.
Pekerja persisten adalah proses yang berjalan lama yang dimulai oleh server Bazel, yang
berfungsi sebagai wrapper di sekitar alat sebenarnya (biasanya compiler), atau
alat itu sendiri. Untuk mendapatkan manfaat dari pekerja persisten, alat tersebut harus mendukung pelaksanaan urutan kompilasi, dan wrapper perlu diterjemahkan antara API alat dan format permintaan/respons yang dijelaskan di bawah ini. Pekerja
yang sama mungkin dipanggil dengan dan tanpa flag --persistent_worker
dalam
build yang sama, dan bertanggung jawab untuk memulai dan berbicara dengan
alat dengan tepat, serta menonaktifkan pekerja saat keluar. Setiap instance pekerja ditetapkan (tetapi tidak di-chroot ke) direktori kerja terpisah di bawah <outputBase>/bazel-workers
.
Menggunakan pekerja persisten adalah strategi eksekusi yang mengurangi overhead start-up, memungkinkan lebih banyak kompilasi JIT, dan memungkinkan caching untuk, misalnya, pohon sintaksis abstrak dalam eksekusi tindakan. Strategi ini mencapai peningkatan ini dengan mengirimkan beberapa permintaan ke proses yang berjalan lama.
Pekerja persisten diterapkan untuk beberapa bahasa, termasuk Java, Scala, Kotlin, dan lainnya.
Program yang menggunakan runtime NodeJS dapat menggunakan library helper @bazel/Worker untuk menerapkan protokol pekerja.
Menggunakan pekerja persisten
Bazel 0.27 dan yang lebih baru
menggunakan pekerja persisten secara default saat menjalankan build, meskipun eksekusi
jarak jauh lebih diutamakan. Untuk tindakan yang tidak mendukung pekerja persisten, Bazel akan kembali memulai instance alat untuk setiap tindakan. Anda dapat menetapkan build secara eksplisit agar menggunakan pekerja persisten dengan menetapkan worker
strategi untuk mnemonik alat yang berlaku. Sebagai praktik terbaik, contoh ini menyertakan penentuan 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 penerapan. Untuk Java, build dapat 2–4 kali lebih cepat, terkadang lebih sering 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 akan 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 pekerja per mnemonik adalah 4, tetapi dapat disesuaikan
dengan
tanda
worker_max_instances
. Ada konsekuensi antara penggunaan CPU yang tersedia dengan baik dan
jumlah kompilasi JIT dan hit cache yang Anda dapatkan. Dengan lebih banyak pekerja, lebih banyak target akan membayar biaya awal menjalankan kode non-JIT dan mencapai cache dingin. Jika Anda memiliki sedikit target untuk dibuat, 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 kumpulan flag (lihat di bawah), sehingga dalam sistem campuran, Anda dapat menggunakan 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
) pada workstation Intel Xeon 3,5 GHz Linux thread 6 core
dengan RAM 64 GB. Untuk setiap konfigurasi pekerja, lima build bersih dijalankan dan
rata-rata empat build terakhir diambil.
Gambar 1. Grafik peningkatan performa build bersih.
Untuk konfigurasi ini, dua pekerja memberikan kompilasi tercepat, meskipun hanya peningkatan 14% daripada satu pekerja. Satu pekerja adalah opsi yang bagus jika Anda ingin menggunakan lebih sedikit memori.
Kompilasi inkremental biasanya jauh lebih menguntungkan. Clean build relatif jarang, tetapi perubahan satu file di antara kompilasi adalah hal yang umum terjadi, khususnya dalam pengembangan berbasis pengujian. Contoh di atas juga memiliki beberapa tindakan pengemasan non-Java ke tindakan tersebut yang dapat membayangi waktu kompilasi tambahan.
Kompilasi ulang sumber Java saja
(//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar
)
setelah mengubah konstanta string internal di
AbstractContainerizingSandboxedSpawn.java
akan mempercepat 3x (rata-rata 20 build inkremental dengan satu build pemanasan yang dihapus):
Gambar 2. Grafik peningkatan performa build inkremental.
Peningkatan kecepatan bergantung pada perubahan yang dibuat. Percepatan faktor 6 diukur dalam situasi di atas saat konstanta yang biasa digunakan berubah.
Memodifikasi pekerja persisten
Anda dapat meneruskan flag
--worker_extra_flag
untuk menentukan flag start-up kepada pekerja, yang dikunci dengan mnemonic. Misalnya,
meneruskan --worker_extra_flag=javac=--debug
hanya akan mengaktifkan proses debug 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 flag mnemonic dan
start-up 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 flag penyiapan.
Anda dapat menggunakan flag
--high_priority_workers
untuk menentukan mnemonik yang harus dijalankan lebih diutamakan daripada mnemonik
dengan prioritas normal. Ini dapat membantu memprioritaskan tindakan yang selalu berada di jalur
penting. Jika ada dua atau lebih pekerja dengan prioritas tinggi yang menjalankan permintaan, semua
pekerja lainnya akan dicegah untuk berjalan. Tanda ini dapat digunakan beberapa kali.
Meneruskan
--worker_sandboxing
membuat setiap permintaan pekerja menggunakan direktori sandbox terpisah untuk semua
inputnya. Penyiapan sandbox memerlukan waktu beberapa saat,
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
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 untuk
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 mnemonic. Karena 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 Android Build.
Mengimplementasikan pekerja persisten
Lihat halaman membuat pekerja persisten untuk informasi 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 ini:
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
ditetapkan ke JSON). Pekerja kemudian melakukan tindakan tersebut, dan mengirimkan WorkResponse
berformat JSON ke Bazel pada stdout-nya. Selanjutnya, Bazel akan mengurai respons ini dan mengonversinya secara manual menjadi proto WorkResponse
. Untuk
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 untuk menggunakan protobuf.
Bazel memperoleh WorkerKey
dari mnemonic dan flag bersama, sehingga jika
konfigurasi ini memungkinkan perubahan parameter max_mem
, pekerja terpisah
akan dihasilkan 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. Fitur pekerja multipleks eksperimental memungkinkan penggunaan beberapa thread, jika alat yang mendasarinya multi-thread dan wrapper disiapkan untuk memahaminya.
Di repositori GitHub ini, Anda dapat melihat contoh wrapper pekerja yang ditulis di Java serta di Python. Jika Anda bekerja di JavaScript atau TypeScript, paket@bazel/pekerja 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 menetapkan
tanda --worker_sandboxing
untuk menjalankan semua pekerja di dalam sandbox, memastikan setiap
eksekusi alat hanya melihat file input yang seharusnya dimilikinya. Alat ini
mungkin masih membocorkan informasi antarpermintaan secara internal, misalnya melalui
cache. Menggunakan strategi dynamic
mengharuskan pekerja untuk di-sandbox.
Untuk memungkinkan penggunaan cache compiler yang benar dengan pekerja, ringkasan akan diteruskan bersama setiap file input. Dengan demikian, compiler atau wrapper dapat memeriksa apakah inputnya masih valid tanpa harus membaca file.
Bahkan saat menggunakan ringkasan input untuk melindungi dari caching yang tidak diinginkan, pekerja yang di-sandbox akan menawarkan sandbox yang kurang ketat dibandingkan dengan sandbox murni, karena alat ini dapat mempertahankan status internal lainnya yang telah terpengaruh oleh permintaan sebelumnya.
Pekerja multipleks hanya dapat di-sandbox jika implementasi pekerja mendukungnya,
dan sandbox ini harus diaktifkan secara terpisah dengan
flag --experimental_worker_multiplex_sandboxing
. Lihat detail selengkapnya di dokumen desain).
Bacaan lebih lanjut
Untuk informasi selengkapnya tentang pekerja persisten, lihat:
- Postingan blog pekerja tetap asli
- Deskripsi implementasi Haskell {: .external}
- Postingan blog oleh Mike Morearty {: .external}
- Pengembangan Front End dengan Bazel: Angular/TypeScript dan Pekerja Tetap dengan Asana {: .external}
- Penjelasan strategi Bazel {: .external}
- Diskusi strategi pekerja yang informatif di milis diskusi bazel {: .external}