คุณลักษณะความหมายของ Python (และภาษาไดนามิกอื่น ๆ ) มีส่วนทำให้ความช้าของมันคืออะไร?


26

ฉันไม่รู้จัก Python มาก ฉันพยายามที่จะเข้าใจอย่างแม่นยำมากขึ้นว่าคุณลักษณะที่แน่นอนของภาษาแบบไดนามิก (à la Python, Lua, Scheme, Perl, Ruby, .... ) กำลังบังคับให้การใช้งานของพวกเขาช้า

ในกรณีนี้ Lua 5.3 เครื่องจักร metatableจะทำให้ Lua ค่อนข้างสังหรณ์ใจ แต่ในทางปฏิบัติ Lua มีข่าวลือว่าค่อนข้างเร็ว (และเร็วกว่า Python)

นอกจากนี้ผมมีสัญชาตญาณ (อาจจะผิดอย่างใดอย่างหนึ่ง) ว่าตั้งแต่โปรเซสเซอร์ปัจจุบันหน่วยความจำได้ช้ากว่าการคำนวณดิบ (เข้าถึงหน่วยความจำแคชพลาดความต้องการในเวลาเดียวกันเป็นหลายร้อยดำเนินการทางคณิตศาสตร์), พิมพ์แบบไดนามิกการตรวจสอบ (เมนูif (value->type != INTEGER_TAG) return;ใน C parlance) สามารถวิ่งได้ค่อนข้างเร็ว

แน่นอนว่าการวิเคราะห์โปรแกรมทั้งหมด (เช่นการดำเนินการตามโครงการของสตาลิน ) สามารถทำให้การใช้ภาษาแบบไดนามิกเป็นนักแปลทำงานได้อย่างรวดเร็ว แต่ลองทำเป็นว่าฉันไม่มีเวลาออกแบบตัววิเคราะห์โปรแกรมทั้งหมดในตอนแรก

(ฉันกำลังออกแบบภาษาไดนามิกในจอภาพ MELTของฉันและบางส่วนจะถูกแปลเป็น C)



1
Lua Performance Tipsซึ่งอธิบายว่าทำไมบางโปรแกรม Lua จึงช้าและวิธีแก้ไข
Robert Harvey

คำตอบ:


24

คุณลักษณะความหมายของ Python (และภาษาไดนามิกอื่น ๆ ) มีส่วนทำให้ความช้าของมันคืออะไร?

ไม่มี.

ประสิทธิภาพของการใช้ภาษาเป็นฟังก์ชั่นของเงินทรัพยากรและวิทยานิพนธ์ระดับปริญญาเอกไม่ใช่คุณสมบัติทางภาษา ตัวเองมีพลังมากกว่า Smalltalk และมีพลังมากกว่า Python, Ruby, ECMAScript หรือ Lua เล็กน้อยและมี VM ที่มีประสิทธิภาพสูงกว่า VM ที่มีอยู่ทั้งหมด Lisp และ Smalltalk (ในความเป็นจริงแล้วการกระจายตัวเองมาพร้อมกับล่าม Smalltalk ขนาดเล็ก และแม้กระทั่งที่เร็วกว่า Smalltalk VMs ส่วนใหญ่ที่มีอยู่เดิม) และแข่งขันกับและบางครั้งก็เร็วกว่าการใช้งาน C ++ ในเวลานั้น

จากนั้นซันหยุดระดมทุนด้วยตนเองและ IBM, Microsoft, Intel และ Co. เริ่มระดมทุน C ++ และแนวโน้มกลับด้าน นักพัฒนา Self ออกจาก Sun เพื่อเริ่มต้น บริษัท ของตนเองโดยใช้เทคโนโลยีที่พัฒนาขึ้นสำหรับ Self VM เพื่อสร้างหนึ่งใน Smalltalk VM ที่เร็วที่สุดเท่าที่เคยมีมา (the Animorphic VM) จากนั้น Sun ก็ซื้อ บริษัท นั้นกลับคืนมา Smalltalk VM เป็นที่รู้จักกันดีในขณะนี้ภายใต้ชื่อ "HotSpot JVM" แดกดันโปรแกรมเมอร์ Java ดูถูกภาษาแบบไดนามิกสำหรับการ "ช้า" เมื่อในความเป็นจริงJavaช้าจนกว่าจะใช้เทคโนโลยีภาษาแบบไดนามิก (ใช่ถูกต้องแล้ว: HotSpot JVM นั้นเป็น Smalltalk VM ตัวตรวจสอบ bytecode ทำการตรวจสอบประเภทจำนวนมาก แต่เมื่อ bytecode ได้รับการยอมรับจากตัวตรวจสอบ VM และโดยเฉพาะอย่างยิ่งเครื่องมือเพิ่มประสิทธิภาพและ JIT ไม่ได้ทำจริง สนใจมากกับประเภทคงที่!)

CPython ไม่ได้ทำสิ่งต่างๆมากมายที่ทำให้ภาษาแบบไดนามิก (หรือมากกว่าการจัดส่งแบบไดนามิก) รวดเร็ว: การรวบรวมแบบไดนามิก (JIT), การเพิ่มประสิทธิภาพแบบไดนามิก, การอินไลน์แบบเก็งกำไร, การเพิ่มประสิทธิภาพแบบเก็งกำไร, การปรับตัวแบบปรับตัว นอกจากนี้ยังมีปัญหาที่แกนกลางและไลบรารีมาตรฐานเกือบทั้งหมดเขียนด้วย C ซึ่งหมายความว่าแม้ว่าคุณจะทำให้ Python 100x เร็วขึ้นในทันทีมันก็ไม่ช่วยอะไรคุณมากนักเพราะบางอย่างเช่นโค้ด 95% ที่ประมวลผลโดย โปรแกรม Python คือ C ไม่ใช่ Python หากทุกอย่างถูกเขียนด้วย Python แม้การเพิ่มความเร็วปานกลางจะสร้างผลกระทบถล่มซึ่งอัลกอริธึมจะเร็วขึ้นและโครงสร้างข้อมูลหลักจะเร็วขึ้น แต่แน่นอนว่าโครงสร้างข้อมูลหลักยังใช้ภายในอัลกอริธึมและอัลกอริธึมหลักและข้อมูลแกน โครงสร้างถูกนำมาใช้ทุกที่อื่น

มีสองสิ่งที่ไม่ดีสำหรับภาษา OO ที่จัดการโดยหน่วยความจำ (ไดนามิกหรือไม่) ในระบบปัจจุบัน การป้องกันหน่วยความจำเสมือนและหน่วยความจำเสมือนสามารถเป็นตัวฆ่าเพื่อประสิทธิภาพในการรวบรวมขยะโดยเฉพาะและประสิทธิภาพของระบบโดยทั่วไป และไม่จำเป็นอย่างสมบูรณ์ในภาษาที่ปลอดภัยสำหรับหน่วยความจำ: ทำไมจึงป้องกันการเข้าถึงหน่วยความจำที่ผิดกฎหมายเมื่อไม่มีหน่วยความจำเข้าถึงในภาษาเพื่อเริ่มต้นด้วย Azul ได้คิดเอาไว้ว่าจะใช้ MMU อันทรงพลังอันทันสมัย ​​(Intel Nehalem และที่ใหม่กว่าและเทียบเท่าของ AMD) เพื่อช่วยในการรวบรวมขยะแทนที่จะขัดขวางมัน แต่ถึงแม้ว่ามันจะได้รับการสนับสนุนจาก CPU ระบบย่อยหน่วยความจำปัจจุบันของระบบปฏิบัติการหลัก เพื่อให้สิ่งนี้ (ซึ่งเป็นสาเหตุที่ JVM ของ Azul ทำงานเสมือนจริงบนโลหะเปลือยนอกเหนือจากนั้น ระบบปฏิบัติการไม่ได้อยู่ภายใน)

ในโครงการ Singularity OS Microsoft ได้วัดผลกระทบของ ~ 30% ต่อประสิทธิภาพของระบบเมื่อใช้การป้องกัน MMU แทนระบบชนิดสำหรับการแยกกระบวนการ

อีกสิ่งหนึ่งที่ Azul สังเกตเห็นเมื่อสร้างจาวาซีพียูเฉพาะของพวกเขาก็คือซีพียูกระแสหลักที่ทันสมัยมุ่งเน้นไปที่สิ่งที่ผิดอย่างสมบูรณ์เมื่อพยายามลดต้นทุนของการพลาดแคช: พวกเขาพยายามลดจำนวนของแคชที่หายไปผ่านสิ่งต่าง ๆ เช่นการพยากรณ์สาขา และอื่น ๆ แต่ในโปรแกรม OO ที่มีรูปแบบหลากหลายรูปแบบการเข้าถึงนั้นเป็นแบบสุ่มหลอกไม่มีอะไรที่จะคาดเดาได้ ดังนั้นทรานซิสเตอร์เหล่านี้ทั้งหมดจึงสูญเปล่าและสิ่งที่เราควรทำแทนการลดต้นทุนของการแคชแต่ละครั้งที่พลาด (ค่าใช้จ่ายทั้งหมดคือค่า #misses * ค่าใช้จ่ายหลักพยายามที่จะลดระดับลงเป็นครั้งแรก Azul เป็นครั้งที่สอง) Java Compute Accelerator ของ Azul อาจมีแคชที่เกิดขึ้นพร้อมกัน 20000 และยังคงดำเนินต่อไป

เมื่อ Azul เริ่มต้นพวกเขาคิดว่าพวกเขาจะเอาส่วนประกอบ I / O ออกไปและออกแบบซีพียูคอร์เฉพาะของตัวเอง แต่สิ่งที่พวกเขาต้องการทำจริง ๆก็คือสิ่งที่ตรงกันข้าม: พวกเขาค่อนข้างใช้มาตรฐาน ชั้นวาง 3-address RISC core และออกแบบคอนโทรลเลอร์หน่วยความจำ MMU และแคชของตนเอง

tl; dr : "ความช้า" ของ Python ไม่ใช่คุณสมบัติของภาษา แต่ a) การนำไปใช้ที่ไร้เดียงสา (หลัก) และ b) ข้อเท็จจริงที่ว่าซีพียูและระบบปฏิบัติการสมัยใหม่ได้รับการออกแบบมาโดยเฉพาะเพื่อให้ C ทำงานได้อย่างรวดเร็ว สำหรับ C นั้นไม่ได้ช่วย (แคช) หรือแม้แต่ประสิทธิภาพการทำงานของ Python ที่ทำให้เจ็บปวด

และคุณสามารถแทรกภาษาที่มีการจัดการหน่วยความจำได้ด้วยโพลิมอร์ฟิซึมแบบ ad-hoc แบบไดนามิกที่นี่…เมื่อพูดถึงความท้าทายของการใช้งานอย่างมีประสิทธิภาพแม้ Python และ Java ก็เป็น "ภาษาเดียวกัน"


คุณมีลิงค์หรือข้อมูลอ้างอิงสำหรับ Azul หรือไม่?
Basile Starynkevitch

4
ฉันไม่เห็นด้วยว่าความหมายของภาษาไม่มีผลต่อความสามารถในการใช้งานอย่างมีประสิทธิภาพ ใช่การดำเนินงานที่ดีด้วยการเพิ่มประสิทธิภาพ JIT ชั้นแรกและการวิเคราะห์สามารถทำให้ใหญ่การปรับปรุงประสิทธิภาพการทำงานของภาษา แต่ในที่สุดมีลักษณะบางอย่างของความหมายที่หลีกเลี่ยงไม่ได้จะจบลงด้วยการเป็นคอขวด ไม่ว่าจะเป็นข้อกำหนดของ C สำหรับการใช้ชื่อแทนพอยน์เตอร์ที่เข้มงวดหรือข้อกำหนดของ Python ที่แสดงการดำเนินการทางอะตอมมีการตัดสินใจเชิงความหมายบางอย่างที่ไม่สามารถหลีกเลี่ยงประสิทธิภาพการทำงานของแอปพลิเคชั่นบางอย่างอย่างหลีกเลี่ยงไม่ได้
จูลส์

1
นอกเหนือจาก ... คุณมีการอ้างอิงสำหรับการปรับปรุง 30% สำหรับภาวะเอกฐานหรือไม่? ฉันเป็นผู้สนับสนุนระบบปฏิบัติการป้องกันทางภาษาเป็นเวลาหลายปี แต่ไม่เคยเห็นตัวเลขนั้นมาก่อนและพบว่ามันค่อนข้างน่าตกใจ (ตัวเลขที่ฉันเคยดูในอดีตนั้นใกล้ถึง 10%) และสงสัยว่าอะไร พวกเขาไม่ได้ที่จะได้รับการปรับปรุงมาก ...
จูลส์

5
@MasonWheeler: เพราะมีการใช้งาน Python เพียงเสี้ยววินาทีเท่านั้น ผู้ใช้ Python ไม่ได้ใช้เงินแม้แต่นิดเดียวผู้คนการวิจัยและทรัพยากรของ IBM, Sun, Oracle, Google และ Co. ที่ใช้ไปกับ J9, JRockit, HotSpot และ Co ทั้งหมด 5 Python ที่รวมกันอาจไม่ มีกำลังคนที่ Oracle ใช้จ่ายไปกับตัวเก็บขยะ IBM กำลังทำงานกับการใช้งาน Python บนพื้นฐานของ Eclipse OMR (เฟรมเวิร์ก VM แบบโอเพนซอร์สที่แยกจาก J9) ฉันยินดีที่จะเดิมพันว่าประสิทธิภาพจะดีตามลำดับ J9
Jörg W Mittag

2
สำหรับเร็กคอร์ดC ช้ากว่า Fortranสำหรับงานตัวเลขเนื่องจาก Fortran บังคับให้ใช้นามแฝงที่เข้มงวดเพื่อให้เครื่องมือเพิ่มประสิทธิภาพสามารถก้าวร้าวมากขึ้น
Michael Shopsin

8

ในขณะที่การใช้งานของ Python ในปัจจุบัน (ซึ่งขาดการปรับให้เหมาะสมโดยภาษาแบบไดนามิกอื่น ๆ เช่นการใช้งานจาวาสคริปต์ที่ทันสมัยและในขณะที่คุณชี้ให้เห็น Lua) เป็นแหล่งที่มาของปัญหาส่วนใหญ่ แต่ก็มีปัญหาทางความหมายบางอย่าง ยากสำหรับการนำไปใช้เพื่อแข่งขันกับภาษาอื่นอย่างน้อยในบางสาขา บางสิ่งที่มีค่าควรพิจารณาเป็นพิเศษ:

  • การกำหนดรายการและพจนานุกรมจำเป็นต้องใช้คำจำกัดความภาษาเพื่อให้เป็นแบบอะตอมมิก ซึ่งหมายความว่าเว้นแต่คอมไพเลอร์ JIT สามารถพิสูจน์ได้ว่าไม่มีการอ้างอิงไปยังวัตถุลิสต์ได้หลบหนีเธรดปัจจุบัน (การวิเคราะห์ที่ยากในหลาย ๆ กรณีและเป็นไปไม่ได้ในกรณีทั่วไป) ต้องแน่ใจว่าการเข้าถึงวัตถุนั้นเป็นอนุกรม (เช่น ผ่านการล็อค) การใช้งาน CPython ช่วยแก้ปัญหานี้โดยใช้ "Global interpreter lock" ที่มีชื่อเสียงซึ่งป้องกันการใช้รหัส Python ในสภาพแวดล้อมการประมวลผลแบบมัลติโพรเซสซิงโดยใช้เทคนิคแบบมัลติเธรดและในขณะที่วิธีแก้ปัญหาอื่น ๆ

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

  • Python มีevalฟังก์ชั่นซึ่งหมายความว่าคอมไพเลอร์ JIT ไม่สามารถตั้งสมมติฐานเกี่ยวกับการกระทำที่ไม่เกิดขึ้นแม้ว่าจะทำการวิเคราะห์ทั้งโปรแกรมตราบใดที่evalมีการใช้เพียงครั้งเดียว ตัวอย่างเช่นคอมไพเลอร์ไพ ธ อนไม่สามารถสันนิษฐานได้ว่าคลาสนั้นไม่มีคลาสย่อยและดังนั้นจึงเรียกการเรียกใช้เมธอดเสมือนจริงเนื่องจากการสันนิษฐานนั้นอาจถูกปฏิเสธในภายหลังผ่านการเรียกไปยังevalโทรเพราะสมมติฐานที่สามารถต่อมาจะเมื่อตะกี้ผ่านการเรียกไปยัง มันจะต้องทำการตรวจสอบประเภทแบบไดนามิกแทนเพื่อให้แน่ใจว่าสมมติฐานที่ทำโดยรหัสที่คอมไพล์มานั้นไม่ได้ใช้งานไม่ได้ก่อนที่จะทำการประมวลผลของรหัสนั้น


3
จุดหลังสามารถลดได้โดยการทevalริกเกอร์การคอมไพล์ใหม่และ / หรือการปรับให้เหมาะสม
Jörg W Mittag

4
อย่างไรก็ตามมันไม่ได้เป็นเอกลักษณ์ของ Python เลย Java (หรือค่อนข้าง JVM) มีการโหลดโค้ดแบบไดนามิกและการเชื่อมโยงแบบไดนามิกดังนั้นการวิเคราะห์ลำดับชั้นของคลาสนั้นเทียบเท่ากับการแก้ปัญหาการหยุดทำงานที่นั่นเช่นกัน กระนั้น HotSpot ชอบเก็งกำไร inlines วิธีการ polymorphic และถ้ามีอะไรบางอย่างในลำดับชั้นของชั้นเรียนมีการเปลี่ยนแปลงก็จะยกเลิกพวกเขากลับ
Jörg W Mittag
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.