Bingkai

Laporkan masalah Lihat sumber Per malam · 7,3 · 7,2 · 7,1 · 7,0 · 6,5

Evaluasi paralel dan model inkrementalitas Bazel.

Model data

Model data terdiri dari item berikut:

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

Evaluasi

Build dicapai dengan mengevaluasi node yang mewakili permintaan build.

Pertama, Bazel menemukan SkyFunction yang sesuai dengan kunci dari level atas SkyKey. Fungsi ini kemudian meminta evaluasi dari {i>node<i} yang diperlukan mengevaluasi node tingkat atas, yang pada akhirnya menghasilkan panggilan SkyFunction lainnya, hingga node {i>leaf<i} tercapai. Simpul daun biasanya adalah yang mewakili file input dalam sistem file. Akhirnya, Bazel berakhir dengan nilai SkyValue tingkat teratas, beberapa efek samping (seperti file output dalam file ) dan grafik terarah asiklik dari dependensi antara node yang terlibat dalam build.

SkyFunction dapat meminta SkyKeys dalam beberapa penerusan jika tidak dapat memberi tahu memajukan semua {i>node<i} yang dibutuhkan untuk melakukan tugasnya. Contoh sederhana adalah mengevaluasi {i>node file<i} input yang ternyata adalah {i>symlink<i}: fungsi tersebut mencoba membaca file, menyadari bahwa itu adalah {i>symlink<i}, dan dengan demikian mengambil node sistem file yang mewakili target symlink. Tapi itu sendiri bisa berupa {i>symlink<i}, di 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. Ini 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 menampilkan null. Dalam kasus yang terakhir, node dependen dievaluasi, lalu builder node asli dipanggil lagi, namun kali ini panggilan env.getValue yang sama akan menampilkan bukan null.
  • Minta evaluasi beberapa node lainnya dengan memanggil env.getValues(). Prinsip ini pada dasarnya sama, hanya saja node dependen adalah dievaluasi secara paralel.
  • Melakukan komputasi selama pemanggilan
  • Memiliki efek samping, misalnya, menulis file ke sistem file. Kebutuhan perawatan diambil bahwa dua fungsi yang berbeda menghindari menginjak satu sama lain jari kaki. Secara umum, tulis efek samping ({i>data<i} mengalir keluar dari Bazel) tidak apa-apa, efek samping baca (di mana data mengalir ke dalam ke Bazel tanpa dependensi terdaftar) tidak, karena merupakan dependensi yang tidak terdaftar dan karenanya, dapat menyebabkan build inkremental yang salah.

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

Setelah memiliki cukup data untuk melakukan tugasnya, fungsi harus menampilkan non-null nilai yang menunjukkan penyelesaian.

Strategi evaluasi ini memiliki sejumlah manfaat:

  • Hermetikitas. Jika fungsi hanya meminta data input dengan cara bergantung pada di node lainnya, Bazel dapat menjamin bahwa jika status inputnya sama, data yang sama ditampilkan. Jika semua fungsi langit bersifat determenistik, ini berarti bahwa seluruh build juga akan bersifat determenistik.
  • Inkrementalitas yang benar dan sempurna. Jika semua data input dari semua fungsi direkam, Bazel hanya dapat membatalkan kumpulan {i>node<i} yang tepat yang perlu menjadi tidak valid saat data input berubah.
  • Paralelisme. Karena fungsi hanya dapat berinteraksi satu sama lain melalui meminta dependensi, fungsi yang tidak bergantung satu sama lain dapat berjalan secara paralel dan Bazel dapat menjamin bahwa hasilnya sama seolah-olah tugas-tugas ini dijalankan secara berurutan.

Inkrementalitas

Karena fungsi hanya dapat mengakses data input berdasarkan {i>node<i} lain, Bazel dapat membuat grafik aliran data yang lengkap dari file {i>input<i} ke {i>output<i} file, dan menggunakan informasi ini hanya untuk membangun ulang {i>node<i} yang benar-benar membutuhkan yang akan dibuat ulang: penutupan transitif terbalik dari kumpulan file input yang diubah.

Secara khusus, ada dua kemungkinan strategi inkrementalitas: metode {i>bottom-up<i} dan dari atas ke bawah. Mana yang optimal tergantung pada bagaimana grafik dependensi itu.

  • Selama pembatalan validasi bottom-up, setelah grafik dibuat dan kumpulan perubahannya diketahui, semua node menjadi tidak valid dan bergantung pada mengubah file. Hal ini optimal jika node level teratas yang sama akan dibuat untuk mencoba lagi perintah. Perhatikan bahwa pembatalan validasi bottom-up perlu menjalankan stat() di semua file input dari build sebelumnya untuk menentukan apakah file tersebut diubah. Ini dapat ditingkatkan menggunakan inotify atau mekanisme serupa untuk mempelajari mengubah file.

  • Selama pembatalan validasi top-down, penutupan transitif node level atas diperiksa dan hanya {i>node<i} yang dipertahankan yang penutupan transitifnya bersih. Hal ini lebih baik jika grafik node berukuran besar, tetapi build berikutnya hanya memerlukan sebuah sebagian kecil saja: pembatalan validasi bottom-up akan membatalkan grafik yang lebih besar build pertama, tidak seperti pembatalan {i>top-down<i}, yang hanya menjelaskan grafik build kedua.

Bazel hanya melakukan pembatalan validasi bottom-up.

Untuk mendapatkan inkrementalitas lebih lanjut, Bazel menggunakan pemangkasan perubahan: jika node dibatalkan, namun setelah dibuat ulang, ditemukan bahwa nilai barunya sama sebagai nilai lamanya, node yang dibatalkan validasinya karena perubahan pada node ini "dibangkitkan".

Ini berguna, misalnya, jika seseorang mengubah komentar dalam file C++: maka File .o yang dihasilkan darinya akan sama, jadi tidak perlu memanggil {i>link <i}lagi.

Penautan / Kompilasi Inkremental

Keterbatasan utama dari model ini adalah pembatalan validasi node masalah menyeluruh: saat dependensi berubah, node dependen akan selalu dibangun ulang dari awal, bahkan jika ada algoritma yang lebih baik yang akan memutasikan nilai lama {i>node<i} berdasarkan perubahannya. Beberapa contoh di mana berguna:

  • Penautan inkremental
  • Saat satu file class berubah dalam file JAR, dimungkinkan memodifikasi file JAR di tempat alih-alih membangunnya dari awal lagi.

Alasan mengapa Bazel tidak mendukung hal-hal ini dengan cara yang berprinsip ada dua hal:

  • Peningkatan performa yang terjadi terbatas.
  • Sulit untuk memvalidasi bahwa hasil mutasinya sama dengan hasil tersebut dari proses build ulang yang bersih, dan nilai-nilai Google untuk build yang bit-for-bit dapat diulang.

Sejauh ini, dimungkinkan untuk mencapai kinerja yang cukup baik dengan menguraikan dan mencapai evaluasi ulang parsial dengan cara tersebut. Misalnya, di aplikasi Android, Anda dapat membagi semua class menjadi beberapa grup dan dex mereka secara terpisah. Dengan cara ini, jika kelas dalam grup tidak berubah, dexing akan tidak harus diulangi.

Pemetaan ke konsep Bazel

Ini adalah ringkasan umum dari kunci SkyFunction dan SkyValue implementasi yang digunakan Bazel untuk menjalankan build:

  • FileStateValue. Hasil lstat(). Untuk file yang ada, juga menghitung informasi tambahan untuk mendeteksi perubahan pada file tersebut. Ini adalah {i>node<i} tingkat terendah dalam grafik Skyframe dan tidak dependensi.
  • FileValue. Digunakan oleh apa pun yang peduli dengan konten aktual atau penyelesaian jalur file. Bergantung pada FileStateValue yang sesuai dan symlink apa pun yang perlu diselesaikan (seperti FileValue untuk a/b memerlukan jalur a yang di-resolve dan jalur a/b yang di-resolve). Tujuan perbedaan antara FileValue dan FileStateValue penting karena yang disebut terakhir dapat digunakan jika isi file tidak benar-benar dibutuhkan. Misalnya, isi file tidak relevan saat mengevaluasi glob sistem file (seperti srcs=glob(["*/*.java"])).
  • DirectoryListingStateValue. Hasil readdir(). Suka FileStateValue, ini adalah node tingkat terendah dan tidak memiliki dependensi.
  • DirectoryListingValue. Digunakan oleh apa pun yang peduli tentang entri dari sebuah direktori. Bergantung pada DirectoryListingStateValue yang sesuai, sebagai serta FileValue direktori yang terkait.
  • PackageValue. Mewakili versi file BUILD yang diurai. Bergantung pada FileValue dari file BUILD yang terkait, dan juga secara transitif pada DirectoryListingValue yang digunakan untuk me-resolve glob dalam paket (struktur data yang mewakili isi file BUILD secara internal).
  • ConfiguredTargetValue. Mewakili target yang dikonfigurasi, yaitu tuple serangkaian tindakan yang dihasilkan selama analisis target dan informasi yang diberikan ke target dependen yang dikonfigurasi. Bergantung pada PackageValue target yang sesuai ada, ConfiguredTargetValues dependensi langsung, dan node khusus yang mewakili build konfigurasi Anda.
  • ArtifactValue. Merepresentasikan file dalam build, baik berupa sumber artefak output. Artefak hampir sama dengan file, dan digunakan untuk merujuk ke file selama eksekusi langkah-langkah build yang sebenarnya. File sumber bergantung pada FileValue node yang terkait, dan artefak output bergantung pada ActionExecutionValue dari tindakan apa pun yang menghasilkan artefak.
  • ActionExecutionValue. Menyatakan eksekusi suatu tindakan. Bergantung pada ArtifactValues file inputnya. Tindakan yang dijalankan dimuat di dalam SkyKey-nya, yang bertentangan dengan konsep bahwa SkyKeys harus kecil. Perhatikan bahwa ActionExecutionValue dan ArtifactValue tidak akan digunakan jika fase eksekusi tidak berjalan.

Sebagai alat bantu visual, diagram ini menunjukkan hubungan antara Implementasi SkyFunction setelah build Bazel itu sendiri:

Grafik hubungan implementasi SkyFunction