Java JIT โกงเมื่อใช้งานรหัส JDK หรือไม่


405

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

//import java.math.BigInteger;

public class MultiplyTest {
    public static void main(String[] args) {
        Random r = new Random(1);
        long tm = 0, count = 0,result=0;
        for (int i = 0; i < 400000; i++) {
            int s1 = 400, s2 = 400;
            BigInteger a = new BigInteger(s1 * 8, r), b = new BigInteger(s2 * 8, r);
            long tm1 = System.nanoTime();
            BigInteger c = a.multiply(b);
            if (i > 100000) {
                tm += System.nanoTime() - tm1;
                count++;
            }
            result+=c.bitLength();
        }
        System.out.println((tm / count) + "nsec/mul");
        System.out.println(result); 
    }
}

เมื่อฉันรันสิ่งนี้ (jdk 1.8.0_144-b01 บน MacOS) มันจะแสดงผล:

12089nsec/mul
2559044166

เมื่อฉันรันด้วยการนำเข้าบรรทัดที่ไม่ใส่เครื่องหมายข้อคิดเห็น:

4098nsec/mul
2559044166

เร็วเกือบสามเท่าเมื่อใช้ BigInteger รุ่น JDK เทียบกับรุ่นของฉันแม้ว่าจะใช้รหัสเดียวกันก็ตาม

ฉันได้ตรวจสอบ bytecode ด้วย javap และเปรียบเทียบเอาท์พุทคอมไพเลอร์เมื่อทำงานกับตัวเลือก:

-Xbatch -XX:-TieredCompilation -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions 
-XX:+PrintInlining -XX:CICompilerCount=1

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


29
น่าสนใจ 1. ผลลัพธ์ที่สอดคล้องกัน (หรือเพียงแค่โชคดีสุ่ม) 2. คุณสามารถลองใช้ JVM หลังจากอุ่นขึ้นได้หรือไม่? 3. คุณสามารถกำจัดปัจจัยสุ่มและให้ชุดข้อมูลเช่นเดียวกับอินพุตสำหรับการทดสอบทั้งสองได้หรือไม่
Jigar Joshi

7
คุณลองใช้การวัดประสิทธิภาพด้วย JMH openjdk.java.net/projects/code-tools/jmhหรือไม่? ไม่ใช่เรื่องง่ายที่จะทำการวัดด้วยตนเองอย่างถูกต้อง (อุ่นเครื่องและทุกอย่างนั้น)
Roman Puchkovskiy

2
ใช่มันสอดคล้องกันมาก ถ้าฉันปล่อยให้มันทำงานเป็นเวลา 10 นาทีฉันก็ยังคงได้รับความแตกต่างเหมือนเดิม เมล็ดสุ่มแบบคงที่ช่วยให้มั่นใจได้ว่าทั้งสองจะได้รับชุดข้อมูลเดียวกัน
Koen Hendrikx

5
คุณอาจยังต้องการ JMH ในกรณี และคุณควรติดตั้ง BigInteger ที่ได้รับการดัดแปลงของคุณเพื่อให้ผู้อื่นสามารถทำซ้ำการทดสอบของคุณและตรวจสอบว่าคุณกำลังทำงานในสิ่งที่คุณคิดว่าคุณกำลังทำงานอยู่
pvg

คำตอบ:


529

ใช่ HotSpot JVM เป็น "การโกง" เพราะมีวิธีการพิเศษบางอย่างBigIntegerที่คุณจะไม่พบในโค้ด Java เมธอดเหล่านี้เรียกว่าJVM intrinsics intrinsics

โดยเฉพาะอย่างยิ่งBigInteger.multiplyToLenเป็นวิธี instrinsic ใน HotSpot มีการใช้งานแอสเซมบลีที่เขียนโค้ดด้วยมือเป็นพิเศษในฐานซอร์ส JVM แต่สำหรับสถาปัตยกรรม x86-64 เท่านั้น

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

PSนี่คือรายการของวิธีการที่อยู่ภายใน HotSpot อื่น ๆ


141

ในJava 8นี่เป็นวิธีที่แท้จริง วิธีแก้ไขเวอร์ชันเล็กน้อย:

 private static BigInteger test() {

    Random r = new Random(1);
    BigInteger c = null;
    for (int i = 0; i < 400000; i++) {
        int s1 = 400, s2 = 400;
        BigInteger a = new BigInteger(s1 * 8, r), b = new BigInteger(s2 * 8, r);
        c = a.multiply(b);
    }
    return c;
}

ใช้สิ่งนี้ด้วย:

 java -XX:+UnlockDiagnosticVMOptions  
      -XX:+PrintInlining 
      -XX:+PrintIntrinsics 
      -XX:CICompilerCount=2 
      -XX:+PrintCompilation   
       <YourClassName>

สิ่งนี้จะพิมพ์บรรทัดจำนวนมากและหนึ่งในนั้นคือ:

 java.math.BigInteger::multiplyToLen (216 bytes)   (intrinsic)

ในJava 9ในอีกทางหนึ่งวิธีการที่ดูเหมือนจะไม่เป็นที่แท้จริงอีกต่อไป แต่ในทางกลับกันมันเรียกวิธีการที่เป็นที่แท้จริง:

 @HotSpotIntrinsicCandidate
 private static int[] implMultiplyToLen

ดังนั้นการเรียกใช้รหัสเดียวกันภายใต้ Java 9 (ด้วยพารามิเตอร์เดียวกัน) จะเปิดเผย:

java.math.BigInteger::implMultiplyToLen (216 bytes)   (intrinsic)

ภายใต้มันเป็นรหัสเดียวกันสำหรับวิธีนี้ - เป็นเพียงการตั้งชื่อที่แตกต่างกันเล็กน้อย

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