สกายเฟรม

รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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