Evaluasi paralel dan model inkrementalitas Bazel.
Model data
Model data terdiri dari item berikut:
SkyValue
. Juga disebut dengan node.SkyValues
adalah objek tetap yang berisi semua data yang dibuat selama proses build dan input build. Contohnya adalah: file input, file output, target, dan target yang dikonfigurasi.SkyKey
. Nama yang tidak dapat diubah untuk mereferensikanSkyValue
, misalnya,FILECONTENTS:/tmp/foo
atauPACKAGE://foo
.SkyFunction
. Membuat node berdasarkan kunci dan node dependennya.- Grafik node. Struktur data yang berisi hubungan dependensi antara node.
Skyframe
. Nama kode untuk framework evaluasi inkremental Bazel didasarkan.
Evaluasi
Build terdiri dari evaluasi node yang mewakili permintaan build (ini adalah status yang kami perjuangkan, tetapi ada banyak kode lama dalam prosesnya). Pertama, SkyFunction
ditemukan dan dipanggil dengan kunci SkyKey
level teratas. Fungsi ini kemudian meminta evaluasi node yang dibutuhkan untuk mengevaluasi node tingkat atas, yang pada akhirnya menghasilkan pemanggilan fungsi lainnya, dan seterusnya, sampai node daun tercapai (yang biasanya merupakan node yang mewakili file input dalam sistem file). Terakhir, kita berakhir dengan nilai SkyValue
level atas, beberapa efek samping (seperti file output dalam sistem file) dan grafik asiklik terarah dari dependensi antara node yang terlibat dalam build.
SkyFunction
dapat meminta SkyKeys
dalam beberapa penerusan jika tidak dapat memberi tahu semua node yang diperlukan untuk melakukan tugasnya terlebih dahulu. Contoh sederhananya adalah mengevaluasi node file input yang ternyata merupakan symlink: fungsi tersebut mencoba membaca file, menyadari bahwa file tersebut adalah symlink, sehingga mengambil node sistem file yang mewakili target symlink. Tetapi elemen itu sendiri dapat berupa symlink, yang dalam hal ini fungsi asli juga perlu mengambil targetnya.
Fungsi direpresentasikan dalam kode oleh antarmuka SkyFunction
dan layanan yang disediakan oleh antarmuka bernama SkyFunction.Environment
. Berikut adalah hal-hal yang dapat dilakukan fungsi:
- Minta evaluasi node lain dengan memanggil
env.getValue
. Jika node tersedia, nilainya akan ditampilkan, jika tidak,null
akan ditampilkan dan fungsi itu sendiri diharapkan untuk menampilkannull
. Pada kasus terakhir, node dependen akan dievaluasi, lalu builder node asli akan dipanggil lagi, tetapi kali ini panggilanenv.getValue
yang sama akan menampilkan nilai non-null
. - Minta evaluasi beberapa node lainnya dengan memanggil
env.getValues()
. Ini pada dasarnya sama, kecuali bahwa node dependen dievaluasi secara paralel. - Melakukan komputasi selama pemanggilan
- Memiliki efek samping, misalnya, menulis file ke sistem file. Perlu diperhatikan bahwa dua fungsi yang berbeda tidak saling mengikuti. Secara umum, tuliskan efek samping (saat data mengalir keluar dari Bazel) bukan merupakan hal yang baik, tetapi sebaiknya gunakan efek samping baca (saat data mengalir ke dalam ke Bazel tanpa dependensi yang terdaftar), karena keduanya merupakan dependensi yang tidak terdaftar dan dengan demikian, dapat menyebabkan build inkremental yang salah.
Implementasi SkyFunction
tidak boleh mengakses data dengan cara selain meminta dependensi (seperti dengan langsung membaca sistem file), karena hal itu akan menyebabkan Bazel tidak mendaftarkan dependensi data pada file yang dibaca sehingga menghasilkan build inkremental yang salah.
Setelah fungsi memiliki cukup data untuk melakukan tugasnya, fungsi tersebut akan menampilkan nilai non-null
yang menunjukkan penyelesaian.
Strategi evaluasi ini memiliki sejumlah manfaat:
- Hermetik. Jika fungsi hanya meminta data input dengan cara bergantung pada node lain, Bazel dapat menjamin bahwa jika status input sama, data yang sama akan ditampilkan. Jika semua fungsi langit bersifat deterministik, artinya seluruh build juga akan bersifat deterministik.
- Inkrementalitas yang benar dan sempurna. Jika semua data input dari semua fungsi dicatat, Bazel hanya dapat membatalkan validasi kumpulan node yang tepat yang perlu dibatalkan validasinya saat data input berubah.
- Paralelisme. Karena fungsi hanya dapat berinteraksi satu sama lain dengan meminta dependensi, fungsi yang tidak bergantung satu sama lain dapat dijalankan secara paralel dan Bazel dapat menjamin bahwa hasilnya sama seperti jika dijalankan secara berurutan.
Incrementality
Karena fungsi hanya dapat mengakses data input dengan bergantung pada node lain, Bazel dapat membuat grafik aliran data lengkap dari file input ke file output, dan menggunakan informasi ini hanya untuk mem-build ulang node yang benar-benar perlu dibuat ulang: penutupan transitif terbalik dari kumpulan file input yang diubah.
Secara khusus, ada dua kemungkinan strategi inkrementalitas: strategi dari bawah ke atas dan strategi dari atas ke bawah. Mana yang optimal bergantung pada tampilan grafik dependensi.
Selama pembatalan validasi dari bawah ke atas, setelah grafik dibuat dan kumpulan input yang diubah diketahui, semua node menjadi tidak valid yang secara transitif bergantung pada file yang diubah. Hal ini optimal jika kita tahu bahwa node tingkat atas yang sama akan di-build lagi. Perhatikan bahwa pembatalan dari bawah ke atas memerlukan menjalankan
stat()
pada semua file input dari build sebelumnya untuk menentukan apakah file tersebut diubah. Anda dapat meningkatkannya menggunakaninotify
atau mekanisme serupa untuk mempelajari file yang diubah.Selama pembatalan validasi dari atas ke bawah, penutupan transitif node tingkat atas akan diperiksa dan hanya node tersebut yang dipertahankan yang penutupan transitifnya bersih. Hal ini akan lebih baik jika kita tahu bahwa grafik node saat ini besar, tetapi kita hanya membutuhkan subset kecil darinya pada build berikutnya: pembatalan validasi dari bawah ke atas akan membatalkan grafik yang lebih besar dari build pertama, tidak seperti pembatalan validasi dari atas ke bawah, yang hanya berjalan dengan grafik kecil dari build kedua.
Saat ini kami hanya melakukan pembatalan dari bawah ke atas.
Untuk mendapatkan inkrementalitas lebih lanjut, kita akan menggunakan pemangkasan perubahan: jika node menjadi tidak valid, tetapi setelah di-build ulang, ternyata nilai barunya sama dengan nilai lamanya, node yang dibatalkan validasinya karena perubahan dalam node ini “dibangkitkan kembali”.
Hal ini berguna, misalnya, jika seseorang mengubah komentar dalam file C++: lalu file .o
yang dihasilkannya akan sama, sehingga kita tidak perlu memanggil penaut lagi.
Penautan / Kompilasi Inkremental
Batasan utama dari model ini adalah bahwa pembatalan node adalah masalah semua atau tidak sama sekali: ketika dependensi berubah, node dependen selalu dibuat ulang dari awal, meskipun algoritme yang lebih baik akan ada yang dapat mengubah nilai node lama berdasarkan perubahan tersebut. Berikut ini beberapa contoh yang berguna:
- Penautan inkremental
- Saat satu file
.class
berubah di.jar
, secara teoretis kita dapat mengubah file.jar
, bukan membuatnya lagi dari awal.
Alasan Bazel saat ini tidak mendukung hal-hal ini dengan cara yang prinsipnya (kami memiliki beberapa dukungan untuk penautan inkremental, tetapi tidak diimplementasikan dalam Skyframe) adalah dua kali lipat: kami hanya memiliki peningkatan performa terbatas dan sulit untuk menjamin bahwa hasil mutasinya sama dengan build ulang yang bersih, dan build Google value yang bit-bit-bit-dapat diulang.
Hingga saat ini, kita selalu dapat mencapai performa yang cukup baik hanya dengan menguraikan langkah build yang mahal dan mencapai evaluasi ulang sebagian dengan cara tersebut: tindakan ini membagi semua class dalam aplikasi menjadi beberapa grup dan melakukan dexing pada keduanya secara terpisah. Dengan demikian, jika class dalam suatu grup tidak berubah, dexing tidak perlu diulang.
Pemetaan ke konsep Bazel
Ini adalah ringkasan kasar dari beberapa implementasi SkyFunction
yang digunakan Bazel untuk melakukan build:
- FileStateValue. Hasil
lstat()
. Untuk file yang sudah ada, kami juga menghitung informasi tambahan untuk mendeteksi perubahan pada file. Ini adalah node level terendah dalam grafik Skyframe dan tidak memiliki dependensi. - FileValue. Digunakan oleh apa pun yang peduli dengan konten yang sebenarnya dan/atau menyelesaikan jalur file. Bergantung pada
FileStateValue
yang sesuai dan symlink apa pun yang perlu diselesaikan (sepertiFileValue
untuka/b
memerlukan jalura
yang telah di-resolve dan jalura/b
yang telah diselesaikan). Perbedaan antaraFileStateValue
penting karena dalam beberapa kasus (misalnya, mengevaluasi glob sistem file (sepertisrcs=glob(["*/*.java"])
) konten file sebenarnya tidak diperlukan. - DirectoryListingValue. Pada dasarnya, hasil dari
readdir()
. Bergantung padaFileValue
terkait yang terkait dengan direktori. - PackageValue yang diinginkan. Menyatakan versi terurai dari file BUILD. Bergantung pada
FileValue
fileBUILD
terkait, dan juga secara transitif padaDirectoryListingValue
apa pun yang digunakan untuk me-resolve glob dalam paket (struktur data yang mewakili isi fileBUILD
secara internal) - ConfiguredTargetValue. Merepresentasikan target yang dikonfigurasi, yang merupakan tuple dari kumpulan tindakan yang dihasilkan selama analisis target dan informasi yang diberikan ke target yang dikonfigurasi yang bergantung pada target ini. Bergantung pada
PackageValue
target yang sesuai,ConfiguredTargetValues
dependensi langsung, dan node khusus yang mewakili konfigurasi build. - ArtifactValue yang diinginkan. Menyatakan file dalam build, baik berupa sumber maupun artefak output (artefak hampir setara dengan file, dan digunakan untuk merujuk ke file selama eksekusi langkah build yang sebenarnya). Untuk file sumber, dependensinya bergantung pada
FileValue
node terkait, untuk artefak output, dependensinya bergantung padaActionExecutionValue
dari tindakan apa pun yang menghasilkan artefak. - ActionExecutionValue. Merepresentasikan eksekusi tindakan. Bergantung pada
ArtifactValues
file inputnya. Tindakan yang dijalankan saat ini berada dalam kunci sky-nya, yang bertentangan dengan konsep bahwa kunci sky harus berukuran kecil. Kami sedang berupaya menyelesaikan perbedaan ini (perhatikan bahwaActionExecutionValue
danArtifactValue
tidak digunakan jika kita tidak menjalankan fase eksekusi di Skyframe).