สกายเฟรม

รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.4 ที่ใช้เวลาเพียง 2 นาที 7.3 · 7.2 · 7.1 · 7.0 · 6.5

การประเมินแบบขนานและรูปแบบการเพิ่มขึ้นของ Bazel

โมเดลข้อมูล

โมเดลข้อมูลประกอบด้วยรายการต่อไปนี้

  • SkyValue เรียกอีกอย่างว่าโหนด SkyValues คือออบเจ็กต์แบบคงที่ซึ่งมีข้อมูลทั้งหมดที่สร้างในระหว่างการสร้างและอินพุตของการสร้าง ตัวอย่างเช่น ไฟล์อินพุต ไฟล์เอาต์พุต เป้าหมาย และที่กำหนดค่าไว้ เป้าหมาย
  • SkyKey ชื่อสั้นๆ ที่เปลี่ยนแปลงไม่ได้เพื่ออ้างอิงถึง SkyValue เช่น FILECONTENTS:/tmp/foo หรือ PACKAGE://foo
  • SkyFunction. สร้างโหนดตามคีย์และโหนดที่เกี่ยวข้อง
  • กราฟโหนด โครงสร้างข้อมูลที่มีความสัมพันธ์แบบการขึ้นต่อกันระหว่าง
  • Skyframe. ชื่อโค้ดของเฟรมเวิร์กการประเมินแบบเพิ่มที่ Bazel สร้างขึ้น

การประเมิน

บิลด์ประกอบด้วยการประเมินโหนดที่แสดงถึงคำขอบิลด์ (นี่คือสถานะที่เราต้องการ แต่มีโค้ดเดิมจำนวนมากอยู่ระหว่างดำเนินการ) ก่อนอื่น ระบบพบ SkyFunction และเรียกใช้ด้วยคีย์ของ SkyKey ระดับบนสุด จากนั้นฟังก์ชันจะขอการประเมินโหนดที่จําเป็นต่อการประเมินโหนดระดับบนสุด ซึ่งจะส่งผลให้มีการเรียกใช้ฟังก์ชันอื่นๆ และอื่นๆ ต่อไปจนกว่าจะถึงโหนดใบ (ซึ่งมักจะเป็นโหนดที่แสดงไฟล์อินพุตในระบบไฟล์) สุดท้าย เราจะได้รับค่าของ SkyValue ระดับบนสุด ผลข้างเคียงบางอย่าง (เช่น ไฟล์เอาต์พุตในระบบไฟล์) และกราฟแบบมีทิศทางแบบไม่วนซ้ำของความสัมพันธ์แบบพึ่งพาระหว่างโหนดที่เกี่ยวข้องในการสร้าง

SkyFunction สามารถขอ SkyKeys ในหลายบัตรได้หากบอกโหนดทั้งหมดที่จำเป็นต้องใช้ในการทำงานล่วงหน้าไม่ได้ ตัวอย่างง่ายๆ คือการประเมินโหนดไฟล์อินพุตที่กลายเป็นสัญลักษณ์ลิงก์: ฟังก์ชันพยายามอ่านไฟล์ พบว่าเป็นสัญลักษณ์ลิงก์ จึงดึงข้อมูลโหนดระบบไฟล์ที่แสดงถึงเป้าหมายของสัญลักษณ์ลิงก์ แต่ไฟล์นั้นอาจเป็น symlink ก็ได้ ซึ่งในกรณีนี้ ฟังก์ชันเดิมจะต้องดึงข้อมูลเป้าหมายด้วย

ฟังก์ชันจะแสดงเป็นโค้ดโดยอินเทอร์เฟซ SkyFunction และบริการที่มีให้โดยอินเทอร์เฟซที่เรียกว่า SkyFunction.Environment ฟังก์ชันทําสิ่งต่อไปนี้ได้

  • ขอการประเมินโหนดอื่นโดยการเรียกใช้ env.getValue หากโหนดพร้อมใช้งาน ระบบจะส่งคืนค่าของโหนด มิเช่นนั้นจะแสดงผล null และฟังก์ชันควรแสดงผล null ในกรณีหลัง โหนดที่อ้างอิงกันจะได้รับการประเมิน แล้วจะมีการเรียกใช้เครื่องมือสร้างโหนดเดิมอีกครั้ง แต่ครั้งนี้การเรียกใช้ env.getValue เดียวกันจะแสดงค่าที่ไม่ใช่ null
  • ขอการประเมินโหนดอื่นๆ หลายโหนดโดยเรียกใช้ env.getValues() ซึ่งจะทํางานแบบเดียวกัน ยกเว้นว่าระบบจะประเมินโหนดที่ขึ้นต่อกันแบบขนาน
  • ดำเนินการคํานวณระหว่างการเรียกใช้
  • มีผลข้างเคียง เช่น การเขียนไฟล์ไปยังระบบไฟล์ แต่ควรระวังเรื่องงาน 2 อย่างที่ว่านี้ไม่ได้เหยียบเท้ากัน โดยทั่วไปแล้ว การเขียนผลข้างเคียง (ในกรณีที่ข้อมูลไหลออกมาจาก Bazel) นั้นใช้ได้ อ่านผลข้างเคียง (ในกรณีที่ข้อมูลไหลเข้าสู่ Bazel โดยไม่ต้องมี Dependency ที่ลงทะเบียน) จะไม่มีผล เนื่องจากข้อมูลดังกล่าวเป็นทรัพยากร Dependency ที่ไม่ได้ลงทะเบียนและอาจทำให้เกิดการเพิ่มขึ้นที่ไม่ถูกต้อง

การใช้งาน SkyFunction ไม่ควรเข้าถึงข้อมูลด้วยวิธีอื่นๆ นอกเหนือจากการขอทรัพยากร Dependency (เช่น การอ่านระบบไฟล์โดยตรง) เนื่องจากจะทำให้ Bazel ไม่ลงทะเบียนการอ้างอิงข้อมูลในไฟล์ที่อ่าน ซึ่งส่งผลให้มีบิลด์ส่วนเพิ่มที่ไม่ถูกต้อง

เมื่อฟังก์ชันมีข้อมูลเพียงพอที่จะทํางานแล้ว ก็ควรแสดงผลค่าที่ไม่ใช่ null ซึ่งบ่งบอกว่าเสร็จสมบูรณ์

กลยุทธ์การประเมินนี้มีประโยชน์หลายประการดังนี้

  • ความสุจริต หากฟังก์ชันขอข้อมูลอินพุตโดยอาศัยโหนดอื่นๆ เท่านั้น Bazel จะรับประกันได้ว่าหากสถานะอินพุตเหมือนกัน ระบบจะแสดงผลข้อมูลเดียวกัน หากฟังก์ชันท้องฟ้าทั้งหมดเป็นแบบกำหนดได้ หมายความว่าทั้งบิลด์จะเป็นแบบกำหนดได้เช่นกัน
  • ส่วนเพิ่มที่ถูกต้องและสมบูรณ์แบบ หากบันทึกข้อมูลอินพุตทั้งหมดของฟังก์ชันทั้งหมดไว้ Bazel จะลบล้างได้เฉพาะชุดโหนดที่แน่นอนซึ่งจำเป็นต้องลบล้างเมื่อข้อมูลอินพุตมีการเปลี่ยนแปลง
  • การทำงานพร้อมกัน เนื่องจากฟังก์ชันต่างๆ สามารถโต้ตอบกันได้โดยการขอทรัพยากรที่เกี่ยวข้องเท่านั้น ฟังก์ชันที่ไม่ขึ้นอยู่กับกันจึงทำงานร่วมกันได้ และ Bazel รับประกันได้ว่าผลลัพธ์จะเหมือนกับการเรียกใช้ตามลำดับ

ส่วนเพิ่ม

เนื่องจากฟังก์ชันจะเข้าถึงได้เฉพาะข้อมูลอินพุตเท่านั้น โดยขึ้นอยู่กับโหนดอื่นๆ ทำให้ Bazel สร้างโฟลว์ข้อมูลที่สมบูรณ์จากไฟล์อินพุตไปยังไฟล์เอาต์พุต และใช้ข้อมูลนี้เพื่อสร้างโหนดใหม่เฉพาะที่ต้องสร้างใหม่เท่านั้น นั่นคือการปิดแบบย้อนกลับของชุดไฟล์อินพุตที่มีการเปลี่ยนแปลง

กล่าวอย่างเจาะจงคือ กลยุทธ์ส่วนเพิ่มที่เป็นไปได้มีอยู่ 2 กลยุทธ์ด้วยกัน ได้แก่ กลยุทธ์จากล่างขึ้นบนและกลยุทธ์จากบนลงล่าง ตัวเลือกใดที่เหมาะที่สุดนั้นขึ้นอยู่กับลักษณะของกราฟทรัพยากร Dependency

  • ในระหว่างการสร้างโมฆะแบบจากล่างขึ้นบน หลังจากที่สร้างกราฟและทราบชุดอินพุตที่มีการเปลี่ยนแปลงแล้ว โหนดทั้งหมดจะเป็นโมฆะซึ่งอาศัยไฟล์ที่มีการเปลี่ยนแปลงแบบสับเปลี่ยน ซึ่งเหมาะสําหรับกรณีที่เราทราบว่าจะมีการสร้างโหนดระดับบนสุดเดียวกันอีกครั้ง โปรดทราบว่าการทำให้โมเดลโมฆะจากล่างขึ้นบนต้องเรียกใช้ stat() ในไฟล์อินพุตทั้งหมดของบิลด์ก่อนหน้าเพื่อดูว่ามีการเปลี่ยนแปลงหรือไม่ ซึ่งคุณจะปรับปรุงได้โดยใช้ inotify หรือกลไกที่คล้ายกันเพื่อเรียนรู้เกี่ยวกับไฟล์ที่มีการเปลี่ยนแปลง

  • ในระหว่างการทำให้ข้อมูลไม่ถูกต้องจากบนลงล่าง ระบบจะตรวจสอบการปิดเชิงสื่อกลางของโหนดระดับบนสุดและเก็บเฉพาะโหนดที่การปิดเชิงสื่อกลางถูกต้องเท่านั้น วิธีนี้จะดีกว่าหากเราทราบว่ากราฟโหนดปัจจุบันมีขนาดใหญ่ แต่เราต้องการเพียงส่วนเล็กๆ ของกราฟโหนดในบิลด์ถัดไปเท่านั้น นั่นคือ การใช้ไม่ได้จากด้านล่างขึ้นบนจะทำให้กราฟที่ใหญ่กว่าของบิลด์แรกใช้งานไม่ได้ ซึ่งต่างจากการใช้จากด้านบนลงล่าง ซึ่งจะเป็นเพียงกราฟเล็กๆ ของบิลด์ที่ 2

ปัจจุบันเราทำให้ฟังก์ชันจากล่างขึ้นบนใช้ไม่ได้เท่านั้น

เราใช้การตัดการเปลี่ยนแปลงเพื่อให้ได้การเพิ่มประสิทธิภาพเพิ่มเติม หากโหนดหนึ่งๆ ใช้งานไม่ได้ แต่พบว่าค่าใหม่ของโหนดนั้นเหมือนกับค่าเดิมเมื่อสร้างใหม่ โหนดที่ใช้งานไม่ได้เนื่องจากการเปลี่ยนแปลงในโหนดนี้จะ "ฟื้นคืนชีพ"

ซึ่งจะเป็นประโยชน์ เช่น ในกรณีที่มีการเปลี่ยนแปลงความคิดเห็นในไฟล์ C++ ไฟล์ .o ที่สร้างขึ้นจากไฟล์นั้นจะยังคงเดิม เราจึงไม่จำเป็นต้องเรียก Linker อีก

การลิงก์/การคอมไพล์ที่เพิ่มขึ้น

ข้อจํากัดหลักของรูปแบบนี้คือ สถานะไม่ถูกต้องของโหนดจะเป็นแบบทั้งหมดหรือไม่มีเลย เมื่อมีการทําการเปลี่ยนแปลงทรัพยากร โหนดที่ขึ้นต่อกันจะสร้างขึ้นใหม่ตั้งแต่ต้นเสมอ แม้ว่าจะมีอัลกอริทึมที่ดีกว่าซึ่งจะเปลี่ยนค่าเดิมของโหนดตามการเปลี่ยนแปลงก็ตาม ตัวอย่างบางส่วนที่การดำเนินการนี้จะเป็นประโยชน์

  • การเพิ่มลิงก์
  • เมื่อไฟล์ .class ไฟล์เดียวมีการเปลี่ยนแปลงใน .jar ในทางทฤษฎี เราอาจแก้ไขไฟล์ .jar แทนการสร้างไฟล์ใหม่ตั้งแต่ต้นอีกครั้ง

เหตุผลที่ปัจจุบัน Bazel ยังไม่รองรับสิ่งเหล่านี้ตามหลักการ (เรามีมาตรการรองรับการลิงก์เพิ่มขึ้น แต่ไม่ได้นำไปใช้ภายใน Skyframe) มีอยู่ 2 อย่าง คือเรามีประสิทธิภาพเพิ่มขึ้นเพียงเล็กน้อยเท่านั้น และรับประกันไม่ได้ว่าผลลัพธ์ของการเปลี่ยนแปลงจะเหมือนกับการสร้างใหม่ที่ดูสะอาดตา และค่าของ Google ที่สร้างแบบบิตต่อบิตก็ทำซ้ำๆ ได้

จนถึงตอนนี้ เราคงประสิทธิภาพที่ดีเพียงพอได้เสมอโดยแยกขั้นตอนบิลด์ที่ราคาแพงและบรรลุผลการประเมินซ้ำบางส่วนด้วยวิธีดังกล่าว โดยจะแบ่งคลาสทั้งหมดในแอปออกเป็นหลายกลุ่ม และแยกคลาสต่างๆ แยกกัน ด้วยวิธีนี้ หากชั้นเรียนในกลุ่มไม่มีการเปลี่ยนแปลง ก็ไม่จำเป็นต้องทำซ้ำ

การแมปกับแนวคิดของ Bazel

นี่คือภาพรวมคร่าวๆ ของการใช้งาน SkyFunction บางส่วนที่ Bazel ใช้ในการสร้างบิลด์

  • FileStateValue ผลลัพธ์ของ lstat() สำหรับไฟล์ที่มีอยู่ เราจะคำนวณข้อมูลเพิ่มเติมเพื่อตรวจหาการเปลี่ยนแปลงในไฟล์ด้วย ซึ่งเป็นโหนดระดับต่ำสุดในกราฟ Skyframe และไม่มีข้อมูลที่ต้องพึ่งพา
  • FileValue ใช้สำหรับอะไรก็ได้ที่ให้ความสำคัญกับเนื้อหาจริงและ/หรือเส้นทางที่แก้ไขแล้วของไฟล์ ขึ้นอยู่กับ FileStateValue ที่เกี่ยวข้องและลิงก์สัญลักษณ์ที่ต้องแก้ไข (เช่น FileValue สำหรับ a/b ต้องใช้เส้นทางที่แก้ไขแล้วของ a และเส้นทางที่แก้ไขแล้วของ a/b) การแยกความแตกต่างระหว่าง FileStateValue นั้นสำคัญเนื่องจากในบางกรณี (เช่น การประเมินนิพจน์ทั่วไปของระบบไฟล์ (เช่น srcs=glob(["*/*.java"])) ไม่จำเป็นต้องใช้เนื้อหาของไฟล์
  • DirectoryListingValue ผลลัพธ์โดยประมาณของ readdir() ขึ้นอยู่กับ FileValue ที่เชื่อมโยงกับไดเรกทอรี
  • PackageValue แสดงเวอร์ชันที่แยกวิเคราะห์ของไฟล์ BUILD ขึ้นอยู่กับ FileValue ของไฟล์ BUILD ที่เชื่อมโยง และขึ้นอยู่กับ DirectoryListingValue ที่ใช้เพื่อแก้ไขนิพจน์ทั่วไปในแพ็กเกจ (โครงสร้างข้อมูลที่แสดงถึงเนื้อหาของไฟล์ BUILD ภายใน) ด้วย
  • ConfiguredTargetValue แสดงเป้าหมายที่กําหนดค่าไว้ ซึ่งเป็นทูเปิลของชุดการดําเนินการที่สร้างขึ้นระหว่างการวิเคราะห์เป้าหมายและข้อมูลที่ให้ไว้กับเป้าหมายที่กําหนดค่าไว้ซึ่งขึ้นอยู่กับเป้าหมายนี้ ขึ้นอยู่กับ PackageValue โดยมีเป้าหมายที่เกี่ยวข้อง, ConfiguredTargetValues ของทรัพยากร Dependency โดยตรง และโหนดพิเศษที่แสดงถึงการกำหนดค่าบิลด์
  • ArtifactValue แสดงไฟล์ในบิลด์ ไม่ว่าจะเป็นแหล่งที่มาหรืออาร์ติแฟกต์เอาต์พุต (อาร์ติแฟกต์เกือบจะเทียบเท่ากับไฟล์ และใช้สําหรับอ้างอิงไฟล์ในระหว่างการดําเนินการจริงของขั้นตอนการสร้าง) สำหรับไฟล์ต้นฉบับ FileValue จะขึ้นอยู่กับโหนดที่เชื่อมโยง ส่วนสำหรับอาร์ติแฟกต์เอาต์พุต ActionExecutionValue จะขึ้นอยู่กับการดำเนินการใดก็ตามที่สร้างอาร์ติแฟกต์
  • ActionExecutionValue. แสดงถึงการดำเนินการ ขึ้นอยู่กับ ArtifactValues ของไฟล์อินพุต การทำงานที่ดำเนินการอยู่นั้นอยู่ในคีย์ Sky ซึ่งขัดกับแนวคิดที่ว่า คีย์ Sky ควรมีขนาดเล็ก เรากำลังหาทางแก้ไขความคลาดเคลื่อนนี้ (โปรดทราบว่าระบบจะไม่ได้ใช้ ActionExecutionValue และ ArtifactValue หากเราไม่ได้เรียกใช้เฟสการดำเนินการบน Skyframe)