Transcript คลิกที่นี่
Register Spilling การล้ นจากรี จิสเตอร์ Spilling (การล้นจากรี จิสเตอร์) • เกิดอะไรขึ ้นถ้ าทุกๆ node มี neighbor มากกว่า k • ตัวอย่างเช่นถ้ าเราต้ องการทา 3-coloring ของ RIG ด้ านล่างนี ้ Spilling (การล้นจากรี จิสเตอร์) • ถ้ าเราลบ node หนึง่ ออกไปตามขั ้นตอน heuristics เราจะไป ต่อไปได้ ดงั แสดงด้ วยรูป RIG ใหม่ต้านล่าง • ในกรณีนี ้เราจะทาการเลือก node ที่จะ spill – ตัวแปรที่ถกู spill ไปจะไป “lives” ในหน่วยความจาหลักแทน – ให้ วา่ เราเลือก f ที่จะต้ องถูก spill ไปที่หน่วยความจาหลัก Spilling (การล้นจากรี จิสเตอร์) • หลังจากที่เรา spill f แล้ ว เราสามารถกลับไปใช้ heuristic ได้ เหมือนเดิม (ดูจากภาพ RIG ด้ านล่าง) Spilling (การล้นจากรี จิสเตอร์) • ในท้ ายที่สดุ เราก็จะต้ องให้ คา่ สีกบั f โดยความคาดหวังของเราก็ คือจาก neighbor ทัง้ 4 ของ f นัน้ บางทีอาจจะมีสีที่ใช้ แตกต่าง กัน น้ อยกว่า 3 สีก็เป็ นได้ Spilling • ในกรณีนี ้ไม่เป็ นเช่นที่เราคาดหวัง – เราจะต้ องทาการจองตาแหน่งในหน่วยความจาหลักให้ กบั f • ส่วนใหญ่ใช้ พื ้นที่บน stack และจัดให้ อยูใ่ น AR • เรี ยกตาแหน่งนี ้ว่า fa • ในกรณีที่มีการใช้ f ให้ เพิ่มคาสัง่ load เพื่อนาข้ อมูลเข้ าเข้ ามาที่ f f := load fa • ในกรณีที่มีการ assign ค่าให้ f (การ write ไปที่ f) ให้ ใส่คาสัง่ store ต่อไปนี ้ store f, fa ตัวอย่าง Spilling • โค๊ ดใหม่หลังจากการ spill ตัวแปร f ค่า Liveness เดิม คานวณ Liveness ใหม่ คานวณ Liveness ใหม่ • ข้ อมูล liveness ที่คานวณได้ ใหม่มีความใกล้ เคียงกับ liveness เดิม มาก (สาหรับ CFG ที่ไม่มีการ spill) • f จะ live ในช่วงต่อไปนี ้ – ระหว่าง f := load fa และคาสัง่ ที่อยูถ่ ดั ไป – ระหว่าง f := load fa และคาสัง่ ที่อยูก่ ่อนหน้ านี ้ • การทา spilling ลดช่วงชีวิต (live range) ของ f – เพราะฉะนันท ้ าให้ interference (การทับหรื อรบกวนกันของตัวแปร) ลดลง – ซึง่ ทาให้ node ของ f ใน RIG มี neighbor ที่น้อยลง คานวณ RIG ใหม่หลังจาก Spilling • Edge หลังจากที่ node ที่ทาการ spill จะถูกกาจัดออกจาก RIG เดิม • ตัวแปร f live range ยังทับซ้ อนกับ live range ของ c และ d • ได้ RIG ใหม่ที่สามารถใช้ เพียง 3 สีในการให้ สี RIG นี ้ได้ (3-colorable) การตัดสิ นใจเกี่ยวกับ Spilling • ในบางครัง้ เราอาจจะต้ อง spill มากกว่าหนึ่งครัง้ ก่อนที่จะทา coloring จากจานวนสีที่กาหนดให้ อย่างจากัดได้ • เราสามารถเลือกที่จะ spill ตัวแปรตัวใดก็ได้ อัลกอริ ทมึ ที่กล่าวมายังคง ทางานได้ ถกู ต้ อง • แต่การเลือกที่ดีจะช่วยเพิ่มประสิทธิภาพของโค๊ ด assembly ที่ผลิลิตได้ – มี heuristics ที่ใช้ ในการเลือกต่อไปนี ้: • Spill ตัวแปรที่ทาให้ เกิด interference มากที่สดุ (นัน่ คือมี neighbor มากที่สดุ ) • Spill ตัวแปรที่ใช้ งานน้ อย • หลีกเลี่ยงการ spill ใน inner loop ประเด็นหลักเกี่ยวกับ Register Allocation • คอมไพเลอร์ ในปั จจุบนั ทา optimization อันนี ้ทังนั ้ น้ – เพราะสมรรถนะที่แตกต่างกันมากระหว่าง CPU กับ memory การคานวณที่ เกี่ยวข้ องกับตัวแปรที่อยูใ่ นรี จิสเตอร์ จะทาได้ เร็วกว่าตัวแปรที่อยู่ใน memory มาก – การผลิลิตโค๊ ดในสไตล์ stack machine จะมีสมรรถนะต่ามากเพราะมีการ โต้ ตอบกับ memory มาก • การทา register allocation สาหรับ CISC จะยุง่ ยากมากกว่า RISC – รี จิสเตอร์ แต่ละตัวมีสิ่งที่พิเศษเฉพาะตัว อาจจะไม่สามารถนามาใส่ตวั แปรบางชนิด ในทุกๆกรณีได้ ลาดับชั้นของระบบหน่วยความจา (Memory Hierarchy) การจัดการหน่วยความจาลาดับชั้น • ภาพของหน่วยความจาที่โปรแกรมเมอร์ เห็นคือที่เก็บข้ อมูลที่มีตาแหน่งติดกันและมี ความจุเป็ นอนันต์และมีประสิทธิภาพสูง (Virtual Memory) • CPU OS และคอมไพเลอร์ จะทาหน้ าที่จดั การลาดับชันของหน่ ้ วยความจาเพื่อให้ ภาพในอุดมคตินนกั ั ้ บโปรแกรมเมอร์ – ในโลกความเป็ นจริ งลาดับชันของหน่ ้ วยความจามีความจากัดทางด้ านความจุและ สมรรถนะ – CPU OS และ คอมไพเลอร์ จะต้ อง “ซ่อน” ข้ อจากัดนี ้และ “หลอก” โปรแกรมเมอร์ ให้ มองเห็นแต่ในโลกเสมือน • CPU จัดการในส่วนที่เกี่ยวข้ องกับ main memory และ cache • คอมไพเลอร์ จดั การในส่วนการบริ หารรี จิสเตอร์ และการติดต่อกับ main memory • OS จัดการในส่วนที่เกี่ยวข้ องกับ main memory และ disc Cache • คอมไพเลอร์ เก่งเรื่ องการจัดการรี จิสเตอร์ • ถ้ าเราต้ องการให้ คอมไพเลอร์ ช่วย CPU ในการจัดการ cache – ทาได้ ระดับหนึง่ แต่คอมไพเลอร์ ไม่เก่งมาก – หลายๆอย่างโปรแกรมเมอร์ ต้องออกมาจากโลกเสมือนเพื่อช่วยคอมไพเลอร์ – เป็ นหนึง่ ในแนวทางการวิจยั ทางด้ านคอมไพเลอร์ ในปั จจุบนั การ Optimize การใช้ Cache • พิจารณา loop ด้ านล่างนี ้ for(j := 1; j < 10; j++) for(i=1; i<10000000; i++) a[i] *= b[i] • Loop นี ้มีประสิทธิภาพต่าเพราะมีการใช้ งาน cache อย่างไม่มี ประสิทธิภาพ • ทุกๆการอ้ างถึงค่าใน a[i] หรื อ b[i] เป็ นการ miss ทังหมด ้ (ให้ วา่ cache block มีขนาดเท่ากับหนึง่ element ของ a และ b) การ Optimize การใช้ Cache • พิจารณาการเขียนโปรแกรมใหม่จากสไลด์ที่แล้ วดังนี ้: for(i=1; i<10000000; i++) for(j := 1; j < 10; j++) a[i] *= b[i] – ให้ ผลิลลัพธ์เหมือนกันแต่สมรรถนะของโปรแกรมนี ้สูงขึ ้นมาก – การใช้ ประโยชน์จาก cache ทาได้ สงู สุด – สมรรถนะที่เพิ่มขึ ้นอาจจะมากกว่า 10 เท่า • Optimization ในลักษณะนี ้ (เรี ยกว่า loop interchange) คอมไพเลอร์ สามารถทาได้ – อาจจะต้ องทาในระดับ high-level IR