การประเมินแบบคู่ขนานและโมเดลส่วนเพิ่มของ Bazel
โมเดลข้อมูล
โมเดลข้อมูลประกอบด้วยรายการต่อไปนี้
SkyValue
เรียกอีกอย่างว่าโหนดSkyValues
เป็นวัตถุที่เปลี่ยนแปลงไม่ได้ มีข้อมูลทั้งหมดที่สร้างขึ้นในช่วงบิลด์ และอินพุตของ งานสร้าง ตัวอย่างเช่น ไฟล์อินพุต ไฟล์เอาต์พุต เป้าหมาย และที่กำหนดค่าไว้ เป้าหมายSkyKey
ชื่อสั้นๆ ที่เปลี่ยนแปลงไม่ได้เพื่ออ้างอิงถึงSkyValue
เช่นFILECONTENTS:/tmp/foo
หรือPACKAGE://foo
SkyFunction
สร้างโหนดโดยอิงตามคีย์และโหนดที่อ้างอิง- กราฟโหนด โครงสร้างข้อมูลที่มีความสัมพันธ์แบบการขึ้นต่อกันระหว่าง
Skyframe
ชื่อโค้ดของเฟรมเวิร์กการประเมินส่วนเพิ่ม Bazel คือ อิงจาก
การประเมิน
บิลด์จะดำเนินการได้โดยการประเมินโหนดที่แสดงถึงคำขอบิลด์
ขั้นแรก Bazel จะค้นหา SkyFunction
ที่ตรงกับคีย์ของระดับบนสุด
SkyKey
จากนั้นฟังก์ชันจะขอการประเมินโหนดที่จำเป็น
ประเมินโหนดระดับบนสุด ซึ่งส่งผลให้มีการเรียก SkyFunction
อื่นๆ
จนกว่าจะถึงโหนดปลายสุด โหนด Leaf มักเป็นโหนดที่แสดงถึง
ในไฟล์ระบบไฟล์ สุดท้าย Bazel จบด้วยคุณค่าของ
SkyValue
ระดับบนสุด ผลข้างเคียงบางอย่าง (เช่น ไฟล์เอาต์พุตในไฟล์
) และกราฟแบบวนซ้ำแบบมีทิศทางของทรัพยากร Dependency ระหว่างโหนด
ที่เกี่ยวข้องกับบิลด์นี้
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 แบบ คือกลยุทธ์จากด้านล่างขึ้นบน และตัวเลือกจากบนลงล่าง วิธีใดจะเหมาะสมที่สุดนั้นขึ้นอยู่กับกราฟทรัพยากร Dependency ดูเหมือน
ระหว่างการทำ Bottom Up โมฆะ หลังจากสร้างกราฟและชุดการเปลี่ยนแปลง ทราบอินพุตแล้ว โหนดทั้งหมดจะใช้การไม่ได้ซึ่งขึ้นอยู่กับทางอ้อม ไฟล์ที่เปลี่ยนแปลง วิธีนี้เหมาะสมที่สุดหากจะสร้างโหนดระดับบนสุดเดียวกัน อีกครั้ง โปรดทราบว่าต้องมีการเรียกใช้
stat()
กับข้อผิดพลาดจากด้านล่างขึ้นบนทั้งหมด ไฟล์อินพุตของบิลด์ก่อนหน้าเพื่อพิจารณาว่ามีการเปลี่ยนแปลงหรือไม่ ช่วงเวลานี้ สามารถปรับปรุงได้โดยใช้inotify
หรือกลไกที่คล้ายกันเพื่อเรียนรู้เกี่ยวกับ ไฟล์ที่เปลี่ยนแปลงระหว่างการทำให้มุมมองบนลงล่างไม่ถูกต้อง การปิดโหนดระดับบนสุดแบบสับเปลี่ยน จะมีการตรวจสอบ และระบบจะเก็บไว้เฉพาะโหนดที่การปิดแบบทรานซิชันไม่มีความเรียบร้อย แบบนี้จะดีกว่าถ้ากราฟโหนดมีขนาดใหญ่ แต่บิลด์ถัดไปต้องใช้ ก็จะทำให้กราฟที่ใหญ่กว่านั้นไม่ถูกต้อง: การใช้ไม่ได้จากล่างขึ้นบน ของบิลด์แรก ซึ่งต่างจากการใช้งานไม่ได้จากบนลงล่าง ซึ่งเพียงแค่นำผู้ลงโฆษณา กราฟของบิลด์ที่ 2
Bazel ทำเพียงการลบล้างจากล่างขึ้นบนเท่านั้น
หากต้องการหาส่วนเพิ่มเพิ่มเติม Bazel จะใช้การตัดการเปลี่ยนแปลง: หากโหนดคือ เป็นโมฆะ แต่เมื่อสร้างใหม่ ก็พบว่าค่าใหม่เหมือนกัน เป็นค่าเดิม โหนดที่ใช้งานไม่ได้เนื่องจากการเปลี่ยนแปลงในโหนดนี้ กลับมา "ฟื้นคืนชีพ" แล้ว
ซึ่งจะเป็นประโยชน์ เช่น ถ้ามีการเปลี่ยนความคิดเห็นในไฟล์ C++:
ไฟล์ .o
ที่สร้างขึ้นจากไฟล์นี้จะเป็นไฟล์เดียวกัน จึงไม่จำเป็นต้องเรียก
Linker อีกครั้ง
การลิงก์ / การคอมไพล์ส่วนเพิ่ม
ข้อจำกัดหลักของโมเดลนี้คือ การใช้งานไม่ได้ของโหนดคือ ความสัมพันธ์ทั้งหมดหรือไม่เกี่ยวข้อง: เมื่อทรัพยากร Dependency เปลี่ยนแปลง โหนดที่อ้างอิงกันเสมอ สร้างใหม่ตั้งแต่ต้น แม้ว่าจะมีอัลกอริทึมที่ดีกว่าเดิมซึ่งจะเปลี่ยนแปลงไปก็ตาม ค่าเดิมของโหนดตามการเปลี่ยนแปลง ตัวอย่าง 2-3 ข้อที่ มีประโยชน์:
- การเพิ่มลิงก์
- เมื่อไฟล์คลาสเดี่ยวมีการเปลี่ยนแปลงในไฟล์ JAR อาจเป็นไปได้ แก้ไขไฟล์ JAR ในตำแหน่งเดิม แทนที่จะสร้างไฟล์ใหม่ตั้งแต่ต้น
เหตุผลที่ Bazel ไม่รองรับสิ่งเหล่านี้ตามหลักการ เป็น 2 เท่า ได้แก่
- มีประสิทธิภาพเพิ่มขึ้นเล็กน้อย
- ความยากในการตรวจสอบว่าผลลัพธ์ของการเปลี่ยนแปลงนั้นเหมือนกันหรือไม่ ของการสร้างใหม่อย่างชัดเจน และ Google ให้ค่ากับการสร้างที่เป็นแบบบิตต่อบิต ซ้ำกันได้
ที่ผ่านมา เราสามารถเพิ่มประสิทธิภาพที่ดีเพียงพอได้โดยการแยก โดยใช้ขั้นตอนการสร้างราคาแพง และได้รับการประเมินซ้ำบางส่วน ตัวอย่างเช่น ในแอป Android คุณสามารถแบ่งชั้นเรียนทั้งหมดเป็นหลายกลุ่มและ dex แยกกัน ด้วยวิธีนี้ หากชั้นเรียนในกลุ่มไม่มีการเปลี่ยนแปลง ส่วน 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 แสดงเป้าหมายที่กำหนดค่าแล้ว ซึ่งเป็น Tuple
ของชุดการกระทำที่สร้างขึ้นระหว่างการวิเคราะห์เป้าหมายและ
ข้อมูลที่ระบุให้กับเป้าหมายที่กำหนดค่าอ้างอิง ขึ้นอยู่กับ
PackageValue
เป้าหมายที่เกี่ยวข้องคือConfiguredTargetValues
ของทรัพยากร Dependency โดยตรง และโหนดพิเศษที่แสดงถึงบิลด์ การกำหนดค่า - ArtifactValue แสดงไฟล์ในบิลด์ ไม่ว่าจะเป็นแหล่งที่มาหรือ
อาร์ติแฟกต์เอาต์พุต อาร์ติแฟกต์แทบจะเทียบเท่ากับไฟล์และใช้เพื่อ
อ้างอิงถึงไฟล์ระหว่างการดำเนินการจริงในขั้นตอนบิลด์ ไฟล์ต้นฉบับ
ขึ้นอยู่กับ
FileValue
ของโหนดที่เกี่ยวข้องและอาร์ติแฟกต์เอาต์พุต ขึ้นอยู่กับActionExecutionValue
ของการดำเนินการใดก็ตามที่สร้าง อาร์ติแฟกต์ - ActionExecutionValue แสดงถึงการดำเนินการ ขึ้นอยู่กับ
ArtifactValues
ของไฟล์อินพุต การดำเนินการที่จะทำจะอยู่ในนั้น ภายใน SkyKey ของตน ซึ่งขัดแย้งกับแนวคิดที่ SkyKeys ควรจะเป็น มีขนาดเล็ก โปรดทราบว่าระบบจะไม่ได้ใช้ActionExecutionValue
และArtifactValue
หาก เฟสการดำเนินการจะไม่ทำงาน
แผนภาพนี้แสดงความสัมพันธ์ระหว่าง การติดตั้งใช้งาน SkyFunction หลังจากสร้าง Bazel ด้วยตนเอง