ประการแรก JVM ส่วนใหญ่มีคอมไพเลอร์ดังนั้น "การตีความ bytecode" จึงค่อนข้างยาก (อย่างน้อยก็ในรหัสเกณฑ์มาตรฐาน - มันค่อนข้างหายากในชีวิตจริงโดยที่โค้ดของคุณมักจะเป็นวงเล็ก ๆ น้อย ๆ ที่ทำซ้ำบ่อยครั้งมาก )
ประการที่สองจำนวนพอสมควรของมาตรฐานที่เกี่ยวข้องปรากฏว่าค่อนข้างลำเอียง (ไม่ว่าจะโดยเจตนาหรือไร้ความสามารถฉันไม่สามารถพูดได้จริงๆ) ตัวอย่างเช่นเมื่อหลายปีก่อนฉันมองไปที่ซอร์สโค้ดที่ลิงก์จากลิงค์ใดลิงค์หนึ่งที่คุณโพสต์ มันมีรหัสดังนี้:
init0 = (int*)calloc(max_x,sizeof(int));
init1 = (int*)calloc(max_x,sizeof(int));
init2 = (int*)calloc(max_x,sizeof(int));
for (x=0; x<max_x; x++) {
init2[x] = 0;
init1[x] = 0;
init0[x] = 0;
}
เนื่องจากcalloc
มีหน่วยความจำที่เป็นศูนย์อยู่แล้วการใช้for
ลูปเพื่อทำให้เป็นศูนย์อีกครั้งนั้นไม่มีประโยชน์อย่างเห็นได้ชัด ตามนี้ (ถ้าหน่วยความจำทำหน้าที่) โดยการกรอกข้อมูลในหน่วยความจำด้วยข้อมูลอื่น ๆ อยู่แล้ว (และไม่มีการพึ่งพามันเป็นศูนย์) ดังนั้นการ zeroing ทั้งหมดจึงไม่จำเป็นเลย การแทนที่โค้ดข้างต้นด้วยวิธีง่าย ๆmalloc
(เช่นเดียวกับบุคคลที่มีสติจะเริ่มต้นด้วย) ปรับปรุงความเร็วของรุ่น C ++ ให้มากพอที่จะเอาชนะเวอร์ชัน Java (โดยมีระยะขอบที่กว้างพอสมควร
พิจารณา (ตัวอย่างอื่น) methcall
เกณฑ์มาตรฐานที่ใช้ในรายการบล็อกในลิงก์สุดท้ายของคุณ แม้จะมีชื่อ (และสิ่งต่าง ๆ ที่อาจมองเห็น) แต่รุ่น C ++ นี้ไม่ได้วัดค่าใช้จ่ายเกี่ยวกับการเรียกใช้เมธอดมากนัก ส่วนของรหัสที่มีความสำคัญอยู่ในระดับสลับ:
class Toggle {
public:
Toggle(bool start_state) : state(start_state) { }
virtual ~Toggle() { }
bool value() {
return(state);
}
virtual Toggle& activate() {
state = !state;
return(*this);
}
bool state;
};
state = !state;
ส่วนที่สำคัญจะออกมาเป็น พิจารณาสิ่งที่เกิดขึ้นเมื่อเราเปลี่ยนรหัสเพื่อเข้ารหัสสถานะint
แทนbool
:
class Toggle {
enum names{ bfalse = -1, btrue = 1};
const static names values[2];
int state;
public:
Toggle(bool start_state) : state(values[start_state])
{ }
virtual ~Toggle() { }
bool value() { return state==btrue; }
virtual Toggle& activate() {
state = -state;
return(*this);
}
};
นี้มีการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ ที่ช่วยเพิ่มความเร็วโดยรวมโดยประมาณ5: 1 อัตรากำไรขั้นต้น แม้ว่ามาตรฐานได้ตั้งใจที่จะวัดเวลาวิธีการโทรในความเป็นจริงมากที่สุดของสิ่งที่มันถูกวัดเป็นเวลาในการแปลงระหว่างและint
bool
ฉันเห็นด้วยอย่างแน่นอนว่าต้นฉบับที่ไร้ประสิทธิภาพแสดงให้เห็นว่าเป็นเรื่องที่โชคร้าย - แต่เนื่องจากดูเหมือนว่าจะเกิดขึ้นจริงในรหัสจริงและความสะดวกในการแก้ไขเมื่อ / ถ้ามันเกิดขึ้นฉันไม่ค่อยมีเวลาคิด ของมันมีความหมายมาก
ในกรณีที่มีใครตัดสินใจที่จะเรียกใช้การวัดประสิทธิภาพที่เกี่ยวข้องอีกครั้งฉันควรเพิ่มว่ามีการปรับเปลี่ยนเล็กน้อยในรุ่น Java ที่สร้างขึ้นเล็กน้อยอย่างเท่าเทียมกัน (หรืออย่างน้อยก็ครั้งเดียวที่สร้างขึ้น - ฉันไม่ได้ทำการทดสอบ JVM เมื่อเร็ว ๆ นี้เพื่อยืนยันว่าพวกเขายังคงทำอยู่) การปรับปรุงที่ค่อนข้างเป็นธรรมในเวอร์ชั่น Java เช่นกัน เวอร์ชัน Java มี NthToggle :: enable () ที่มีลักษณะดังนี้:
public Toggle activate() {
this.counter += 1;
if (this.counter >= this.count_max) {
this.state = !this.state;
this.counter = 0;
}
return(this);
}
การเปลี่ยนสิ่งนี้เพื่อเรียกใช้ฟังก์ชันพื้นฐานแทนที่จะจัดการthis.state
โดยตรงให้การปรับปรุงความเร็วค่อนข้างมาก (แต่ไม่เพียงพอที่จะติดตามเวอร์ชัน C ++ ที่แก้ไขแล้ว)
ดังนั้นสิ่งที่เราท้ายที่สุดก็คือสมมติฐานที่ผิดพลาดเกี่ยวกับการตีความรหัสไบต์เทียบกับมาตรฐานที่เลวร้ายที่สุด (ฉัน) เคยเห็นมาบ้าง ไม่ให้ผลลัพธ์ที่มีความหมาย
ประสบการณ์ของฉันคือว่าด้วยโปรแกรมเมอร์ที่มีประสบการณ์เท่ากันให้ความสำคัญกับการปรับให้เหมาะสม C ++ จะเอาชนะ Java ได้บ่อยกว่า แต่ - (อย่างน้อยระหว่างสอง) ภาษาจะไม่ค่อยสร้างความแตกต่างเท่านักเขียนโปรแกรมและการออกแบบ มาตรฐานที่อ้างถึงบอกเราเพิ่มเติมเกี่ยวกับความสามารถ (ใน) / (dis) ความซื่อสัตย์ของผู้เขียนมากกว่าที่พวกเขาทำเกี่ยวกับภาษาที่พวกเขาอ้างว่าเป็นมาตรฐาน
[แก้ไข: ตามที่กล่าวไว้ในที่เดียวข้างต้น แต่ไม่เคยระบุอย่างที่ควรจะเป็นโดยตรงผลลัพธ์ที่ฉันอ้างถึงคือสิ่งที่ฉันได้รับเมื่อฉันทดสอบสิ่งนี้เมื่อ 5 ปีก่อนโดยใช้ C ++ และ Java implementations ที่เป็นปัจจุบันในเวลานั้น . ฉันไม่ได้ทำการทดสอบซ้ำกับการใช้งานปัจจุบัน อย่างไรก็ตามอย่างรวดเร็วบ่งชี้ว่ารหัสไม่ได้รับการแก้ไขดังนั้นสิ่งที่จะมีการเปลี่ยนแปลงจะเป็นความสามารถของคอมไพเลอร์เพื่อปกปิดปัญหาในรหัส]
อย่างไรก็ตามหากเราไม่สนใจตัวอย่าง Java เป็นไปได้ที่โค้ดที่แปลนั้นจะทำงานได้เร็วกว่าโค้ดที่คอมไพล์ (แม้ว่าจะยากและค่อนข้างผิดปกติ)
สิ่งที่เกิดขึ้นตามปกติคือรหัสที่ถูกตีความมีขนาดเล็กกว่ารหัสเครื่องมากหรือใช้งานบน CPU ที่มีแคชข้อมูลขนาดใหญ่กว่าโค้ดแคช
ในกรณีเช่นนี้ล่ามขนาดเล็ก (เช่นล่ามชั้นในของการใช้งาน Forth) อาจจะพอดีกับแคชโค้ดทั้งหมดและโปรแกรมที่ล่ามนั้นพอดีกับแคชข้อมูลทั้งหมด โดยทั่วไปแคชจะเร็วกว่าหน่วยความจำหลักอย่างน้อย 10 เท่าและบ่อยกว่านั้นมาก (ปัจจัย 100 ไม่ได้ยากยิ่งกว่านี้)
ดังนั้นหากแคชเร็วกว่าหน่วยความจำหลักโดยใช้ N เป็นปัจจัยและใช้เวลาน้อยกว่า N คำแนะนำในการใช้รหัสเครื่องเพื่อใช้รหัสไบต์แต่ละรหัสไบต์ควรชนะ (ฉันลดความซับซ้อน แต่ฉันคิดว่าความคิดทั่วไปควรจะยังคง ชัดเจน)