Java นั้นยากกว่าการ "ปรับแต่ง" เพื่อประสิทธิภาพเมื่อเทียบกับ C / C ++ หรือไม่? [ปิด]


11

"ความมหัศจรรย์" ของ JVM ขัดขวางอิทธิพลของโปรแกรมเมอร์ที่มีต่อการปรับให้เหมาะสมขนาดจิ๋วใน Java หรือไม่? ฉันเพิ่งอ่านใน C ++ บางครั้งการเรียงลำดับของข้อมูลสมาชิกสามารถให้การเพิ่มประสิทธิภาพ (ได้รับในสภาพแวดล้อม microsecond) และฉันสันนิษฐานว่ามือของโปรแกรมเมอร์ถูกผูกไว้เมื่อมันมาถึงการบีบประสิทธิภาพจาก Java?

ฉันขอขอบคุณอัลกอริทึมที่เหมาะสมให้ความเร็วที่เพิ่มขึ้น แต่เมื่อคุณมีอัลกอริทึมที่ถูกต้องแล้ว Java จะปรับแต่งยากขึ้นเนื่องจากการควบคุม JVM หรือไม่

ถ้าไม่ใช่คนอาจยกตัวอย่างว่าคุณสามารถใช้กลอุบายอะไรใน Java (นอกเหนือจากการคอมไพล์แฟล็กเรียบง่าย)


14
หลักการพื้นฐานที่อยู่เบื้องหลังการเพิ่มประสิทธิภาพ Java ทั้งหมดคือ: JVM อาจทำได้ดีกว่าที่คุณสามารถทำได้ การปรับให้เหมาะสมส่วนใหญ่เกี่ยวข้องกับการฝึกการเขียนโปรแกรมที่สมเหตุสมผลและหลีกเลี่ยงสิ่งต่าง ๆ เช่นการต่อสตริงในลูป
Robert Harvey

3
หลักการของการเพิ่มประสิทธิภาพขนาดเล็กในทุกภาษาคือคอมไพเลอร์ได้ทำไปแล้วดีกว่าที่คุณสามารถทำได้ หลักการอื่น ๆ ของการเพิ่มประสิทธิภาพขนาดเล็กในทุกภาษาคือการเพิ่มฮาร์ดแวร์ลงในนั้นมีราคาถูกกว่าการใช้เวลาอย่างมีประสิทธิภาพของโปรแกรมเมอร์ โปรแกรมเมอร์มีแนวโน้มที่จะปรับขนาดปัญหา (อัลกอริทึมย่อย) แต่การเพิ่มประสิทธิภาพขนาดเล็กนั้นเป็นการเสียเวลา บางครั้งการปรับให้เหมาะสมขนาดเล็กเข้ากับระบบฝังตัวที่คุณไม่สามารถทิ้งฮาร์ดแวร์เพิ่มเติมได้ แต่ Android ที่ใช้ Java และการใช้งานที่ไม่ดีแสดงให้เห็นว่าส่วนใหญ่มีฮาร์ดแวร์เพียงพอแล้ว
Jan Hudec

1
สำหรับ "Java เทคนิคประสิทธิภาพ" มูลค่าการศึกษาคือ: มีผลบังคับใช้ Java , Angelika Langer ลิงค์ - Java ผลการดำเนินงานและบทความที่เกี่ยวข้องกับประสิทธิภาพการทำงานโดยไบรอันเก๊ในทฤษฎีและการปฏิบัติ JavaและThreading เบา ๆ ชุดจดทะเบียนที่นี่
ริ้น

2
ระวังอย่างมากเกี่ยวกับเคล็ดลับและเทคนิค - JVM ระบบปฏิบัติการและฮาร์ดแวร์เคลื่อนย้ายไป - คุณดีที่สุดในการเรียนรู้วิธีการปรับแต่งประสิทธิภาพและการใช้การปรับปรุงสำหรับสภาพแวดล้อมเฉพาะของคุณ :-)
Martijn Verburg

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

คำตอบ:


5

แน่นอนว่าในระดับ micro-optimization JVM จะทำบางสิ่งที่คุณจะสามารถควบคุมได้น้อยเมื่อเทียบกับ C และ C ++ โดยเฉพาะ

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

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


มันมีความสำคัญมากเมื่อคุณพบว่าแอปของคุณไม่ได้ปรับขนาดตามแกน
James

@james - สนใจที่จะทำอย่างละเอียด?
Telastyn

1
ดูที่นี่สำหรับการเริ่มต้น: mechanical-sympathy.blogspot.co.uk/2011/07/false-sharing.html
James

1
@James การปรับสเกลข้ามคอร์มีน้อยมากเกี่ยวกับภาษาการใช้งาน (Python ยกเว้น!) และอื่น ๆ ที่เกี่ยวข้องกับสถาปัตยกรรมแอปพลิเคชัน
James Anderson

29

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

อย่างไรก็ตามมีพื้นที่สำคัญอย่างหนึ่งของการปรับให้เหมาะสมที่ C + + และ Java แตกต่างกันโดยพื้นฐานและนั่นคือการเข้าถึงหน่วยความจำจำนวนมาก C ++ มีการจัดการหน่วยความจำด้วยตนเองซึ่งหมายความว่าคุณสามารถปรับการจัดวางข้อมูลของแอปพลิเคชันและรูปแบบการเข้าถึงให้เหมาะสมเพื่อใช้แคชอย่างเต็มประสิทธิภาพ มันค่อนข้างยากโดยเฉพาะกับฮาร์ดแวร์ที่คุณใช้ (เพื่อประสิทธิภาพที่เพิ่มขึ้นอาจหายไปกับฮาร์ดแวร์ที่แตกต่างกัน) แต่ถ้าทำถูกต้องมันจะนำไปสู่ประสิทธิภาพที่น่าทึ่งอย่างแท้จริง แน่นอนคุณจ่ายด้วยศักยภาพสำหรับข้อผิดพลาดที่น่ากลัวทุกชนิด

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

ถ้าไม่ใช่คนอาจยกตัวอย่างว่าคุณสามารถใช้กลอุบายอะไรใน Java (นอกเหนือจากการคอมไพล์แฟล็กเรียบง่าย)

ธงคอมไพเลอร์ไม่เกี่ยวข้องใน Java เพราะคอมไพเลอร์ Java แทบจะไม่มีการปรับให้เหมาะสม รันไทม์ทำ

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


1
+1: โดยพื้นฐานแล้วสิ่งที่ฉันเขียนในคำตอบอาจเป็นสูตรที่ดีกว่า
Klaim

1
+1: คะแนนที่ดีมากอธิบายอย่างกระชับ: "นี่มันค่อนข้างยาก ... แต่ถ้าทำถูกต้องมันจะนำไปสู่ประสิทธิภาพที่น่าทึ่งอย่างแน่นอนคุณต้องจ่ายด้วยศักยภาพสำหรับข้อผิดพลาดที่น่ากลัวทุกประเภท ."
จอร์โจ

1
@MartinBa: การจ่ายหน่วยความจำให้เกิดประโยชน์สูงสุดสำหรับคุณ หากคุณไม่พยายามเพิ่มประสิทธิภาพการจัดการหน่วยความจำการจัดการหน่วยความจำ C ++ นั้นไม่ใช่เรื่องยาก (หลีกเลี่ยงการใช้หน่วยความจำ STL ทั้งหมดหรือทำให้ใช้งาน RAII ได้ง่าย) แน่นอนว่าการใช้งาน RAII ใน C ++ นั้นต้องใช้โค้ดหลายบรรทัดมากกว่าการไม่ทำอะไรเลยใน Java (เช่นเพราะ Java จัดการให้คุณได้)
Brian

3
@ มาร์ตินบา: โดยทั่วไปใช่ พอยน์เตอร์ที่ห้อยอยู่, บัฟเฟอร์โอเวอร์โฟลว์, พอยน์เตอร์ที่ไม่กำหนดค่าเริ่มต้น, ข้อผิดพลาดในการคำนวณทางคณิตศาสตร์ของตัวชี้, ทุกสิ่งที่ไม่มีอยู่โดยไม่มีการจัดการหน่วยความจำด้วยตนเอง และเพิ่มประสิทธิภาพการเข้าถึงหน่วยความจำสวยมากคุณจะต้องทำมากของการจัดการหน่วยความจำคู่มือ
Michael Borgwardt

1
มีสองสิ่งที่คุณสามารถทำได้ในจาวา หนึ่งคือการรวมวัตถุซึ่งจะเพิ่มตำแหน่งหน่วยความจำโอกาสของวัตถุ (ซึ่งแตกต่างจาก C ++ ซึ่งมันสามารถรับประกันตำแหน่งหน่วยความจำ)
RokL

5

[... ] (ได้รับในสภาพแวดล้อมไมโครวินาที) [... ]

เพิ่มขึ้นในเวลาเพียงไม่กี่วินาทีหากเราวนซ้ำหลายล้านต่อหลายพันล้านสิ่งของ เซสชัน vtune / micro-optimization ส่วนตัวจาก C ++ (ไม่มีการปรับปรุงอัลกอริทึม):

T-Rex (12.3 million facets):
Initial Time: 32.2372797 seconds
Multithreading: 7.4896073 seconds
4.9201039 seconds
4.6946372 seconds
3.261677 seconds
2.6988536 seconds
SIMD: 1.7831 seconds
4-valence patch optimization: 1.25007 seconds
0.978046 seconds
0.970057 seconds
0.911041 seconds

ทุกอย่างนอกเหนือจาก "มัลติเธรด", "SIMD" (เขียนด้วยลายมือเพื่อเอาชนะคอมไพเลอร์) และการเพิ่มประสิทธิภาพแพตช์ 4 วาเลนซ์คือการปรับแต่งหน่วยความจำระดับไมโคร รหัสต้นฉบับที่เริ่มต้นจากเวลาเริ่มต้นของ 32 วินาทีนั้นได้รับการปรับปรุงให้ดีขึ้นเล็กน้อย (ความซับซ้อนของอัลกอริทึมที่ดีที่สุดในทางทฤษฎี) และนี่คือเซสชันล่าสุด รุ่นดั้งเดิมยาวนานก่อนเซสชันล่าสุดนี้ใช้เวลาประมวลผลนานกว่า 5 นาที

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

ในความสำคัญของการเพิ่มประสิทธิภาพ Micro

ฉันรู้สึกกระวนกระวายใจเล็กน้อยจากความคิดนี้ว่าการปรับขนาดเล็กแบบไมโครทำให้เสียเวลา ฉันยอมรับว่ามันเป็นคำแนะนำทั่วไปที่ดี แต่ไม่ใช่ทุกคนที่ทำมันอย่างไม่ถูกต้องโดยยึดตามลางสังหรณ์และไสยศาสตร์มากกว่าการวัด ทำอย่างถูกต้องไม่จำเป็นต้องส่งผลกระทบต่อไมโคร ถ้าเราใช้ Embree kernel (raytracing kernel) ของ Intel และทดสอบเฉพาะ scalar BVH ที่พวกเขาเขียน (ไม่ใช่ ray packet ซึ่งยากต่อการอธิบาย) จากนั้นลองเอาชนะประสิทธิภาพของโครงสร้างข้อมูลนั้น ประสบการณ์ต่ำต้อยแม้สำหรับทหารผ่านศึกที่ใช้ในการทำโปรไฟล์และปรับแต่งโค้ดมานานหลายทศวรรษ และทั้งหมดนี้เป็นเพราะมีการใช้การเพิ่มประสิทธิภาพแบบไมโคร วิธีแก้ปัญหาของพวกเขาสามารถประมวลผลรังสีกว่าร้อยล้านต่อวินาทีเมื่อฉันเห็นผู้เชี่ยวชาญด้านอุตสาหกรรมที่ทำงานใน raytracing ที่สามารถ '

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

อัลกอริทึม

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

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

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

ฉันคิดว่าความแตกต่างนี้อาจไม่สำคัญกับคนส่วนใหญ่ระหว่างพูด 12 นาทีและ 15 วินาทีและนั่นคือเหตุผลที่การเพิ่มประสิทธิภาพขนาดเล็กมักจะไร้ประโยชน์เพราะมันมักจะชอบความแตกต่างระหว่างนาทีและวินาทีไม่ใช่นาทีและเดือน อีกเหตุผลที่ฉันคิดว่ามันไร้ประโยชน์ก็คือมันมักจะใช้กับพื้นที่ที่ไม่สำคัญ: พื้นที่เล็ก ๆ ที่ไม่ได้เป็นวงแหวนและสำคัญซึ่งทำให้เกิดความแตกต่าง 1% ที่น่าสงสัย (ซึ่งอาจเป็นเพียงเสียงรบกวน) แต่สำหรับผู้ที่สนใจเกี่ยวกับความแตกต่างของเวลาเหล่านี้และยินดีที่จะวัดและทำสิ่งที่ถูกต้องฉันคิดว่ามันควรค่าแก่ความสนใจอย่างน้อยแนวคิดพื้นฐานของลำดับชั้นหน่วยความจำ (โดยเฉพาะระดับบนที่เกี่ยวข้องกับความผิดพลาดของหน้าเว็บ .

Java ออกจากพื้นที่มากมายสำหรับการเพิ่มประสิทธิภาพขนาดเล็กที่ดี

ว้าขอโทษด้วยการพูดจาโผงผางแบบนั้น:

"ความมหัศจรรย์" ของ JVM ขัดขวางอิทธิพลของโปรแกรมเมอร์ที่มีต่อการปรับให้เหมาะสมขนาดจิ๋วใน Java หรือไม่?

เล็กน้อย แต่ไม่มากเท่าที่คนอื่นคิดว่าคุณทำถูกต้อง ตัวอย่างเช่นหากคุณกำลังทำการประมวลผลภาพในโค้ดเนทีฟพร้อมด้วยลายมือ SIMD, multithreading และการปรับแต่งหน่วยความจำ (รูปแบบการเข้าถึงและอาจเป็นตัวแทนขึ้นอยู่กับอัลกอริทึมการประมวลผลภาพ) มันง่ายที่จะกระทืบพิกเซลหลายร้อยล้านพิกเซลต่อวินาที บิตพิกเซล RGBA (ช่องสี 8 บิต) และบางครั้งอาจพันล้านต่อวินาที

เป็นไปไม่ได้ที่จะเข้าใกล้ทุกแห่งใน Java ถ้าคุณบอกว่าสร้างPixelวัตถุ (เพียงอย่างเดียวนี้จะขยายขนาดของพิกเซลจาก 4 ไบต์ถึง 16 ใน 64- บิต)

แต่คุณอาจเข้าใกล้ได้มากขึ้นถ้าคุณหลีกเลี่ยงPixelวัตถุใช้อาร์เรย์จำนวนไบต์และทำโมเดลImageวัตถุ Java ยังคงมีความสามารถอยู่ที่นั่นหากคุณเริ่มใช้อาร์เรย์ของข้อมูลเก่าแบบธรรมดา ฉันเคยลองสิ่งเหล่านี้มาก่อนใน Java และรู้สึกประทับใจมากหากคุณไม่ได้สร้างวัตถุเล็ก ๆ น้อย ๆ ทุกที่ที่ใหญ่กว่าปกติ 4 เท่า (เช่นใช้intแทนInteger) และเริ่มสร้างโมเดลอินเทอร์เฟซจำนวนมากเช่นImageอินเตอร์เฟสไม่ใช่Pixelอินเตอร์เฟส ฉันยังอยากบอกว่า Java สามารถเทียบเคียงประสิทธิภาพ C ++ ได้หากคุณวนซ้ำข้อมูลเก่าธรรมดาไม่ใช่วัตถุ ( floatเช่นอาร์เรย์ขนาดใหญ่เช่นไม่ใช่Float)

บางทีสิ่งที่สำคัญกว่าขนาดหน่วยความจำก็คืออาเรย์ของintการรับประกันการแสดงที่ต่อเนื่องกัน อาร์เรย์ของIntegerไม่ Contiguity มักจะเป็นสิ่งจำเป็นสำหรับสถานที่อ้างอิงเนื่องจากมันหมายถึงองค์ประกอบหลายอย่าง (เช่น: 16 ints) ทั้งหมดสามารถใส่ลงในแคชบรรทัดเดียว ในขณะเดียวกันIntegerอาจติดที่ใดที่หนึ่งในหน่วยความจำที่มีหน่วยความจำรอบไม่เกี่ยวข้องเท่านั้นที่จะมีพื้นที่ของหน่วยความจำที่โหลดลงในสายแคชเท่านั้นที่จะใช้จำนวนเต็มเดียวก่อนที่จะขับไล่เมื่อเทียบกับ 16 จำนวนเต็ม แม้ว่าเราจะโชคดีและสิ่งรอบตัวIntegersอยู่ติดกันในหน่วยความจำเราสามารถใส่ 4 เข้าไปในแคชไลน์ที่สามารถเข้าถึงได้ก่อนการขับไล่เนื่องจากมีIntegerขนาดใหญ่กว่า 4 เท่าและนั่นเป็นสถานการณ์ที่ดีที่สุด

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

ฉันเพิ่งอ่านใน C ++ บางครั้งการเรียงลำดับของข้อมูลสมาชิกสามารถให้การเพิ่มประสิทธิภาพ [... ]

ลำดับของสมาชิกข้อมูลโดยทั่วไปไม่สำคัญใน Java แต่ส่วนใหญ่เป็นสิ่งที่ดี ใน C และ C ++ การรักษาลำดับของข้อมูลสมาชิกมักมีความสำคัญต่อเหตุผลของ ABI เพื่อให้คอมไพเลอร์ไม่ยุ่งกับเรื่องนั้น นักพัฒนามนุษย์ที่ทำงานจะต้องระมัดระวังในการทำสิ่งต่าง ๆ เช่นจัดเรียงข้อมูลสมาชิกตามลำดับจากมากไปหาน้อยที่สุดเพื่อหลีกเลี่ยงการสูญเสียความจำในการขยาย ด้วย Java นั้น JIT สามารถเรียงลำดับสมาชิกให้คุณได้อย่างรวดเร็วเพื่อให้แน่ใจว่ามีการจัดเรียงที่เหมาะสมในขณะที่ลดช่องว่างภายในลงดังนั้นหากเป็นกรณีนี้โดยอัตโนมัติสิ่งที่โปรแกรมเมอร์ C และ C ++ โดยเฉลี่ยสามารถทำได้ไม่ดี ซึ่งไม่เพียง แต่เป็นการสูญเสียความทรงจำ แต่บ่อยครั้งที่สูญเสียความเร็วโดยการเพิ่มความก้าวหน้าระหว่างโครงสร้าง AoS โดยไม่จำเป็นและทำให้แคชหายไปมากขึ้น) มัน' เป็นสิ่งที่หุ่นยนต์มากที่จะจัดเรียงเขตข้อมูลใหม่เพื่อลดช่องว่างภายในเพื่อให้มนุษย์ไม่ต้องจัดการกับเรื่องนั้น เวลาเดียวที่การจัดเรียงฟิลด์อาจสำคัญในแบบที่มนุษย์ต้องการทราบการจัดเรียงที่เหมาะสมคือถ้าวัตถุมีขนาดใหญ่กว่า 64 ไบต์และเรากำลังจัดเรียงเขตข้อมูลตามรูปแบบการเข้าถึง (ไม่ใช่ช่องว่างภายในที่เหมาะสม) - ในกรณีนี้ อาจเป็นความพยายามของมนุษย์มากขึ้น (ต้องมีการทำความเข้าใจเส้นทางที่สำคัญซึ่งบางอย่างเป็นข้อมูลที่คอมไพเลอร์ไม่สามารถคาดการณ์ได้โดยไม่ทราบว่าผู้ใช้จะทำอะไรกับซอฟต์แวร์)

ถ้าไม่ใช่คนอาจยกตัวอย่างว่าคุณสามารถใช้กลอุบายอะไรใน Java (นอกเหนือจากการคอมไพล์แฟล็กเรียบง่าย)

ความแตกต่างที่ยิ่งใหญ่ที่สุดสำหรับฉันในแง่ของการเพิ่มประสิทธิภาพความคิดระหว่าง Java และ C ++ คือ C ++ อาจอนุญาตให้คุณใช้ออบเจ็กต์เล็กน้อย (เล็ก) มากกว่า Java ในสถานการณ์ที่มีประสิทธิภาพสูง ตัวอย่างเช่น C ++ สามารถล้อมค่าจำนวนเต็มไปยังคลาสที่ไม่มีค่าใช้จ่ายใด ๆ (เปรียบเทียบกับทุกที่) Java จะต้องมีค่าใช้จ่ายที่เมตาดาต้าชี้สไตล์ + จัดตำแหน่ง padding ต่อวัตถุซึ่งเป็นเหตุผลที่Booleanมีขนาดใหญ่กว่าboolean( แต่ในการแลกเปลี่ยนการให้สิทธิประโยชน์สม่ำเสมอของการสะท้อนและความสามารถในการแทนที่ฟังก์ชันใด ๆ ที่ไม่ทำเครื่องหมายเป็นfinalสำหรับทุก UDT เดียว)

มันง่ายกว่านิดหน่อยใน C ++ ที่จะควบคุมความต่อเนื่องของเลย์เอาต์ของหน่วยความจำในฟิลด์ที่ไม่เป็นเนื้อเดียวกัน (เช่น interleaving floats และ ints เป็นหนึ่งอาเรย์ผ่านโครงสร้าง / คลาส) เนื่องจากตำแหน่งเชิงพื้นที่มักจะหายไป ใน Java เมื่อจัดสรรวัตถุผ่าน GC

... แต่บ่อยครั้งที่โซลูชันที่มีประสิทธิภาพสูงสุดมักจะแยกสิ่งเหล่านั้นออกไปและใช้รูปแบบการเข้าถึง SOA ผ่านอาร์เรย์ข้อมูลเก่าธรรมดาที่ต่อเนื่องกัน ดังนั้นสำหรับพื้นที่ที่ต้องการประสิทธิภาพสูงสุดกลยุทธ์ในการปรับเลย์เอาท์หน่วยความจำระหว่าง Java และ C ++ มักจะเหมือนกันและมักจะทำให้คุณต้องทำลายอินเทอร์เฟซแบบเชิงวัตถุขนาดเล็กเพื่อสนับสนุนอินเตอร์เฟสสไตล์คอลเลกชันที่สามารถทำสิ่งต่างๆเช่นร้อน / การแยกเขตข้อมูลเย็นตัวแทนของ SOA ฯลฯ ตัวแทนที่ไม่เหมือนกัน AoSoA ดูเหมือนว่าเป็นไปไม่ได้ใน Java (เว้นแต่คุณจะใช้อาร์เรย์ดิบเป็นไบต์หรืออะไรทำนองนั้น) แต่กรณีเหล่านี้เป็นกรณีที่หายากซึ่งทั้งคู่รูปแบบการเข้าถึงแบบลำดับและแบบสุ่มจำเป็นต้องรวดเร็วในขณะเดียวกันก็มีประเภทของฟิลด์ผสมสำหรับฟิลด์ร้อนพร้อมกัน สำหรับฉันแล้วความแตกต่างในกลยุทธ์การปรับให้เหมาะสม (ในระดับทั่วไป) ระหว่างสองสิ่งนี้เป็นสิ่งที่สงสัยหากคุณเข้าถึงประสิทธิภาพสูงสุด

ความแตกต่างแตกต่างกันไปเล็กน้อยถ้าคุณแค่เอื้อมไปถึงประสิทธิภาพ "ดี" - ไม่สามารถทำอะไรได้มากนักกับวัตถุขนาดเล็กเช่นIntegervs. vs. intอาจเป็น PITA ได้อีกเล็กน้อยโดยเฉพาะอย่างยิ่งเมื่อมันโต้ตอบกับยาชื่อสามัญ . เป็นบิตยากที่จะเป็นเพียงแค่การสร้างโครงสร้างข้อมูลหนึ่งทั่วไปเป็นเป้าหมายการเพิ่มประสิทธิภาพของกลางใน Java ที่ผลงานint, floatฯลฯ ขณะที่หลีกเลี่ยงผู้ UDTs ที่ใหญ่กว่าและมีราคาแพง แต่ส่วนใหญ่มักจะพื้นที่การปฏิบัติงานที่สำคัญจะต้องมีมือกลิ้งโครงสร้างข้อมูลของคุณเอง ปรับเพื่อจุดประสงค์ที่เฉพาะเจาะจงอยู่แล้วดังนั้นจึงเป็นเรื่องน่ารำคาญสำหรับโค้ดที่พยายามให้มีประสิทธิภาพที่ดี แต่ไม่ใช่ประสิทธิภาพสูงสุด

ค่าใช้จ่ายวัตถุ

โปรดทราบว่าค่าใช้จ่ายวัตถุ Java (ข้อมูลเมตาและการสูญเสียพื้นที่เชิงพื้นที่และการสูญเสียตำแหน่งชั่วคราวหลังจากรอบ GC เริ่มต้น) มักจะมีขนาดใหญ่สำหรับสิ่งที่มีขนาดเล็กมาก (เช่นintvs. Integer) ซึ่งถูกเก็บไว้เป็นล้านในโครงสร้างข้อมูลบางส่วน ส่วนใหญ่ต่อเนื่องและเข้าถึงได้ในลูปแน่นมาก ดูเหมือนว่าจะมีความไวมากเกี่ยวกับเรื่องนี้ดังนั้นฉันควรชี้แจงว่าคุณไม่ต้องการที่จะกังวลเกี่ยวกับค่าใช้จ่ายวัตถุสำหรับวัตถุขนาดใหญ่เช่นภาพเพียงแค่วัตถุจิ๋วเช่นพิกเซลเดียว

หากใครรู้สึกสงสัยเกี่ยวกับส่วนนี้ฉันขอแนะนำให้สร้างมาตรฐานระหว่างการสรุปการสุ่มintsล้านครั้งIntegersและการสุ่มแบบสุ่มและการทำเช่นนี้ซ้ำ ๆ (การIntegersจะสับเปลี่ยนหน่วยความจำหลังจากรอบ GC เริ่มต้น)

Ultimate Trick: การออกแบบส่วนต่อประสานที่ทำให้ห้องมีประสิทธิภาพสูงสุด

ดังนั้นเคล็ดลับ Java ขั้นสุดท้ายที่ฉันเห็นถ้าคุณจัดการกับสถานที่ที่รับภาระอย่างหนักเหนือวัตถุขนาดเล็ก (เช่น: a Pixel, 4-vector, 4x4 matrix, a Particle, อาจเป็นไปได้Accountถ้ามันมีขนาดเล็กเพียงไม่กี่ เขตข้อมูล) คือการหลีกเลี่ยงการใช้วัตถุสำหรับสิ่งเล็ก ๆ เหล่านี้และใช้อาร์เรย์ (อาจถูกล่ามโซ่ไว้ด้วยกัน) ของข้อมูลเก่าธรรมดา วัตถุกลายเป็นแล้วอินเตอร์เฟซคอลเลกชันเช่นImage, ParticleSystem, Accountsคอลเลกชันของการฝึกอบรมหรือเวกเตอร์ ฯลฯ แต่ละคนสามารถเข้าถึงได้โดยดัชนีเช่นนี้ยังเป็นหนึ่งในเทคนิคการออกแบบที่ดีที่สุดใน C และ C ++ ตั้งแต่แม้จะไม่ได้ว่าค่าใช้จ่ายวัตถุขั้นพื้นฐานและ หน่วยความจำที่แยกจากกันการสร้างแบบจำลองอินเทอร์เฟซที่ระดับอนุภาคเดียวช่วยป้องกันการแก้ปัญหาที่มีประสิทธิภาพสูงสุด


1
เมื่อพิจารณาว่าประสิทธิภาพที่ไม่ดีในกลุ่มอาจมีโอกาสที่จะได้รับประสิทธิภาพสูงสุดในพื้นที่วิกฤตฉันไม่คิดว่าใครจะเพิกเฉยต่อข้อดีของการมีประสิทธิภาพที่ดีได้อย่างง่ายดาย และกลเม็ดในการเปลี่ยนอาร์เรย์ของ struct ให้เป็นโครงสร้างของอาร์เรย์จะแบ่งย่อยเมื่อค่าทั้งหมด (หรือเกือบทั้งหมด) ที่ประกอบไปด้วยหนึ่งในโครงสร้างดั้งเดิมนั้นจะเข้าถึงได้ในเวลาเดียวกัน BTW: ฉันเห็นว่าคุณไม่ได้สิ่งของโพสต์เก่า ๆ มากมายและเพิ่มคำตอบที่ดีของคุณเองบางครั้งก็เป็นคำตอบที่ดี ;-)
Deduplicator

1
@Dupuplicator หวังว่าฉันไม่ใช่คนที่น่ารำคาญด้วยการชนมากเกินไป! อันนี้มีตัวเล็กนิดหน่อย - บางทีฉันควรปรับปรุงมันหน่อย SOA กับ AoS มักจะเป็นสิ่งที่ยากสำหรับฉัน (การเข้าถึงแบบต่อเนื่องและแบบสุ่ม) ฉันไม่ค่อยรู้ล่วงหน้าว่าควรใช้อันไหนเพราะมักจะมีการผสมผสานของการเข้าถึงแบบลำดับและแบบสุ่มในกรณีของฉัน บทเรียนที่มีค่าที่ฉันมักจะเรียนรู้คือการออกแบบอินเทอร์เฟซที่ปล่อยให้มีพื้นที่ว่างเพียงพอที่จะเล่นกับการแสดงข้อมูล - อินเทอร์เฟซแบบ kinda bulkier ที่มีอัลกอริธึมการแปลงขนาดใหญ่เมื่อเป็นไปได้

1
ฉันเพิ่งสังเกตเห็นเพราะสิ่งต่าง ๆ ช้ามาก และฉันใช้เวลาของฉันกับแต่ละคน
Deduplicator

ฉันสงสัยจริงๆว่าทำไมuser204677หายไป ช่างเป็นคำตอบที่ยอดเยี่ยมมาก
oligofren

3

มีพื้นที่ตรงกลางระหว่างการเพิ่มประสิทธิภาพขนาดเล็กบนมือข้างหนึ่งและตัวเลือกที่ดีของอัลกอริทึมในที่อื่น ๆ

เป็นพื้นที่ของการเพิ่มความเร็วคงที่และสามารถให้ออเดอร์ได้
วิธีการทำเช่นนั้นคือการตัดเวลาเศษส่วนทั้งหมดเช่น 30% แรกจากนั้น 20% ของสิ่งที่เหลือจากนั้น 50% ของที่เหลือและต่อไปเรื่อย ๆ สำหรับการทำซ้ำหลายรอบจนกระทั่งไม่มีอะไรเหลือเลย

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

โดยทั่วไปการเพิ่มความเร็วประกอบด้วยสิ่งต่าง ๆ เช่น:

  • ลดการโทรให้น้อยที่สุดnewด้วยการรวมกำไรและการใช้วัตถุเก่า

  • ตระหนักถึงสิ่งต่าง ๆ ที่กำลังทำอยู่เพื่อเห็นแก่ประโยชน์ส่วนรวมแทนที่จะจำเป็นจริงๆ

  • การแก้ไขโครงสร้างข้อมูลโดยใช้คลาสการรวบรวมต่าง ๆ ที่มีพฤติกรรมแบบ big-O เหมือนกัน แต่ใช้ประโยชน์จากรูปแบบการเข้าถึงที่ใช้จริง

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

  • ยอมรับความไม่แน่นอนจำนวนหนึ่งระหว่างโครงสร้างข้อมูลที่ซ้ำซ้อนแทนที่จะพยายามรักษาให้สอดคล้องกับเหตุการณ์การแจ้งเตือนทั้งหมด

  • ฯลฯ

แต่แน่นอนว่าไม่ควรทำสิ่งเหล่านี้โดยไม่แสดงให้เห็นว่ามีปัญหาก่อนโดยการสุ่มตัวอย่าง


2

Java (เท่าที่ฉันทราบ) ให้คุณไม่สามารถควบคุมตำแหน่งตัวแปรในหน่วยความจำคุณจึงมีเวลาที่ยากขึ้นในการหลีกเลี่ยงสิ่งต่าง ๆ เช่นการแบ่งปันที่ผิดพลาดและการจัดแนวตัวแปร (คุณสามารถแยกชั้นเรียนออกจากสมาชิกที่ไม่ได้ใช้หลายคน) อีกสิ่งหนึ่งที่ฉันไม่คิดว่าคุณสามารถใช้ประโยชน์ได้คือคำแนะนำเช่นmmpauseแต่สิ่งเหล่านี้เป็น CPU เฉพาะและถ้าคุณคิดว่าคุณต้องการ Java อาจไม่ใช่ภาษาที่จะใช้

มีคลาสที่ไม่ปลอดภัยที่ให้ความยืดหยุ่นของ C / C ++ แต่ยังมีอันตรายของ C / C ++

มันอาจช่วยให้คุณดูรหัสประกอบที่ JVM สร้างขึ้นสำหรับรหัสของคุณ

หากต้องการอ่านเกี่ยวกับแอปพลิเคชัน Java ที่ดูรายละเอียดประเภทนี้ดูรหัส Disruptor ที่ออกโดย LMAX


2

คำถามนี้ยากที่จะตอบเพราะมันขึ้นอยู่กับการใช้งานภาษา

โดยทั่วไปจะมีห้องเล็ก ๆ สำหรับ "การเพิ่มประสิทธิภาพขนาดเล็ก" ในทุกวันนี้ เหตุผลหลักคือคอมไพเลอร์ใช้ประโยชน์จากการเพิ่มประสิทธิภาพดังกล่าวในระหว่างการรวบรวม ตัวอย่างเช่นไม่มีความแตกต่างของประสิทธิภาพระหว่างโอเปอเรเตอร์การเพิ่มก่อนและหลังการเพิ่มในสถานการณ์ที่ซีแมนทิกส์เหมือนกัน อีกตัวอย่างหนึ่งจะเป็นตัวอย่างของการวนรอบเช่นนี้for(int i=0; i<vec.size(); i++)ที่หนึ่งสามารถโต้เถียงว่าแทนที่จะเรียกsize()ฟังก์ชั่นสมาชิกในระหว่างการทำซ้ำแต่ละมันจะดีกว่าที่จะได้รับขนาดของเวกเตอร์ก่อนที่วงแล้วเปรียบเทียบกับตัวแปรเดียวที่และทำให้การหลีกเลี่ยงการเรียกใช้ฟังก์ชั่นต่อการทำซ้ำ อย่างไรก็ตามมีหลายกรณีที่คอมไพเลอร์จะตรวจจับกรณีที่โง่และแคชผลลัพธ์ อย่างไรก็ตามนี่เป็นไปได้ก็ต่อเมื่อฟังก์ชั่นไม่มีผลข้างเคียงและคอมไพเลอร์สามารถมั่นใจได้ว่าขนาดเวกเตอร์ยังคงที่ในระหว่างการวนรอบดังนั้นมันจึงใช้กับกรณีที่ค่อนข้างน่ารำคาญ


สำหรับกรณีที่สองฉันไม่คิดว่าคอมไพเลอร์สามารถปรับให้เหมาะสมในอนาคตอันใกล้ การตรวจจับว่าปลอดภัยที่จะเพิ่มประสิทธิภาพ vec.size () ขึ้นอยู่กับการพิสูจน์ว่าขนาดถ้าเวกเตอร์ / สูญหายไม่เปลี่ยนแปลงภายในลูปซึ่งฉันเชื่อว่าไม่สามารถตัดสินใจได้เนื่องจากปัญหาการหยุดทำงาน
Lie Ryan

@LieRyan ฉันได้เห็นหลายกรณี (แบบง่าย) ซึ่งคอมไพเลอร์ได้สร้างไฟล์ไบนารีที่เหมือนกันทุกประการถ้าผลที่ได้รับ "แคช" ด้วยตนเองและถ้าขนาด () ได้รับการเรียก ฉันเขียนโค้ดและปรากฎว่าพฤติกรรมนั้นขึ้นอยู่กับวิธีการทำงานของโปรแกรมเป็นอย่างมาก มีหลายกรณีที่คอมไพเลอร์สามารถรับประกันได้ว่าจะไม่มีความเป็นไปได้ที่ขนาดเวกเตอร์จะเปลี่ยนแปลงในระหว่างการวนรอบและจากนั้นก็มีบางกรณีที่ไม่สามารถรับประกันได้ว่าจะคล้ายกับปัญหาการหยุดชะงักตามที่คุณกล่าว สำหรับตอนนี้ฉันไม่สามารถตรวจสอบการอ้างสิทธิ์ของฉันได้ (การถอดแยกชิ้นส่วน C ++ เป็นความเจ็บปวด) ดังนั้นฉันจึงแก้ไขคำตอบ
zxcdw

2
@Lie Ryan: หลายสิ่งหลายอย่างที่ไม่สามารถตัดสินใจได้ในกรณีทั่วไปนั้นสามารถตัดสินใจได้อย่างสมบูรณ์แบบสำหรับกรณีเฉพาะ แต่เป็นเรื่องธรรมดาและนั่นคือทั้งหมดที่คุณต้องการที่นี่
Michael Borgwardt

@LieRyan หากคุณเรียกconstวิธีการเฉพาะบนเวกเตอร์นี้ฉันค่อนข้างมั่นใจว่าผู้ปรับแต่งคอมไพเลอร์หลายคนจะเข้าใจ
K.Steff

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

1

ผู้คนสามารถให้ตัวอย่างของเทคนิคที่คุณสามารถใช้ใน Java (นอกเหนือจากคอมไพเลอร์ธงง่าย ๆ )

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

ตัวอย่าง Java เพื่อเข้าถึงอาร์เรย์ 1000x1000 ints

พิจารณาโค้ดตัวอย่างด้านล่าง - มันเข้าถึงพื้นที่หน่วยความจำเดียวกัน (อาร์เรย์ 1000x1000 ของ ints) แต่ในลำดับที่แตกต่างกัน ใน mac mini ของฉัน (Core i7, 2.7 GHz) ผลลัพธ์เป็นดังนี้แสดงให้เห็นว่าการสำรวจอาร์เรย์โดยแถวมากกว่าสองเท่าของประสิทธิภาพ (เฉลี่ยมากกว่า 100 รอบต่อวินาที)

Processing columns by rows*** took 4 ms (avg)
Processing rows by columns*** took 10 ms (avg) 

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

สำหรับ Core i7 (sandy bridge) สายแคชมีขนาด 64 ไบต์ดังนั้นการเข้าถึงหน่วยความจำแต่ละครั้งจึงเรียก 64 ไบต์ เนื่องจากการทดสอบครั้งแรกเข้าถึงหน่วยความจำตามลำดับที่คาดเดาได้โปรเซสเซอร์จะดึงข้อมูลล่วงหน้าก่อนที่โปรแกรมจะถูกใช้จริง โดยรวมแล้วจะส่งผลให้เวลาในการเข้าถึงหน่วยความจำลดลงดังนั้นจึงช่วยปรับปรุงประสิทธิภาพ

รหัสตัวอย่าง:

  package test;

  import java.lang.*;

  public class PerfTest {
    public static void main(String[] args) {
      int[][] numbers = new int[1000][1000];
      long startTime;
      long stopTime;
      long elapsedAvg;
      int tries;
      int maxTries = 100;

      // process columns by rows 
      System.out.print("Processing columns by rows");
      for(tries = 0, elapsedAvg = 0; tries < maxTries; tries++) {
       startTime = System.currentTimeMillis();
       for(int r = 0; r < 1000; r++) {
         for(int c = 0; c < 1000; c++) {
           int v = numbers[r][c]; 
         }
       }
       stopTime = System.currentTimeMillis();
       elapsedAvg += ((stopTime - startTime) - elapsedAvg) / (tries + 1);
      }

      System.out.format("*** took %d ms (avg)\n", elapsedAvg);     

      // process rows by columns
      System.out.print("Processing rows by columns");
      for(tries = 0, elapsedAvg = 0; tries < maxTries; tries++) {
       startTime = System.currentTimeMillis();
       for(int c = 0; c < 1000; c++) {
         for(int r = 0; r < 1000; r++) {
           int v = numbers[r][c]; 
         }
       }
       stopTime = System.currentTimeMillis();
       elapsedAvg += ((stopTime - startTime) - elapsedAvg) / (tries + 1);
      }

      System.out.format("*** took %d ms (avg)\n", elapsedAvg);     
    }
  }

1

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

บล็อกที่ให้ข้อมูลสูงในหัวข้อจากผู้เขียนDisruptorขอแนะนำให้อ่าน:

เรามักจะถามว่าทำไมต้องใช้ Java ถ้าคุณต้องการ micro-optimisations มีหลายวิธีในการเร่งความเร็วของฟังก์ชั่นเช่นการใช้ JNA หรือ JNI เพื่อส่งต่อไปยังไลบรารี่ดั้งเดิม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.