สกายเฟรม

รายงานปัญหา ดูแหล่งที่มา

การประเมินพร้อมกันและโมเดลส่วนเพิ่มของ 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 เอง

กราฟความสัมพันธ์ในการใช้งาน SkyFunction