Bingkai

Laporkan masalah Lihat sumber Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Model evaluasi paralel dan inkrementalitas Bazel.

Model data

Model data terdiri dari item berikut:

  • SkyValue. Juga disebut node. SkyValues adalah objek yang tidak dapat diubah yang berisi semua data yang dibuat selama proses build dan input dari build. Contohnya adalah: file input, file output, target, dan target yang dikonfigurasi.
  • SkyKey. Nama pendek yang tidak dapat diubah untuk merujuk ke SkyValue, misalnya, FILECONTENTS:/tmp/foo atau PACKAGE://foo.
  • SkyFunction. Membangun node berdasarkan kunci dan node dependennya.
  • Grafik node. Struktur data yang berisi hubungan dependensi antar-node.
  • Skyframe. Nama kode untuk framework evaluasi inkremental yang menjadi dasar Bazel.

Evaluasi

Build dicapai dengan mengevaluasi node yang merepresentasikan permintaan build.

Pertama, Bazel menemukan SkyFunction yang sesuai dengan kunci SkyKey tingkat teratas. Kemudian, fungsi meminta evaluasi node yang diperlukan untuk mengevaluasi node tingkat teratas, yang pada gilirannya menghasilkan panggilan SkyFunction lainnya, hingga node daun tercapai. Node daun biasanya adalah node yang merepresentasikan file input dalam sistem file. Terakhir, Bazel akan mendapatkan nilai SkyValue tingkat teratas, 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 langkah jika tidak dapat mengetahui semua node yang diperlukan untuk melakukan tugasnya terlebih dahulu. Contoh sederhananya adalah mengevaluasi node file input yang ternyata merupakan symlink: fungsi mencoba membaca file, menyadari bahwa file tersebut adalah symlink, dan dengan demikian mengambil node sistem file yang merepresentasikan target symlink. Namun, direktori itu sendiri dapat berupa symlink, yang dalam hal ini fungsi asli juga perlu mengambil targetnya.

Fungsi diwakili dalam kode oleh antarmuka SkyFunction dan layanan yang disediakan untuknya oleh antarmuka yang disebut SkyFunction.Environment. Berikut 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 menampilkan null. Dalam kasus terakhir, node dependen dievaluasi, lalu builder node asli dipanggil lagi, tetapi kali ini panggilan env.getValue yang sama akan menampilkan nilai non-env.getValue.null
  • Minta evaluasi beberapa node lain dengan memanggil env.getValues(). Hal ini pada dasarnya sama, kecuali 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 mengganggu. Secara umum, efek samping penulisan (saat data mengalir keluar dari Bazel) tidak masalah, efek samping pembacaan (saat data mengalir ke dalam Bazel tanpa dependensi terdaftar) tidak boleh, karena merupakan dependensi yang tidak terdaftar dan dengan demikian, dapat menyebabkan build inkremental yang salah.

Implementasi SkyFunction yang berperilaku baik menghindari akses data dengan cara lain selain meminta dependensi (seperti dengan membaca sistem file secara langsung), karena hal itu menyebabkan Bazel tidak mendaftarkan dependensi data pada file yang dibaca, sehingga menghasilkan build inkremental yang salah.

Setelah memiliki cukup data untuk menjalankan tugasnya, fungsi akan menampilkan nilai selain null yang menunjukkan penyelesaian.

Strategi evaluasi ini memiliki sejumlah manfaat:

  • Hermetisitas. Jika fungsi hanya meminta data input dengan bergantung pada node lain, Bazel dapat menjamin bahwa jika status inputnya sama, data yang sama akan ditampilkan. Jika semua fungsi langit bersifat deterministik, berarti seluruh build juga akan bersifat deterministik.
  • Kenaikan yang benar dan sempurna. Jika semua data input 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 cara meminta dependensi, fungsi yang tidak bergantung satu sama lain dapat dijalankan secara paralel dan Bazel dapat menjamin bahwa hasilnya sama seperti jika fungsi tersebut dijalankan secara berurutan.

Inkrementalitas

Karena fungsi hanya dapat mengakses data input dengan bergantung pada node lain, Bazel dapat membuat grafik alur data lengkap dari file input ke file output, dan menggunakan informasi ini untuk hanya membangun kembali node yang benar-benar perlu dibangun kembali: penutupan transitif terbalik dari kumpulan file input yang berubah.

Secara khusus, ada dua kemungkinan strategi inkrementalitas: strategi bottom-up dan strategi top-down. Mana yang optimal bergantung pada tampilan grafik dependensi.

  • Selama pembatalan validasi dari bawah ke atas, setelah grafik dibuat dan set input yang berubah diketahui, semua node yang bergantung secara transitif pada file yang berubah akan dibatalkan validasinya. Hal ini optimal jika node tingkat teratas yang sama akan dibangun lagi. Perhatikan bahwa pembatalan dari bawah ke atas memerlukan eksekusi stat() pada semua file input build sebelumnya untuk menentukan apakah file tersebut diubah. Hal ini dapat ditingkatkan dengan menggunakan inotify atau mekanisme serupa untuk mempelajari file yang berubah.

  • Selama pembatalan validasi dari atas ke bawah, penutupan transitif node tingkat teratas diperiksa dan hanya node yang penutupan transitifnya bersih yang dipertahankan. Hal ini lebih baik jika grafik node berukuran besar, tetapi build berikutnya hanya memerlukan sebagian kecilnya: pembatalan validasi dari bawah ke atas akan membatalkan validasi grafik yang lebih besar dari build pertama, tidak seperti pembatalan validasi dari atas ke bawah, yang hanya menelusuri grafik kecil dari build kedua.

Bazel hanya melakukan pembatalan dari bawah ke atas.

Untuk mendapatkan inkrementalitas lebih lanjut, Bazel menggunakan penghapusan perubahan: jika node dibatalkan validasinya, tetapi setelah dibangun ulang, diketahui bahwa nilai barunya sama dengan nilai lamanya, node yang dibatalkan validasinya karena perubahan pada node ini akan "dibangkitkan".

Hal ini berguna, misalnya, jika seseorang mengubah komentar dalam file C++: maka file .o yang dihasilkan darinya akan sama, sehingga tidak perlu memanggil linker lagi.

Penautan / Kompilasi Tambahan

Batasan utama model ini adalah pembatalan validasi node bersifat semua atau tidak sama sekali: saat dependensi berubah, node dependen selalu dibangun ulang dari awal, meskipun ada algoritma yang lebih baik yang akan memutasikan nilai lama node berdasarkan perubahan. Beberapa contoh situasi yang cocok untuk fitur ini:

  • Penautan inkremental
  • Jika satu file class berubah dalam file JAR, file JAR dapat dimodifikasi di tempat, bukan dibangun dari awal lagi.

Alasan Bazel tidak mendukung hal-hal ini secara prinsip adalah karena dua hal:

  • Peningkatan performa terbatas.
  • Akan sulit untuk memvalidasi bahwa hasil mutasi sama dengan hasil pembangunan ulang bersih, dan Google menghargai build yang dapat diulang bit demi bit.

Hingga saat ini, performa yang cukup baik dapat dicapai dengan menguraikan langkah build yang mahal dan mencapai evaluasi ulang sebagian dengan cara tersebut. Misalnya, di aplikasi Android, Anda dapat membagi semua class ke dalam beberapa grup dan menggabungkannya secara terpisah. Dengan cara ini, jika class dalam grup tidak berubah, dexing tidak perlu dilakukan lagi.

Pemetaan ke konsep Bazel

Berikut ringkasan tingkat tinggi dari penerapan SkyFunction dan SkyValue utama yang digunakan Bazel untuk melakukan build:

  • FileStateValue. Hasil lstat(). Untuk file yang sudah ada, fungsi ini juga menghitung informasi tambahan untuk mendeteksi perubahan pada file. Ini adalah node tingkat terendah dalam grafik Skyframe dan tidak memiliki dependensi.
  • FileValue. Digunakan oleh apa pun yang peduli dengan konten sebenarnya atau jalur file yang telah diselesaikan. Bergantung pada FileStateValue yang sesuai dan setiap symlink yang perlu diselesaikan (seperti FileStateValue untuk a/b memerlukan jalur yang diselesaikan dari a dan jalur yang diselesaikan dari a/b). Perbedaan antara FileValue dan FileStateValue penting karena FileStateValue dapat digunakan dalam kasus ketika konten file tidak benar-benar diperlukan.FileValue Misalnya, konten file tidak relevan saat mengevaluasi glob sistem file (seperti srcs=glob(["*/*.java"])).
  • DirectoryListingStateValue. Hasil readdir(). Seperti FileStateValue, ini adalah node tingkat terendah dan tidak memiliki dependensi.
  • DirectoryListingValue. Digunakan oleh apa pun yang peduli dengan entri direktori. Bergantung pada DirectoryListingStateValue yang sesuai, serta FileValue terkait dari direktori.
  • PackageValue. Mewakili versi file BUILD yang diuraikan. Bergantung pada FileValue file BUILD terkait, dan juga secara transitif pada DirectoryListingValue yang digunakan untuk menyelesaikan glob dalam paket (struktur data yang merepresentasikan konten file BUILD secara internal).
  • ConfiguredTargetValue. Mewakili target yang dikonfigurasi, yang merupakan tuple dari kumpulan tindakan yang dihasilkan selama analisis target dan informasi yang diberikan ke target yang dikonfigurasi dependen. Bergantung pada PackageValue target yang sesuai berada, ConfiguredTargetValues dependensi langsung, dan node khusus yang merepresentasikan build konfigurasi.
  • ArtifactValue. Mewakili file dalam build, baik itu sumber maupun artefak output. Artefak hampir setara dengan file, dan digunakan untuk merujuk ke file selama eksekusi langkah-langkah build yang sebenarnya. File sumber bergantung pada FileValue dari node terkait, dan artefak output bergantung pada ActionExecutionValue dari tindakan apa pun yang menghasilkan artefak.
  • ActionExecutionValue. Mewakili eksekusi tindakan. Bergantung pada ArtifactValues file inputnya. Tindakan yang dijalankannya terdapat dalam SkyKey-nya, yang bertentangan dengan konsep bahwa SkyKey harus kecil. Perhatikan bahwa ActionExecutionValue dan ArtifactValue tidak digunakan jika fase eksekusi tidak berjalan.

Sebagai bantuan visual, diagram ini menunjukkan hubungan antara implementasi SkyFunction setelah build Bazel itu sendiri:

Grafik hubungan penerapan SkyFunction