การประเมินพร้อมกันและโมเดลส่วนเพิ่มของ Bazel
โมเดลข้อมูล
โมเดลข้อมูลประกอบด้วยรายการต่อไปนี้
SkyValue
เรียกอีกอย่างว่าโหนดSkyValues
เป็นออบเจ็กต์ที่เปลี่ยนแปลงไม่ได้ซึ่งมีข้อมูลทั้งหมดที่สร้างขึ้นตลอดระยะเวลาการสร้างบิลด์และอินพุตของบิลด์ ตัวอย่างเช่น ไฟล์อินพุต ไฟล์เอาต์พุต เป้าหมาย และเป้าหมายที่มีการกำหนดค่าSkyKey
ชื่อสั้นๆ ที่เปลี่ยนไม่ได้สำหรับใช้อ้างอิงSkyValue
เช่นFILECONTENTS:/tmp/foo
หรือPACKAGE://foo
SkyFunction
สร้างโหนดตามคีย์และโหนดที่เกี่ยวข้อง- กราฟโหนด โครงสร้างข้อมูลที่มีความสัมพันธ์ของทรัพยากร Dependency ระหว่างโหนดต่างๆ
Skyframe
ชื่อโค้ดสำหรับเฟรมเวิร์กการประเมินส่วนเพิ่มที่ Bazel อิงตาม
การประเมิน
การบิลด์จะทำได้ด้วยการประเมินโหนดที่แสดงถึงคำขอบิลด์
ขั้นแรก Bazel ค้นหา SkyFunction
ที่สอดคล้องกับคีย์ของ SkyKey
ระดับบนสุด จากนั้นฟังก์ชันจะขอการประเมินโหนดที่ต้องใช้เพื่อประเมินโหนดระดับบนสุด ซึ่งส่งผลให้มีการเรียกใช้ SkyFunction
อื่นๆ จนกว่าจะถึงโหนด Leaf โหนด Leaf มักจะเป็นโหนดที่แสดง
ไฟล์อินพุตในระบบไฟล์ สุดท้าย Bazel ลงเอยด้วยค่าของ SkyValue
ระดับบนสุด ผลข้างเคียงบางอย่าง (เช่น ไฟล์เอาต์พุตในระบบไฟล์) และกราฟแบบวงกลมที่มีทิศทางของการอ้างอิงระหว่างโหนดที่เกี่ยวข้องในการสร้างบิลด์
SkyFunction
จะขอ SkyKeys
ในบัตรหลายใบได้ หากไม่สามารถบอกล่วงหน้าให้โหนดทั้งหมดที่จำเป็นต่อการทำงานของโหนดได้ ตัวอย่างง่ายๆ คือการประเมินโหนดไฟล์อินพุตที่กลายเป็นลิงก์สัญลักษณ์: ฟังก์ชันจะพยายามอ่านไฟล์และพบว่าเป็นลิงก์สัญลักษณ์ จึงดึงข้อมูลโหนดระบบไฟล์ที่แสดงถึงเป้าหมายของลิงก์สัญลักษณ์ แต่นั่นอาจเป็นลิงก์สัญลักษณ์ ซึ่งในกรณีนี้ ฟังก์ชันเดิมจะต้องดึงเป้าหมายด้วย
ฟังก์ชันจะแสดงในโค้ดโดยอินเทอร์เฟซ SkyFunction
และบริการที่มีให้โดยอินเทอร์เฟซที่เรียกว่า SkyFunction.Environment
สิ่งที่ฟังก์ชันสามารถทำได้มีดังนี้
- ขอรับการประเมินโหนดอื่นโดยการเรียกใช้
env.getValue
หากโหนดพร้อมใช้งาน ระบบจะแสดงผลค่า มิเช่นนั้น ระบบจะส่งคืนnull
และฟังก์ชันจะแสดงผลnull
ในกรณีหลัง โหนดที่ขึ้นต่อกันจะได้รับการประเมิน จากนั้นจะมีการเรียกใช้เครื่องมือสร้างโหนดเดิมอีกครั้ง แต่ในการเรียกenv.getValue
เดียวกันจะแสดงค่าที่ไม่ใช่null
- ขอรับการประเมินโหนดอื่นๆ หลายรายการโดยการเรียกใช้
env.getValues()
โดยพื้นฐานแล้ว ก็จะเหมือนกัน ยกเว้นว่าโหนดที่อ้างอิงจะได้รับการประเมินพร้อมกัน - คำนวณระหว่างการเรียกใช้
- อาจมีผลข้างเคียง เช่น การเขียนไฟล์ไปยังระบบไฟล์ ดังนั้นจึงควรระมัดระวังเนื่องจากการทำงานของ 2 อย่างที่ต่างกันจะหลีกเลี่ยงการชนกัน โดยทั่วไปแล้ว การเขียนผลข้างเคียง (ในกรณีที่มีข้อมูลไหลออกจาก Bazel) ก็ไม่เป็นไร ให้อ่านผลข้างเคียง (ในกรณีที่ข้อมูลเข้าสู่ Bazel โดยไม่มีทรัพยากร Dependency ที่จดทะเบียน) ไม่ได้ เนื่องจากเป็นทรัพยากร Dependency ที่ไม่ได้ลงทะเบียน ดังนั้นจึงอาจทำให้เกิดบิลด์ที่เพิ่มขึ้นอย่างไม่ถูกต้อง
การติดตั้งใช้งาน SkyFunction
ที่ทำงานได้ดีจะหลีกเลี่ยงการเข้าถึงข้อมูลด้วยวิธีอื่นนอกเหนือจากการขอทรัพยากร Dependency (เช่น โดยการอ่านระบบไฟล์โดยตรง) เนื่องจากจะทำให้ Bazel ไม่บันทึกทรัพยากร Dependency ของข้อมูลที่อ่านได้ ซึ่งทำให้การสร้างบิลด์เพิ่มขึ้นไม่ถูกต้อง
เมื่อฟังก์ชันมีข้อมูลเพียงพอที่จะทำงานแล้ว ฟังก์ชันควรแสดงผลค่าที่ไม่ใช่ null
ซึ่งระบุถึงการเสร็จสิ้น
กลยุทธ์การประเมินนี้มีประโยชน์หลายประการดังนี้
- ความห่างเหิน หากฟังก์ชันขอข้อมูลอินพุตโดยใช้เพียงโหนดอื่นๆ เท่านั้น Bazel ก็รับประกันได้ว่าหากสถานะอินพุตเหมือนกัน ก็จะแสดงผลข้อมูลเดียวกัน หากฟังก์ชัน Sky ทั้งหมดเป็นแบบกำหนดทิศทาง ก็หมายความว่าบิลด์ทั้งหมดจะมีการตัดสินใจด้วย
- ส่วนเพิ่มที่ถูกต้องและสมบูรณ์แบบ หากมีการบันทึกข้อมูลอินพุตทั้งหมดของฟังก์ชันทั้งหมด Bazel จะทำให้เฉพาะชุดโหนดที่ต้องทำให้ใช้งานไม่ได้เมื่อข้อมูลอินพุตเปลี่ยนแปลง
- การทำงานพร้อมกัน เนื่องจากฟังก์ชันต่างๆ จะโต้ตอบซึ่งกันและกันด้วยการขอทรัพยากร Dependency เท่านั้น ฟังก์ชันที่ไม่ได้พึ่งพากันและกันจึงสามารถเรียกใช้พร้อมกัน และ Bazel รับประกันได้ว่าผลลัพธ์ที่ได้จะเหมือนกับการเรียกใช้ตามลำดับ
ส่วนเพิ่ม
เนื่องจากฟังก์ชันสามารถเข้าถึงข้อมูลอินพุตโดยขึ้นอยู่กับโหนดอื่นๆ เท่านั้น Bazel สามารถสร้างกราฟโฟลว์ข้อมูลทั้งหมดจากไฟล์อินพุตไปยังไฟล์เอาต์พุต และใช้ข้อมูลนี้เพื่อสร้างโหนดใหม่เฉพาะที่ต้องสร้างใหม่เท่านั้น นั่นคือการปิดแบบทรานซิทีฟย้อนกลับของชุดไฟล์อินพุตที่มีการเปลี่ยนแปลง
โดยเฉพาะอย่างยิ่ง กลยุทธ์ส่วนเพิ่มที่เป็นไปได้ 2 แบบ ได้แก่ แบบจากล่างขึ้นบน และแบบจากบนลงล่าง แบบใดจะเหมาะสมที่สุดนั้นขึ้นอยู่กับลักษณะของกราฟการอ้างอิง
ในระหว่างการทำให้ไม่ถูกต้องจากล่างขึ้นบน หลังจากสร้างกราฟและทราบชุดอินพุตที่เปลี่ยนแปลงแล้ว โหนดทั้งหมดจะถูกทำให้ใช้งานไม่ได้ซึ่งจะขึ้นอยู่กับไฟล์ที่เปลี่ยนแปลง ซึ่งเหมาะสมอย่างยิ่งหากจะมีการสร้างโหนดระดับบนสุดเดียวกันอีกครั้ง โปรดทราบว่าการทำให้เป็นโมฆะจากล่างขึ้นบนจำเป็นต้องเรียกใช้
stat()
ในไฟล์อินพุตทั้งหมดของบิลด์ก่อนหน้าเพื่อพิจารณาว่ามีการเปลี่ยนแปลงหรือไม่ ซึ่งปรับปรุงได้โดยใช้inotify
หรือกลไกที่คล้ายกันในการเรียนรู้เกี่ยวกับไฟล์ที่มีการเปลี่ยนแปลงในระหว่างโมฆะจากบนลงล่าง ระบบจะเลือกการปิดทรานซิทีฟของโหนดระดับบนสุดและจะเก็บเฉพาะโหนดเหล่านั้นซึ่งมีการปิดทางอ้อมที่สะอาด ซึ่งดีกว่าหากกราฟโหนดมีขนาดใหญ่ แต่บิลด์ถัดไปต้องใช้ชุดย่อยขนาดเล็กเท่านั้น โมฆะจากล่างขึ้นบนจะทำให้กราฟขนาดใหญ่ของบิลด์แรกไม่ทำงาน ซึ่งต่างจากการใช้งานไม่ได้จากบนลงล่างซึ่งเดินไปตามกราฟขนาดเล็กของบิลด์ที่สอง
Bazel ใช้การระบุว่าเป็นโมฆะจากล่างขึ้นบนเท่านั้น
เพื่อให้ได้ส่วนเพิ่มเพิ่มเติม Bazel จะใช้การตัดทอนการเปลี่ยนแปลง หากโหนดไม่ถูกต้อง แต่เมื่อสร้างใหม่ จะพบว่าค่าใหม่เหมือนกับค่าเก่า โหนดที่ใช้งานไม่ได้เนื่องจากการเปลี่ยนแปลงในโหนดนี้จะได้รับการ "กู้คืน"
วิธีนี้มีประโยชน์ เช่น หากมีการเปลี่ยนแปลงความคิดเห็นในไฟล์ C++ ดังนั้นไฟล์ .o
ที่สร้างจากไฟล์ดังกล่าวจะเหมือนกัน จึงไม่จำเป็นต้องเรียก Linker อีกครั้ง
การลิงก์ที่เพิ่มขึ้น / การรวบรวม
ข้อจำกัดหลักของโมเดลนี้คือการที่โหนดใช้งานไม่ได้เป็นทุกอย่างหรือไม่ต้องมีเลย เมื่อมีการเปลี่ยนแปลงทรัพยากร Dependency โหนดที่ขึ้นต่อกันจะสร้างขึ้นใหม่ตั้งแต่ต้นเสมอ แม้ว่าจะมีอัลกอริทึมที่ดีกว่าซึ่งจะเปลี่ยนแปลงค่าเก่าของโหนดตามการเปลี่ยนแปลงดังกล่าวก็ตาม ตัวอย่างบางส่วนที่จะเป็นประโยชน์ ได้แก่
- การลิงก์เพิ่มเติม
- เมื่อมีการเปลี่ยนแปลงไฟล์คลาสเดียวในไฟล์ JAR คุณอาจแก้ไขไฟล์ JAR ในตำแหน่งเดิมแทนที่จะสร้างใหม่ทั้งหมดอีกครั้ง
เหตุผลที่ Bazel ไม่สนับสนุนหลักการเหล่านี้มีหลักการ มี 2 ข้อ ดังนี้
- ประสิทธิภาพเพิ่มขึ้นอย่างจำกัด
- ความยากในการตรวจสอบว่าผลลัพธ์ของการกลายพันธุ์นั้นเหมือนกับการสร้างใหม่ และค่าต่างๆ ของ Google ที่สร้างซ้ำได้ทีละบิต
ก่อนหน้านี้ คุณจะสร้างประสิทธิภาพที่ดีพอด้วยการแยกขั้นตอนการสร้างที่มีราคาแพงไป แล้วทำการประเมินใหม่บางส่วนก็เป็นได้ เช่น ในแอป Android คุณสามารถแยกชั้นเรียนทั้งหมดออกเป็นหลายๆ กลุ่มและถอดรหัสแยกกัน วิธีนี้หากไม่มีการเปลี่ยนแปลงคลาสในกลุ่มก็ไม่ต้องทำซ้ำ Dexing
การแมปกับแนวคิด Bazel
นี่เป็นการสรุประดับสูงของการติดตั้งใช้งาน SkyFunction
และ SkyValue
ที่ Bazel ใช้ในการดำเนินงานสร้าง
- FileStateValue ผลลัพธ์ของ
lstat()
สำหรับไฟล์ที่มีอยู่ ฟังก์ชันนี้จะคำนวณข้อมูลเพิ่มเติมเพื่อตรวจจับการเปลี่ยนแปลงของไฟล์ด้วย นี่คือโหนดระดับต่ำสุดในกราฟ Skyframe และไม่มีทรัพยากร Dependency - FileValue ไฟล์จะถูกใช้โดยตัวเลือกทั้งหมดที่สำคัญกับเนื้อหาจริงหรือเส้นทางที่แก้ไขแล้วของไฟล์ ขึ้นอยู่กับ
FileStateValue
ที่เกี่ยวข้องและลิงก์สัญลักษณ์ต่างๆ ที่ต้องได้รับการแก้ไข (เช่นFileValue
สำหรับa/b
ต้องมีเส้นทางที่แปลงแล้วของa
และเส้นทางที่แก้ไขแล้วของa/b
) ความแตกต่างระหว่างFileValue
กับFileStateValue
เป็นสิ่งสำคัญเพราะจะใช้กรณีหลังได้ในกรณีที่ไม่จำเป็นต้องใช้เนื้อหาของไฟล์ ตัวอย่างเช่น เนื้อหาไฟล์จะไม่เกี่ยวข้องเมื่อประเมิน GlOB ของระบบไฟล์ (เช่นsrcs=glob(["*/*.java"])
) - DirectoryListingStateValue ผลลัพธ์ของ
readdir()
เช่นเดียวกับFileStateValue
นี่เป็นโหนดระดับต่ำสุดและไม่มีทรัพยากร Dependency - DirectoryListingValue ใช้โดยทุกสิ่งที่คำนึงถึงรายการในไดเรกทอรี ขึ้นอยู่กับ
DirectoryListingStateValue
ที่เกี่ยวข้อง รวมถึงFileValue
ที่เชื่อมโยงของไดเรกทอรี - PackageValue แสดงเวอร์ชันที่แยกวิเคราะห์ของไฟล์ BUILD ขึ้นอยู่กับ
FileValue
ของไฟล์BUILD
ที่เกี่ยวข้อง และส่งผลโดยตรงต่อDirectoryListingValue
ที่ใช้แก้ไขข้อบกพร่องในแพ็กเกจ (โครงสร้างข้อมูลที่แสดงเนื้อหาภายในไฟล์BUILD
) - ConfiguredTargetValue แสดงเป้าหมายที่กำหนดค่าแล้ว ซึ่งเป็นชุดของชุดการดำเนินการที่สร้างขึ้นระหว่างการวิเคราะห์เป้าหมายและข้อมูลที่ให้ไว้กับเป้าหมายที่กำหนดค่าไว้แบบอิสระ ขึ้นอยู่กับ
PackageValue
เป้าหมายที่เกี่ยวข้อง,ConfiguredTargetValues
ของทรัพยากร Dependency โดยตรง และโหนดพิเศษที่แสดงถึงการกำหนดค่าบิลด์ - ArtifactValue แสดงไฟล์ในบิลด์ ไม่ว่าจะเป็นแหล่งที่มาหรืออาร์ติแฟกต์เอาต์พุต อาร์ติแฟกต์นั้นแทบจะเทียบเท่าไฟล์ และใช้เพื่ออ้างอิงไฟล์ระหว่างการดำเนินการในขั้นตอนการสร้างจริง ไฟล์ต้นฉบับจะขึ้นอยู่กับ
FileValue
ของโหนดที่เกี่ยวข้องและอาร์ติแฟกต์เอาต์พุตจะขึ้นอยู่กับActionExecutionValue
ของการดำเนินการใดก็ตามที่สร้างอาร์ติแฟกต์ - ActionExecutionValue หมายถึงการดำเนินการต่างๆ ขึ้นอยู่กับ
ArtifactValues
ของไฟล์อินพุต การทำงานที่แอปทำนั้นอยู่ใน SkyKey ซึ่งขัดแย้งกับแนวคิดที่ SkyKeys ควรมีขนาดเล็ก โปรดทราบว่าระบบจะไม่ใช้ActionExecutionValue
และArtifactValue
หากเฟสการดำเนินการไม่ทำงาน
แผนภาพนี้เป็นเครื่องมือช่วยเหลือแบบภาพ ซึ่งแสดงความสัมพันธ์ระหว่างการติดตั้งใช้งาน SkyFunction หลังจากที่มีการสร้างแอป Bazel เอง