คอมไพเลอร์อย่าง Javac ตรวจจับฟังก์ชันบริสุทธิ์โดยอัตโนมัติและทำให้เป็นคู่ขนานหรือไม่?


12

ฟังก์ชั่นที่บริสุทธิ์เป็นที่รู้จักกันเพื่ออำนวยความสะดวกในการ parellelizing มันเกี่ยวกับฟังก์ชั่นการเขียนโปรแกรมที่ทำให้มันปรับให้เข้ากับการประมวลผลแบบขนานโดยเนื้อแท้คืออะไร?

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


7
คำถามนี้ไม่เพียง แต่คอมไพเลอร์สามารถรู้ได้ว่าฟังก์ชั่นนั้นบริสุทธิ์หรือไม่ ไม่เพียงพอที่จะปิดไฟเธรดใหม่สำหรับแต่ละเธรด: สิ่งนี้ไม่มีประสิทธิภาพ GHC (Haskell) เกี่ยวข้องกับเรื่องนี้โดยใช้ความเกียจคร้านและ "กระทู้สีเขียว"; ฉันจะแปลกใจอย่างแท้จริงหากมีการลองใช้ภาษาที่ไม่บริสุทธิ์ด้วยความยากลำบากเพิ่มเติมในการตรวจสอบให้แน่ใจว่ามีการกำหนดเธรดบริสุทธิ์อย่างถูกต้องตามความเหมาะสมกับเธรดหลักที่ไม่บริสุทธิ์
Ryan Reich

@RyanReich มีการเพิ่มประสิทธิภาพของการใช้ฟังก์ชั่นการเขียนโปรแกรมในภาษาการทำงานที่ไม่บริสุทธิ์เช่น Java? ได้ประโยชน์จากการเขียนโปรแกรมฟังก์ชั่นการทำงานอย่างหมดจดเช่น modularity?
Naveen

@RyanReich GHC จัดการกับปัญหาโดยให้โปรแกรมเมอร์ทำหมายเหตุประกอบเมื่อพวกเขาต้องการความเท่าเทียม ความบริสุทธิ์หมายถึงว่าคำอธิบายประกอบเหล่านี้ไม่เคยเปลี่ยนความหมายเพียงแค่ประสิทธิภาพ (นอกจากนี้ยังมีกลไกการทำงานพร้อมกันที่สามารถก่อให้เกิดความเท่าเทียมกัน แต่นี่คือปลากาต้มน้ำที่แตกต่างกัน)
Derek Elkins ซ้าย SE

@ Naveen มีประโยชน์อื่น ๆ สำหรับฟังก์ชั่นที่บริสุทธิ์เกี่ยวกับการเพิ่มประสิทธิภาพนอกเหนือจากการขนานกันเช่นรหัส reordering อิสระมากขึ้นบันทึกและการกำจัด subexpression ทั่วไป ฉันอาจจะผิด แต่ฉันสงสัยว่า javac พยายามที่จะตรวจจับความบริสุทธิ์แม้ว่ามันอาจจะค่อนข้างหายากในรหัสสำนวนและค่อนข้างยากสำหรับทุกคน แต่เป็นกรณีที่น่ารำคาญที่สุด ตัวอย่างเช่นคุณจำเป็นต้องรู้ว่าจะไม่มีการใด ๆNullPointerExceptions ประโยชน์ของการปรับให้เหมาะสมตามสิ่งนี้อาจมีขนาดค่อนข้างเล็กสำหรับแอปพลิเคชัน Java ทั่วไป
Derek Elkins ออกจาก SE

6
javac เป็นคอมไพเลอร์ภาษาจาวาซึ่งใช้ซอร์สโค้ดจาวาและสร้างไฟล์คลาสจาวาโค้ดไบต์ มันค่อนข้าง จำกัด ว่าจะทำอะไรได้บ้าง (และควรจะทำ) มันไม่มีเสรีภาพหรือกลไกพื้นฐานที่จำเป็นสำหรับการแนะนำการขนานในไฟล์คลาสโค้ดไบต์
Erik Eidt

คำตอบ:


33

คอมไพเลอร์เช่น Javac ฉลาดพอที่จะตรวจจับเมื่อเมธอดเป็นฟังก์ชันที่บริสุทธิ์

มันไม่ใช่คำถามของ "ฉลาดพอ" สิ่งนี้เรียกว่าการวิเคราะห์ความบริสุทธิ์และไม่สามารถพิสูจน์ได้ในกรณีทั่วไป: เทียบเท่ากับการแก้ปัญหาการหยุดชะงัก

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

และแม้แต่ในกรณีที่ใช้งานได้อัลกอริธึมก็ซับซ้อนและมีราคาแพง

ดังนั้นนั่นคือปัญหา # 1: ใช้งานได้สำหรับกรณีพิเศษเท่านั้น

ปัญหา # 2: ห้องสมุด เพื่อให้ฟังก์ชั่นมีความบริสุทธิ์มันสามารถเรียกฟังก์ชั่นที่บริสุทธิ์เท่านั้น (และฟังก์ชั่นเหล่านั้นสามารถเรียกฟังก์ชั่นที่บริสุทธิ์และอื่น ๆ เป็นต้น) เห็นได้ชัดว่า Javac รู้เพียงเกี่ยวกับ Java และรู้เพียงเกี่ยวกับโค้ดที่สามารถมองเห็นได้ ดังนั้นหากฟังก์ชันของคุณเรียกใช้ฟังก์ชันในหน่วยการคอมไพล์อื่นคุณไม่สามารถรู้ได้ว่ามันบริสุทธิ์หรือไม่ ถ้ามันเรียกฟังก์ชันที่เขียนด้วยภาษาอื่นคุณไม่รู้หรอก ถ้ามันเรียกฟังก์ชั่นในห้องสมุดที่อาจยังไม่ได้ติดตั้งคุณไม่สามารถรู้ได้ และอื่น ๆ

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

ปัญหา # 3: การกำหนดเวลา เมื่อคุณทราบว่าส่วนใดบริสุทธิ์แล้วคุณยังต้องจัดตารางเวลาเพื่อแยกเธรด หรือไม่. การเริ่มต้นและการหยุดเธรดมีราคาแพงมาก (โดยเฉพาะใน Java) แม้ว่าคุณจะเก็บเธรดพูลและไม่เริ่มหรือหยุดเธรดการสลับบริบทเธรดก็มีราคาแพงเช่นกัน คุณต้องแน่ใจว่าการคำนวณจะทำงานได้นานกว่าเวลาที่กำหนดไว้และการสลับบริบทไม่เช่นนั้นคุณจะสูญเสียประสิทธิภาพไม่ได้รับ

ตามที่คุณอาจเดาได้ในตอนนี้การหาระยะเวลาในการคำนวณจะเป็นไปไม่ได้ในกรณีทั่วไป (เราไม่สามารถคิดได้ว่าจะใช้เวลาจำนวน จำกัด เวลาเพียงอย่างเดียว) และหนักและแพงแม้ใน กรณีพิเศษ

นอกเหนือ: Javac และการเพิ่มประสิทธิภาพ โปรดทราบว่าการใช้งานส่วนใหญ่ของ javac ไม่ได้ทำการปรับแต่งมากมาย การดำเนินงานของ Oracle javac ตัวอย่างเช่นอาศัยอยู่กับเครื่องยนต์ต้นแบบที่จะทำเพิ่มประสิทธิภาพ สิ่งนี้นำไปสู่ปัญหาอีกชุดหนึ่งนั่นคือพูดว่า javac ตัดสินใจว่าฟังก์ชั่นเฉพาะนั้นบริสุทธิ์และมีราคาแพงพอและมันก็คอมไพล์มันให้ทำงานบนเธรดอื่น จากนั้นเครื่องมือเพิ่มประสิทธิภาพของแพลตฟอร์ม (เช่นคอมไพเลอร์ HotSpot C2 JIT) มาพร้อมและปรับฟังก์ชั่นทั้งหมดออกไป ตอนนี้คุณมีเธรดว่างเปล่าที่ไม่ทำอะไรเลย หรือลองจินตนาการอีกครั้งว่า javac ตัดสินใจที่จะกำหนดเวลาฟังก์ชั่นในเธรดอื่นและเครื่องมือเพิ่มประสิทธิภาพแพลตฟอร์มสามารถทำได้ ปรับให้เหมาะที่สุดอย่างสมบูรณ์ยกเว้นว่าไม่สามารถทำการอินไลน์ข้ามขอบเขตของเธรดและดังนั้นฟังก์ชันที่สามารถปรับให้เหมาะสมที่สุดได้นั้นจะถูกดำเนินการโดยไม่จำเป็น

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

โปรดทราบว่าตัวอย่างเช่นคอมไพเลอร์ HotSpot C2 JIT จะดำเนินการ auto-vectorization บางอย่างซึ่งยังเป็นรูปแบบของการขนานอัตโนมัติ


ดีขึ้นอยู่กับคำจำกัดความของคุณของ "ฟังก์ชั่นบริสุทธิ์" การใช้งานฟังก์ชั่นไม่บริสุทธิ์ในการดำเนินการอาจได้รับอนุญาต
Deduplicator

@Dupuplicator ดีขึ้นอยู่กับคำนิยามของdefinitionคุณโดยใช้ความแตกต่างdefinitionของpurityอาจจะคลุมเครือ
แมว

1
ปัญหาของคุณ # 2 ส่วนใหญ่จะถูกยกเลิกโดยความจริงที่ว่าการเพิ่มประสิทธิภาพทั้งหมดได้รับการดำเนินการโดย JIT (คุณรู้อย่างชัดเจน แต่ไม่สนใจ) ปัญหาที่คล้ายกัน # 3 ได้รับการรับรองความถูกต้องบางส่วนเนื่องจาก JIT อาศัยสถิติที่รวบรวมโดยล่ามเป็นอย่างมาก ฉันไม่เห็นด้วยโดยเฉพาะอย่างยิ่งกับ "คุณไม่สามารถใช้ห้องสมุดใด ๆ " เนื่องจากมีการเพิ่มประสิทธิภาพการช่วยเหลือ ฉันยอมรับว่าความซับซ้อนที่เพิ่มเข้ามาจะเป็นปัญหา
maaartinus

2
@ maaartinus: นอกจากนี้เฉพาะคำตอบสุดท้ายของฉันเท่านั้นที่เฉพาะกับ javac ฉันเฉพาะทำเอ่ยถึงเช่นว่า "นี้จะทำงานเฉพาะเมื่อคุณมีการวิเคราะห์ทั้งโปรแกรมเมื่อโปรแกรมทั้งหมดที่เขียนในภาษาเดียวกันและทั้งหมดจะถูกรวบรวมในครั้งเดียวในหนึ่งไป." เห็นได้ชัดว่าเป็นจริงสำหรับ C2: มันเกี่ยวข้องกับภาษาเดียวเท่านั้น (JVM bytecode) และสามารถเข้าถึงโปรแกรมทั้งหมดได้ในครั้งเดียว
Jörg W Mittag

1
@ JörgWMittagฉันรู้ว่า OP ถามเกี่ยวกับ javac แต่ฉันคิดว่าพวกเขากำลังสันนิษฐานว่า javac เป็นสิ่งที่รับผิดชอบในการเพิ่มประสิทธิภาพ และพวกเขาแทบไม่รู้ว่ามี C2 ฉันไม่ได้พูดคำตอบของคุณไม่ดี เป็นเพียงการให้ javac ทำการเพิ่มประสิทธิภาพใด ๆ (ยกเว้นเรื่องเล็ก ๆ น้อย ๆ เช่นการใช้งานStringBuilder) นั้นไม่มีความรู้สึกดังนั้นฉันจึงยกเลิกมันและเพียงแค่สมมติว่า OP เขียน javac แต่หมายถึง Hotspot ปัญหาของคุณ # 2 เป็นเหตุผลที่ดีในการต่อต้านสิ่งใดใน javac
maaartinus

5

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

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

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

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

ดังนั้นคำตอบสำหรับคำถามของคุณคือไม่คอมไพเลอร์จะไม่ "ฉลาด" พอที่จะทำให้ฟังก์ชันบริสุทธิ์ขนานกันโดยอัตโนมัติโดยเฉพาะอย่างยิ่งในโลก Java พวกมันฉลาดด้วยการไม่ทำให้ขนานกันโดยอัตโนมัติ!


5
" พวกเขาฉลาดโดยไม่ทำให้ขนานกันโดยอัตโนมัติ! " : นี่ไปไกลเกินไป ในขณะที่มันเป็นความจริงที่การขนานในทุกจุดที่เป็นไปได้เพียงเพื่อประโยชน์ของตัวเองโดยทั่วไปจะไม่มีประสิทธิภาพคอมไพเลอร์สมาร์ทจะระบุกลยุทธ์การขนานที่ใช้งานได้จริง ฉันคิดว่าคนส่วนใหญ่เข้าใจสิ่งนี้ดังนั้นเมื่อเราพูดถึงการทำให้เป็นคู่ขนานอัตโนมัติเราหมายถึงการขนานอัตโนมัติที่ใช้ได้จริง
Nat

@Nat: ขันยากเกินไป สิ่งนี้จะต้องมีการระบุฟังก์ชั่นบริสุทธิ์ในระดับรันไทม์ของ 100 วินาทีของมิลลิวินาทีและคาดว่าคอมไพเลอร์ที่จะได้รับความคิดของรันไทม์ของลูปที่ไม่มีค่าคงที่ในการทำซ้ำของพวกเขา (และกรณีที่คุณต้องการไม่)
Joshua

ฉันเห็นด้วย - ความคิดเห็นของ @ Nat บอกเป็นนัยว่าการขนานไม่จำเป็นต้องหมายถึงหลายเธรดซึ่งเป็นความจริง ตัวอย่างเช่น JIT สามารถ inline การเรียกหลาย ๆ ครั้งไปยังฟังก์ชั่น pure และแทรกคำสั่ง CPU ของตนในบางกรณี ตัวอย่างเช่นถ้าทั้งสองวิธีเรียกใช้ค่าคงที่ก็สามารถดึงหนึ่งครั้งและเก็บไว้ในการลงทะเบียน CPU สำหรับทั้งสองอินสแตนซ์ของวิธีการที่จะใช้ CPU สมัยใหม่เป็นสัตว์ร้ายที่มีการลงทะเบียนวัตถุประสงค์ทั่วไปจำนวนมากและคำแนะนำเฉพาะที่มีประโยชน์มากเมื่อปรับรหัสให้เหมาะสม

1
@Joshua: ง่ายกว่าสำหรับคอมไพเลอร์ JIT แน่นอน คอมไพเลอร์ของ JIT ยังสามารถเข้าใจได้ว่าฟังก์ชั่นอาจไม่บริสุทธิ์ แต่ไม่มีการเรียกใด ๆ ที่ทำให้เกิดพฤติกรรมที่ไม่บริสุทธิ์
gnasher729

ฉันเห็นด้วยกับ @Joshua ฉันมีอัลกอริทึมที่ยากต่อการขนาน ฉันได้พยายามทำให้มันขนานกันด้วยตนเองแม้จะทำการประมาณที่ง่ายขึ้น (และการปรับเปลี่ยนอัลกอริทึม) และล้มเหลวอย่างน่าสังเวชทุกครั้ง แม้แต่โปรแกรมที่บอกว่ามันเป็นไปได้ที่จะขนานสิ่งที่ยากมากถึงแม้ว่ามันจะง่ายกว่าการขนานมันจริงๆ จำไว้ว่าเรากำลังพูดถึงภาษาโปรแกรมทัวริงที่สมบูรณ์
juhist
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.