JIT กับ Static Compiler
ดังที่ได้กล่าวไปแล้วในโพสต์ก่อนหน้านี้ JIT สามารถรวบรวม IL / bytecode เป็นโค้ดเนทีฟได้ที่รันไทม์ ค่าใช้จ่ายนั้นถูกกล่าวถึง แต่ยังไม่สรุป:
JIT มีปัญหาใหญ่อย่างหนึ่งคือไม่สามารถรวบรวมทุกอย่างได้: การคอมไพล์ JIT ต้องใช้เวลาดังนั้น JIT จะรวบรวมโค้ดเพียงบางส่วนในขณะที่คอมไพเลอร์แบบคงที่จะสร้างไบนารีเนทีฟแบบเต็ม: สำหรับโปรแกรมบางประเภทสแตติก คอมไพเลอร์จะทำได้ดีกว่า JIT อย่างง่ายดาย
แน่นอนว่า C # (หรือ Java หรือ VB) มักจะสร้างโซลูชันที่ทำงานได้และมีประสิทธิภาพได้เร็วกว่า C ++ (หากเป็นเพียงเพราะ C ++ มีความหมายที่ซับซ้อนและไลบรารีมาตรฐาน C ++ ในขณะที่น่าสนใจและมีประสิทธิภาพนั้นค่อนข้างแย่เมื่อเทียบกับแบบเต็ม ขอบเขตของไลบรารีมาตรฐานจาก. NET หรือ Java) ดังนั้นโดยปกติความแตกต่างระหว่าง C ++ และ. NET หรือ Java JIT จะไม่ปรากฏแก่ผู้ใช้ส่วนใหญ่และสำหรับไบนารีที่มีความสำคัญคุณยังสามารถเรียกใช้การประมวลผล C ++ ได้ จาก C # หรือ Java (แม้ว่าการเรียกแบบเนทีฟแบบนี้อาจมีค่าใช้จ่ายค่อนข้างสูง) ...
การเขียนโปรแกรม C ++
โปรดทราบว่าโดยปกติแล้วคุณกำลังเปรียบเทียบโค้ดรันไทม์ C ++ กับโค้ดที่เทียบเท่าใน C # หรือ Java แต่ C ++ มีคุณสมบัติอย่างหนึ่งที่สามารถทำงานได้ดีกว่า Java / C # นอกกรอบนั่นคือการเขียนโปรแกรมแม่แบบ: การประมวลผลโค้ดจะดำเนินการในเวลาคอมไพล์ (ดังนั้นจึงเพิ่มเวลาในการรวบรวมอย่างมาก) ทำให้รันไทม์เป็นศูนย์ (หรือเกือบเป็นศูนย์)
ฉันยังเห็นผลกระทบในชีวิตจริงในเรื่องนี้ (ฉันเล่นกับแนวคิดเท่านั้น แต่ถึงตอนนั้นความแตกต่างคือวินาทีของการดำเนินการสำหรับ JIT และเป็นศูนย์สำหรับ C ++) แต่สิ่งนี้ควรค่าแก่การกล่าวถึงควบคู่ไปกับการตั้งโปรแกรมเมทาโพรแกรมของเทมเพลตแฟล็ก จิ๊บจ๊อย ...
แก้ไข 2011-06-10:ใน C ++ การเล่นกับประเภทจะเสร็จสิ้นในเวลาคอมไพล์ซึ่งหมายถึงการสร้างรหัสทั่วไปที่เรียกรหัสที่ไม่ใช่ทั่วไป (เช่นตัวแยกวิเคราะห์ทั่วไปจากสตริงเป็นประเภท T เรียก API ไลบรารีมาตรฐานสำหรับประเภท T ที่รู้จัก และการทำให้ parser สามารถขยายได้ง่ายโดยผู้ใช้) นั้นง่ายมากและมีประสิทธิภาพมากในขณะที่ Java หรือ C # ที่เทียบเท่านั้นเจ็บปวดที่สุดในการเขียนและจะช้ากว่าและแก้ไขได้เสมอเมื่อรันไทม์แม้ว่าจะทราบประเภทในเวลาคอมไพล์ก็ตาม หมายความว่าความหวังเดียวของคุณคือการให้ JIT แทรกซึมเข้าไปในทุกสิ่ง
...
แก้ไข 2011-09-20:ทีมที่อยู่เบื้องหลัง Blitz ++ ( หน้าแรก , Wikipedia ) ไปในทางนั้นและเห็นได้ชัดว่าเป้าหมายของพวกเขาคือการบรรลุผลการดำเนินงานของ FORTRAN ในการคำนวณทางวิทยาศาสตร์โดยการย้ายจากการรันไทม์ไปจนถึงเวลาคอมไพล์ให้มากที่สุดเท่าที่จะเป็นไปได้ . ดังนั้น "ฉันยังเห็นผลกระทบในชีวิตจริงในส่วนนี้ " ที่ฉันเขียนไว้ข้างต้นดูเหมือนจะมีอยู่ในชีวิตจริง
การใช้หน่วยความจำ Native C ++
C ++ มีการใช้งานหน่วยความจำที่แตกต่างจาก Java / C # ดังนั้นจึงมีข้อดี / ข้อบกพร่องที่แตกต่างกัน
ไม่ว่าการเพิ่มประสิทธิภาพ JIT จะไม่มีอะไรเร็วเท่ากับการเข้าถึงตัวชี้โดยตรงไปยังหน่วยความจำ (ลองละเว้นไปชั่วขณะแคชโปรเซสเซอร์ ฯลฯ ) ดังนั้นหากคุณมีข้อมูลที่ต่อเนื่องกันในหน่วยความจำการเข้าถึงผ่านตัวชี้ C ++ (เช่นตัวชี้ C ... ขอให้ Caesar ครบกำหนด) จะเร็วกว่าใน Java / C # หลายเท่า และ C ++ มี RAII ซึ่งทำให้การประมวลผลง่ายกว่าใน C # หรือแม้แต่ใน Java มาก C ++ ไม่จำเป็นต้องusing
กำหนดขอบเขตการมีอยู่ของวัตถุ และ C ++ ไม่มีfinally
อนุประโยค นี่ไม่ใช่ข้อผิดพลาด
:-)
และแม้จะมีโครงสร้างเหมือนดั้งเดิมของ C # แต่วัตถุ C ++ "บนสแต็ก" จะไม่เสียค่าใช้จ่ายใด ๆ ในการจัดสรรและการทำลายและไม่จำเป็นต้องมี GC ในการทำงานในเธรดอิสระในการทำความสะอาด
สำหรับการแยกส่วนหน่วยความจำตัวจัดสรรหน่วยความจำในปี 2008 ไม่ใช่ตัวจัดสรรหน่วยความจำแบบเก่าจากปี 1980 ที่มักจะเปรียบเทียบกับ GC: การจัดสรร C ++ ไม่สามารถย้ายในหน่วยความจำได้จริง แต่แล้วเช่นเดียวกับระบบไฟล์ Linux: ใครต้องการฮาร์ดดิสก์ การจัดเรียงข้อมูลเมื่อไม่เกิดการแยกส่วน? การใช้ตัวจัดสรรที่เหมาะสมสำหรับงานที่เหมาะสมควรเป็นส่วนหนึ่งของชุดเครื่องมือสำหรับนักพัฒนา C ++ ตอนนี้การเขียนตัวจัดสรรไม่ใช่เรื่องง่ายแล้วพวกเราส่วนใหญ่มีสิ่งที่ดีกว่าที่ต้องทำและสำหรับการใช้งานส่วนใหญ่ RAII หรือ GC นั้นดีเกินพอ
แก้ไข 2011-10-04:สำหรับตัวอย่างเกี่ยวกับตัวจัดสรรที่มีประสิทธิภาพ: บนแพลตฟอร์ม Windows ตั้งแต่ Vista, Low Fragmentation Heapถูกเปิดใช้งานโดยค่าเริ่มต้น สำหรับเวอร์ชันก่อนหน้า LFH สามารถเปิดใช้งานได้โดยเรียกใช้ฟังก์ชัน WinAPI HeapSetInformation ) ในระบบปฏิบัติการอื่นจะมีการจัดเตรียมตัวจัดสรรสำรองไว้ให้ (ดูhttps://secure.wikimedia.org/wikipedia/en/wiki/Mallocสำหรับรายชื่อ)
ตอนนี้โมเดลหน่วยความจำค่อนข้างซับซ้อนมากขึ้นด้วยการเพิ่มขึ้นของเทคโนโลยีมัลติคอร์และมัลติเธรด ในช่องนี้ฉันเดาว่า. NET มีข้อได้เปรียบและ Java ที่ฉันได้รับการบอกกล่าวว่าถือเป็นจุดสูงสุด เป็นเรื่องง่ายที่แฮ็กเกอร์ "บนเครื่องโลหะ" บางคนจะยกย่องรหัส "ใกล้เครื่อง" ของเขา แต่ตอนนี้มันค่อนข้างยากที่จะสร้างการประกอบด้วยมือที่ดีกว่าการปล่อยให้คอมไพเลอร์ทำงาน สำหรับ C ++ คอมไพเลอร์มักจะดีกว่าแฮ็กเกอร์ตั้งแต่ทศวรรษที่ผ่านมา สำหรับ C # และ Java สิ่งนี้ง่ายยิ่งขึ้น
ถึงกระนั้น C ++ 0x มาตรฐานใหม่จะกำหนดรูปแบบหน่วยความจำอย่างง่ายให้กับคอมไพเลอร์ C ++ ซึ่งจะทำให้โค้ดหลายกระบวนการ / ขนาน / เธรดที่มีประสิทธิภาพเป็นมาตรฐาน (และทำให้ง่ายขึ้น) ใน C ++ และทำให้การเพิ่มประสิทธิภาพง่ายขึ้นและปลอดภัยสำหรับคอมไพเลอร์ แต่แล้วเราจะเห็นในอีกไม่กี่ปีข้างหน้าว่าคำสัญญานั้นเป็นจริงหรือไม่
C ++ / CLI เทียบกับ C # / VB.NET
หมายเหตุ: ในส่วนนี้ฉันกำลังพูดถึง C ++ / CLI นั่นคือ C ++ ที่โฮสต์โดย. NET ไม่ใช่ C ++ ดั้งเดิม
สัปดาห์ที่แล้วฉันมีการฝึกอบรมเกี่ยวกับการเพิ่มประสิทธิภาพ. NET และพบว่าคอมไพเลอร์แบบคงที่มีความสำคัญมาก สำคัญกว่า JIT
โค้ดเดียวกันที่คอมไพล์ใน C ++ / CLI (หรือบรรพบุรุษของมันคือ Managed C ++) อาจเร็วกว่าโค้ดเดียวกันที่ผลิตใน C # (หรือ VB.NET ซึ่งคอมไพเลอร์สร้าง IL เหมือนกันมากกว่า C #)
เนื่องจากคอมไพเลอร์แบบคงที่ของ C ++ สามารถสร้างโค้ดที่ปรับให้เหมาะสมแล้วได้ดีกว่า C #
ตัวอย่างเช่นฟังก์ชัน inlining ใน. NET จะ จำกัด เฉพาะฟังก์ชันที่มีความยาว bytecode น้อยกว่าหรือเท่ากับ 32 ไบต์ ดังนั้นโค้ดบางตัวใน C # จะสร้างตัวเข้าถึงขนาด 40 ไบต์ซึ่ง JIT จะไม่อินไลน์เลย รหัสเดียวกันใน C ++ / CLI จะสร้างตัวเข้าถึงขนาด 20 ไบต์ซึ่ง JIT จะอยู่ในบรรทัด
อีกตัวอย่างหนึ่งคือตัวแปรชั่วคราวซึ่งรวบรวมโดยคอมไพเลอร์ C ++ ในขณะที่ยังคงถูกกล่าวถึงใน IL ที่สร้างโดยคอมไพเลอร์ C # การเพิ่มประสิทธิภาพการคอมไพล์แบบคงที่ C ++ จะส่งผลให้โค้ดน้อยลงดังนั้นจึงให้สิทธิ์การปรับแต่ง JIT เชิงรุกมากขึ้นอีกครั้ง
เหตุผลนี้ถูกคาดเดาว่าเป็นความจริงที่ว่าคอมไพเลอร์ C ++ / CLI ได้รับประโยชน์จากเทคนิคการเพิ่มประสิทธิภาพมากมายจากคอมไพเลอร์ดั้งเดิมของ C ++
ข้อสรุป
ฉันรัก C ++
แต่เท่าที่ฉันเห็น C # หรือ Java ล้วนเป็นทางออกที่ดีกว่า ไม่ใช่เพราะเร็วกว่า C ++ แต่เป็นเพราะเมื่อคุณเพิ่มคุณสมบัติของพวกเขาพวกเขาจะมีประสิทธิผลมากขึ้นต้องการการฝึกอบรมน้อยลงและมีไลบรารีมาตรฐานที่สมบูรณ์มากกว่า C ++ และสำหรับโปรแกรมส่วนใหญ่ความแตกต่างของความเร็ว (ไม่ทางใดก็ทางหนึ่ง) จะเล็กน้อย ...
แก้ไข (2011-06-06)
ประสบการณ์ของฉันเกี่ยวกับ C # /. NET
ตอนนี้ฉันมีการเข้ารหัส C # ระดับมืออาชีพเกือบ 5 เดือน (ซึ่งรวมถึง CV ของฉันแล้วเต็มไปด้วย C ++ และ Java และสัมผัส C ++ / CLI)
ฉันเล่นกับ WinForms (อะแฮ่ม ... ) และ WCF (เจ๋ง!) และ WPF (เจ๋ง !!!! ทั้งผ่าน XAML และ C # ดิบ WPF นั้นง่ายมากฉันเชื่อว่า Swing ไม่สามารถเปรียบเทียบกับมันได้) และ C # 4.0
ข้อสรุปคือแม้ว่าจะง่าย / เร็วกว่าในการสร้างโค้ดที่ทำงานใน C # / Java มากกว่าใน C ++ แต่การสร้างโค้ดที่แข็งแกร่งปลอดภัยและมีประสิทธิภาพใน C # (และยากกว่าใน Java) มากกว่าใน C ++ เหตุผลมากมาย แต่สามารถสรุปได้โดย:
- Generics ไม่ได้มีประสิทธิภาพเท่าเทมเพลต ( พยายามเขียนวิธีการแยกวิเคราะห์ทั่วไปที่มีประสิทธิภาพ (จากสตริงถึง T) หรือการเพิ่มประสิทธิภาพที่เทียบเท่ากับการเพิ่ม :: lexical_cast ใน C # เพื่อทำความเข้าใจปัญหา )
- RAII ยังคงไม่ตรงกัน ( GC ยังคงรั่วไหลได้ (ใช่ฉันต้องจัดการปัญหานั้น) และจะจัดการเฉพาะหน่วยความจำแม้แต่ C #
using
ก็ไม่ง่ายและทรงพลังเพราะการเขียนการกำจัดการใช้งานที่ถูกต้องเป็นเรื่องยาก )
- C #
readonly
และ Java final
ไม่มีประโยชน์เท่า C ++const
( ไม่มีทางที่คุณจะเปิดเผยข้อมูลที่ซับซ้อนแบบอ่านอย่างเดียว (เช่น Tree of Nodes) ใน C # โดยไม่ต้องทำงานมากในขณะที่เป็นคุณลักษณะในตัวของ C ++ ข้อมูลที่ไม่เปลี่ยนรูปเป็นโซลูชันที่น่าสนใจ แต่ไม่ใช่ทุกอย่างที่จะไม่เปลี่ยนรูปได้ดังนั้นมันจึงไม่เพียงพอด้วยซ้ำ)
ดังนั้น C # ยังคงเป็นภาษาที่น่ารื่นรมย์ตราบเท่าที่คุณต้องการบางสิ่งที่ใช้ได้ผล แต่เป็นภาษาที่น่าหงุดหงิดในขณะที่คุณต้องการสิ่งที่ใช้ได้ผลเสมอและปลอดภัย
Java นั้นน่าผิดหวังมากยิ่งขึ้นเนื่องจากมีปัญหาเดียวกันกับ C # และอื่น ๆ : การขาดusing
คีย์เวิร์ดของ C # ที่เทียบเท่าเพื่อนร่วมงานที่มีทักษะสูงของฉันใช้เวลามากเกินไปในการตรวจสอบให้แน่ใจว่าทรัพยากรที่ได้รับการปลดปล่อยอย่างถูกต้องในขณะที่ C ++ ที่เทียบเท่าจะมี เป็นเรื่องง่าย (โดยใช้ตัวทำลายและตัวชี้อัจฉริยะ)
ดังนั้นฉันเดาว่าการเพิ่มผลผลิตของ C # / Java สามารถมองเห็นได้สำหรับโค้ดส่วนใหญ่ ... จนถึงวันที่คุณต้องการโค้ดให้สมบูรณ์แบบที่สุด วันนั้นคุณจะรู้ความเจ็บปวด (คุณจะไม่เชื่อว่าสิ่งที่ถามจากเซิร์ฟเวอร์และแอป GUI ของเรา ... )
เกี่ยวกับ Java ฝั่งเซิร์ฟเวอร์และ C ++
ฉันยังคงติดต่อกับทีมเซิร์ฟเวอร์ (ฉันทำงานร่วมกัน 2 ปีก่อนที่จะกลับไปที่ทีม GUI) ที่อีกด้านหนึ่งของอาคารและฉันได้เรียนรู้สิ่งที่น่าสนใจ
ปีที่ผ่านมามีแนวโน้มที่จะมีการกำหนดให้แอปเซิร์ฟเวอร์ Java มาแทนที่แอปเซิร์ฟเวอร์ C ++ รุ่นเก่าเนื่องจาก Java มีเฟรมเวิร์ก / เครื่องมือมากมายและง่ายต่อการบำรุงรักษาปรับใช้และอื่น ๆ ฯลฯ
... จนกระทั่งปัญหาความหน่วงแฝงต่ำทำให้หัวน่าเกลียดเกิดขึ้นในช่วงหลายเดือนที่ผ่านมา จากนั้นแอปเซิร์ฟเวอร์ Java ไม่ว่าการเพิ่มประสิทธิภาพจะพยายามโดยทีม Java ที่มีทักษะของเราเพียงแค่และแพ้การแข่งขันกับเซิร์ฟเวอร์ C ++ รุ่นเก่าที่ไม่ได้รับการปรับให้เหมาะสมอย่างแท้จริง
ปัจจุบันการตัดสินใจคือการเก็บเซิร์ฟเวอร์ Java ไว้สำหรับการใช้งานทั่วไปในขณะที่ยังคงมีความสำคัญโดยไม่เกี่ยวข้องกับเป้าหมายที่มีเวลาแฝงต่ำและเพิ่มประสิทธิภาพแอปพลิเคชันเซิร์ฟเวอร์ C ++ ที่เร็วกว่าอยู่แล้วอย่างจริงจังเพื่อความหน่วงแฝงต่ำและความต้องการเวลาแฝงที่ต่ำมาก
ข้อสรุป
ไม่มีอะไรง่ายอย่างที่คิด
Java และ C # เป็นภาษาที่ยอดเยี่ยมพร้อมด้วยไลบรารีและเฟรมเวิร์กมาตรฐานที่กว้างขวางซึ่งคุณสามารถเขียนโค้ดได้อย่างรวดเร็วและได้ผลลัพธ์ในไม่ช้า
แต่เมื่อคุณต้องการพลังดิบการเพิ่มประสิทธิภาพที่ทรงพลังและเป็นระบบการสนับสนุนคอมไพเลอร์ที่แข็งแกร่งคุณสมบัติภาษาที่ทรงพลังและความปลอดภัยที่สมบูรณ์ Java และ C # ทำให้ยากที่จะชนะเปอร์เซ็นต์สุดท้ายที่ขาดหายไป แต่มีคุณภาพที่สำคัญที่คุณต้องอยู่เหนือคู่แข่ง
ราวกับว่าคุณต้องการเวลาน้อยลงและนักพัฒนาที่มีประสบการณ์น้อยกว่าใน C # / Java เมื่อเทียบกับ C ++ ในการสร้างโค้ดคุณภาพโดยเฉลี่ย แต่ในทางกลับกันช่วงเวลาที่คุณต้องการโค้ดคุณภาพที่ยอดเยี่ยมสมบูรณ์แบบทันใดนั้นก็จะได้ผลลัพธ์ที่ง่ายและเร็วขึ้น ใน C ++
แน่นอนว่านี่เป็นการรับรู้ของฉันเองบางทีอาจ จำกัด เฉพาะความต้องการเฉพาะของเรา
แต่ถึงกระนั้นมันก็เป็นสิ่งที่เกิดขึ้นในปัจจุบันทั้งในทีม GUI และทีมฝั่งเซิร์ฟเวอร์
แน่นอนฉันจะอัปเดตโพสต์นี้หากมีสิ่งใหม่เกิดขึ้น
แก้ไข (2011-06-22)
"เราพบว่าในเรื่องของประสิทธิภาพ C ++ ชนะด้วยอัตรากำไรที่มากอย่างไรก็ตามยังต้องใช้ความพยายามในการปรับแต่งอย่างละเอียดมากที่สุดซึ่งหลาย ๆ อย่างได้ดำเนินการในระดับที่ซับซ้อนซึ่งโปรแกรมเมอร์ทั่วไปจะไม่สามารถใช้งานได้
[... ] เวอร์ชัน Java น่าจะเป็นเวอร์ชันที่ใช้งานง่ายที่สุด แต่ยากที่สุดในการวิเคราะห์ประสิทธิภาพ โดยเฉพาะผลกระทบจากการเก็บขยะนั้นซับซ้อนและยากที่จะปรับแต่ง "
แหล่งที่มา:
แก้ไข (2011-09-20)
"คำที่กำลังดำเนินอยู่ใน Facebook คือ ' โค้ด C ++ ที่เขียนขึ้นอย่างมีเหตุผลทำงานได้เร็ว ' ซึ่งเน้นย้ำถึงความพยายามอย่างมากที่ใช้ในการเพิ่มประสิทธิภาพโค้ด PHP และ Java ในทางตรงกันข้ามโค้ด C ++ นั้นเขียนยากกว่าในภาษาอื่น ๆ แต่โค้ดที่มีประสิทธิภาพคือ [เขียนภาษา C ++ ง่ายกว่าภาษาอื่น] มาก "
- Herb Sutterที่// build /อ้างถึงAndrei Alexandrescu
แหล่งที่มา: