ใน java การใช้ byte หรือ short แทน int และ float แทน double มีประสิทธิภาพมากกว่าหรือไม่?


91

ฉันสังเกตเห็นว่าฉันใช้ int และเพิ่มเป็นสองเท่าไม่ว่าจำนวนจะน้อยหรือมาก ดังนั้นใน java การใช้byteหรือshortแทนที่จะใช้intและfloatแทนที่จะมีประสิทธิภาพมากกว่ากันdouble?

สมมติว่าฉันมีโปรแกรมที่มี ints และ doubles มากมาย จะคุ้มไหมที่จะผ่านและเปลี่ยน ints เป็นไบต์หรือกางเกงขาสั้นถ้าฉันรู้ว่าจำนวนนั้นพอดี

ฉันรู้ว่า java ไม่มีประเภทที่ไม่ได้ลงชื่อ แต่มีอะไรเพิ่มเติมที่ฉันสามารถทำได้ถ้าฉันรู้ว่าจำนวนจะเป็นบวกเท่านั้น

โดยประสิทธิภาพส่วนใหญ่ฉันหมายถึงการประมวลผล ฉันคิดว่าตัวเก็บขยะจะเร็วขึ้นมากถ้าตัวแปรทั้งหมดมีขนาดครึ่งหนึ่งและการคำนวณนั้นก็น่าจะเร็วขึ้นด้วย (ฉันเดาว่าเนื่องจากฉันทำงานกับ Android ฉันก็ต้องกังวลเกี่ยวกับ ram ด้วย)

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

ฉันลองใช้กับแอป Android ขนาดเล็กที่มี แต่ไม่ได้สังเกตเห็นความแตกต่างเลย (แม้ว่าฉันจะไม่ได้วัดอะไร "ทางวิทยาศาสตร์")

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

จะคุ้มไหมที่จะทำตั้งแต่เริ่มต้นโครงการใหม่ (ฉันหมายความว่าฉันคิดว่าทุก ๆ อย่างจะช่วยได้ แต่ถ้าเป็นเช่นนั้นอีกครั้งทำไมดูเหมือนไม่มีใครทำ)

คำตอบ:


110

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

คำตอบสั้น ๆ

ใช่คุณคิดผิด ในกรณีส่วนใหญ่จะสร้างความแตกต่างเพียงเล็กน้อยในแง่ของพื้นที่ที่ใช้

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

คำตอบที่ยาวขึ้น

Java Virtual Machine จำลองสแต็กและฟิลด์อ็อบเจ็กต์โดยใช้ออฟเซ็ตที่ (มีผล) ทวีคูณของขนาดเซลล์ดั้งเดิม 32 บิต ดังนั้นเมื่อคุณประกาศตัวแปรโลคัลหรือฟิลด์อ็อบเจ็กต์เป็น (พูด) a byteตัวแปร / ฟิลด์จะถูกเก็บไว้ในเซลล์ 32 บิตเช่นเดียวกับintไฟล์.

มีสองข้อยกเว้นสำหรับสิ่งนี้:

  • longและdoubleค่าต้องการเซลล์ 32 บิตดั้งเดิม 2 เซลล์
  • อาร์เรย์ของชนิดดั้งเดิมจะแสดงในรูปแบบบรรจุดังนั้น (ตัวอย่างเช่น) อาร์เรย์ของไบต์จะมี 4 ไบต์ต่อคำ 32 บิต

ดังนั้นจึงอาจจะมีการใช้งานที่คุ้มค่าการเพิ่มประสิทธิภาพของlongและdouble... และอาร์เรย์ใหญ่ของวิทยาการ แต่โดยทั่วไปไม่มี

ในทฤษฎี JIT อาจจะสามารถเพิ่มประสิทธิภาพนี้ แต่ในทางปฏิบัติผมไม่เคยได้ยิน JIT ที่ไม่ อุปสรรคอย่างหนึ่งคือโดยทั่วไป JIT จะไม่สามารถทำงานได้จนกว่าจะมีการสร้างอินสแตนซ์ของคลาสที่กำลังคอมไพล์ หาก JIT ปรับเลย์เอาต์หน่วยความจำให้เหมาะสมคุณอาจมี "รสชาติ" ของวัตถุระดับเดียวกันสอง (หรือมากกว่า) ... และนั่นจะทำให้เกิดปัญหาใหญ่หลวง


การทบทวน

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

ฉันคิดว่าคำอธิบายคือ JIT น่าจะทำการคูณโดยใช้คำแนะนำการคูณ 32 บิตในแต่ละกรณี แต่ในbyteและshortกรณีที่จะรันพิเศษคำแนะนำในการแปลงค่า 32 บิตกลางไปbyteหรือshortในวงแต่ละซ้ำ (ตามทฤษฎีแล้วการแปลงนั้นสามารถทำได้ครั้งเดียวในตอนท้ายของลูป ... แต่ฉันสงสัยว่าเครื่องมือเพิ่มประสิทธิภาพจะสามารถคิดออกได้)

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


30
+1 อย่าปรับให้เหมาะสมเว้นแต่คุณจะมีหลักฐานชัดเจนว่ามีปัญหาด้านประสิทธิภาพ
โบฮีเมีย

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

@meriton - ฉันค่อนข้างมั่นใจว่าเค้าโครงวัตถุถูกกำหนดที่เวลาโหลดคลาสและจะไม่เปลี่ยนแปลงหลังจากนั้น ดูส่วน "พิมพ์ละเอียด" ในคำตอบของฉัน หากเค้าโครงหน่วยความจำจริงเปลี่ยนไปเมื่อโค้ดเป็น JITed JVM จะจัดการได้ยากมาก (เมื่อฉันบอกว่า JIT อาจปรับรูปแบบให้เหมาะสมนั่นเป็นสมมุติฐานและใช้ไม่ได้จริง ... ซึ่งสามารถอธิบายได้ว่าทำไมฉันไม่เคยได้ยินมาก่อนว่า JIT ทำอย่างนั้นจริง ๆ )
Stephen C

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

@meriton - ข้อมูลจำเพาะของ JVM กำลังพูดถึง "virtual machine word offets" ภายในเฟรม / วัตถุในเครื่อง วิธีการแมปเหล่านี้กับออฟเซ็ตของเครื่องจริงไม่ได้ระบุไว้ อันที่จริงไม่สามารถระบุได้ ... เนื่องจากอาจมีข้อกำหนดการจัดตำแหน่งฟิลด์เฉพาะฮาร์ดแวร์
Stephen C

29

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

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

อย่างไรก็ตามโดยทั่วไปแล้วการใช้ประเภทดั้งเดิมที่เล็กกว่านั้นไม่ได้เร็วกว่า

เพื่อแสดงให้เห็นถึงมาตรฐานดังต่อไปนี้:

package tools.bench;

import java.math.BigDecimal;

public abstract class Benchmark {

    final String name;

    public Benchmark(String name) {
        this.name = name;
    }

    abstract int run(int iterations) throws Throwable;

    private BigDecimal time() {
        try {
            int nextI = 1;
            int i;
            long duration;
            do {
                i = nextI;
                long start = System.nanoTime();
                run(i);
                duration = System.nanoTime() - start;
                nextI = (i << 1) | 1; 
            } while (duration < 100000000 && nextI > 0);
            return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }   

    @Override
    public String toString() {
        return name + "\t" + time() + " ns";
    }

    public static void main(String[] args) throws Exception {
        Benchmark[] benchmarks = {
            new Benchmark("int multiplication") {
                @Override int run(int iterations) throws Throwable {
                    int x = 1;
                    for (int i = 0; i < iterations; i++) {
                        x *= 3;
                    }
                    return x;
                }
            },
            new Benchmark("short multiplication") {                   
                @Override int run(int iterations) throws Throwable {
                    short x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x *= 3;
                    }
                    return x;
                }
            },
            new Benchmark("byte multiplication") {                   
                @Override int run(int iterations) throws Throwable {
                    byte x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x *= 3;
                    }
                    return x;
                }
            },
            new Benchmark("int[] traversal") {                   
                @Override int run(int iterations) throws Throwable {
                    int[] x = new int[iterations];
                    for (int i = 0; i < iterations; i++) {
                        x[i] = i;
                    }
                    return x[x[0]];
                }
            },
            new Benchmark("short[] traversal") {                   
                @Override int run(int iterations) throws Throwable {
                    short[] x = new short[iterations];
                    for (int i = 0; i < iterations; i++) {
                        x[i] = (short) i;
                    }
                    return x[x[0]];
                }
            },
            new Benchmark("byte[] traversal") {                   
                @Override int run(int iterations) throws Throwable {
                    byte[] x = new byte[iterations];
                    for (int i = 0; i < iterations; i++) {
                        x[i] = (byte) i;
                    }
                    return x[x[0]];
                }
            },
        };
        for (Benchmark bm : benchmarks) {
            System.out.println(bm);
        }
    }
}

ซึ่งพิมพ์บนสมุดบันทึกเก่าของฉัน (เพิ่มช่องว่างเพื่อปรับคอลัมน์):

int       multiplication    1.530 ns
short     multiplication    2.105 ns
byte      multiplication    2.483 ns
int[]     traversal         5.347 ns
short[]   traversal         4.760 ns
byte[]    traversal         2.064 ns

อย่างที่คุณเห็นความแตกต่างของประสิทธิภาพค่อนข้างน้อย การเพิ่มประสิทธิภาพอัลกอริทึมมีความสำคัญมากกว่าการเลือกประเภทดั้งเดิม


3
แทนที่จะพูดว่า "สะดุดตาที่สุดเมื่อใช้อาร์เรย์" ฉันคิดว่ามันอาจจะง่ายกว่าที่จะพูดแบบนั้นshortและbyteมีประสิทธิภาพมากกว่าเมื่อเก็บไว้ในอาร์เรย์ที่มีขนาดใหญ่พอที่จะมีความสำคัญ (ยิ่งอาร์เรย์มีขนาดใหญ่เท่าใดประสิทธิภาพbyte[2]ก็จะยิ่งแตกต่างกันมากขึ้นเท่านั้นอาจจะมากกว่า หรือมีประสิทธิภาพน้อยกว่าint[2]แต่ไม่มากพอที่จะมีความสำคัญทางใดทางหนึ่ง) intแต่ที่แต่ละค่าจะถูกเก็บไว้อย่างมีประสิทธิภาพมากขึ้นเป็น
supercat

2
สิ่งที่ฉันตรวจสอบ: เกณฑ์มาตรฐานเหล่านั้นใช้ int ('3') เป็นตัวประกอบหรือตัวถูกดำเนินการกำหนดเสมอ (ตัวแปรลูปแล้วแคสต์) สิ่งที่ฉันทำคือใช้ปัจจัยการพิมพ์ / ตัวถูกดำเนินการกำหนดขึ้นอยู่กับชนิด lvalue: int mult 76.481 ns int mult (พิมพ์) 72.581 ns สั้น mult 87.908 ns สั้น mult (พิมพ์) 90.772 ns ไบต์ mult 87.859 ns ไบต์ mult (พิมพ์) 89.524 ns int [] trav 88.905 ns int [] trav (พิมพ์) 89.126 ns short [] trav 10.563 ns short [] trav (พิมพ์) 10.039 ns byte [] trav 8.356 ns byte [] trav (พิมพ์) 8.338 ns ฉันคิดว่ามี a การหล่อที่ไม่จำเป็นมากมาย การทดสอบเหล่านั้นทำงานบนแท็บ Android
Bondax

5

การใช้byteแทนintสามารถเพิ่มประสิทธิภาพได้หากคุณใช้งานเป็นจำนวนมาก นี่คือการทดลอง:

import java.lang.management.*;

public class SpeedTest {

/** Get CPU time in nanoseconds. */
public static long getCpuTime() {
    ThreadMXBean bean = ManagementFactory.getThreadMXBean();
    return bean.isCurrentThreadCpuTimeSupported() ? bean
            .getCurrentThreadCpuTime() : 0L;
}

public static void main(String[] args) {
    long durationTotal = 0;
    int numberOfTests=0;

    for (int j = 1; j < 51; j++) {
        long beforeTask = getCpuTime();
        // MEASURES THIS AREA------------------------------------------
        long x = 20000000;// 20 millions
        for (long i = 0; i < x; i++) {
                           TestClass s = new TestClass(); 

        }
        // MEASURES THIS AREA------------------------------------------
        long duration = getCpuTime() - beforeTask;
        System.out.println("TEST " + j + ": duration = " + duration + "ns = "
                + (int) duration / 1000000);
        durationTotal += duration;
        numberOfTests++;
    }
    double average = durationTotal/numberOfTests;
    System.out.println("-----------------------------------");
    System.out.println("Average Duration = " + average + " ns = "
            + (int)average / 1000000 +" ms (Approximately)");


}

}

คลาสนี้ทดสอบความเร็วในการสร้างไฟล์TestClass. การทดสอบแต่ละครั้งทำ 20 ล้านครั้งและมีการทดสอบ 50 ครั้ง

นี่คือ TestClass:

 public class TestClass {
     int a1= 5;
     int a2= 5; 
     int a3= 5;
     int a4= 5; 
     int a5= 5;
     int a6= 5; 
     int a7= 5;
     int a8= 5; 
     int a9= 5;
     int a10= 5; 
     int a11= 5;
     int a12=5; 
     int a13= 5;
     int a14= 5; 
 }

ฉันเรียกใช้ไฟล์ SpeedTestชั้นเรียนและในที่สุดก็ได้สิ่งนี้:

 Average Duration = 8.9625E8 ns = 896 ms (Approximately)

ตอนนี้ฉันกำลังเปลี่ยน ints เป็นไบต์ใน TestClass และเรียกใช้อีกครั้ง นี่คือผลลัพธ์:

 Average Duration = 6.94375E8 ns = 694 ms (Approximately)

ฉันเชื่อว่าการทดลองนี้แสดงให้เห็นว่าหากคุณสร้างตัวแปรจำนวนมากการใช้ byte แทน int สามารถเพิ่มประสิทธิภาพได้


4
โปรดทราบว่าเกณฑ์มาตรฐานนี้เป็นการวัดต้นทุนที่เกี่ยวข้องกับการจัดสรรและการก่อสร้างเท่านั้นและเฉพาะกรณีของคลาสที่มีฟิลด์จำนวนมาก ถ้าปรับปรุงการดำเนินงานคณิตศาสตร์ / ทำงานในทุ่งนาผล @ Meriton ของแนะนำว่าbyteอาจจะช้ากว่า >> << intกว่า
Stephen C

จริงอยู่ฉันควรจะพูดให้กระจ่างดีกว่านี้
WVrock

2

โดยทั่วไปแล้วไบต์จะถือว่าเป็น 8 บิต โดยทั่วไปจะถือว่าสั้นเป็น 16 บิต

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

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

ณ จุดนี้ขึ้นอยู่กับวิธีการใช้งานฮาร์ดแวร์ อย่างไรก็ตามจากที่ฉันเคยคิดมาความเร็วที่ดีที่สุดคือการจัดเก็บสิ่งต่างๆเป็นชิ้น ๆ ซึ่งสะดวกสบายสำหรับ CPU ของคุณในการใช้งาน โปรเซสเซอร์ 64 บิตชอบจัดการกับองค์ประกอบ 64 บิตและสิ่งที่น้อยกว่านั้นมักต้องใช้ "เวทมนตร์ทางวิศวกรรม" เพื่อแสร้งทำเป็นว่ามันชอบจัดการกับพวกมัน


3
ฉันไม่แน่ใจว่าคุณหมายถึงอะไรโดย "เวทมนตร์ทางวิศวกรรม" ... โปรเซสเซอร์สมัยใหม่ส่วนใหญ่ / ทั้งหมดมีคำแนะนำที่รวดเร็วในการโหลดไบต์และขยายขนาดเพื่อจัดเก็บหนึ่งจากการลงทะเบียนแบบเต็มความกว้างและการทำไบต์กว้าง หรือเลขคณิตแบบความกว้างสั้นในส่วนของการลงทะเบียนแบบเต็มความกว้าง หากคุณพูดถูกมันก็สมเหตุสมผลหากเป็นไปได้ที่จะแทนที่ ints ทั้งหมดด้วย longs บนโปรเซสเซอร์ 64 บิต
Ed Staub

ฉันสามารถจินตนาการได้ว่าเป็นเรื่องจริง ฉันจำได้ว่าในตัวจำลอง Motorola 68k ที่เราใช้การทำงานส่วนใหญ่สามารถทำงานได้กับค่า 16 บิตในขณะที่ไม่ใช่ 32 บิตหรือ 64 บิต ฉันคิดว่านั่นหมายความว่าระบบมีขนาดค่าที่ต้องการซึ่งสามารถดึงข้อมูลได้อย่างเหมาะสมที่สุด แม้ว่าฉันจะจินตนาการได้ว่าโปรเซสเซอร์ 64 บิตที่ทันสมัยสามารถดึงข้อมูล 8 บิต 16 บิต 32 บิตและ 64 บิตได้อย่างง่ายดายในกรณีนี้เป็นแบบไม่ออก ขอบคุณที่ชี้ให้เห็น
Dmitry

"... โดยทั่วไปถือว่าเป็น ... " - ที่จริงมีการระบุไว้อย่างชัดเจน >> ระบุ << เป็นขนาดเหล่านั้น ใน Java และบริบทของคำถามนี้คือ Java
Stephen C

โปรเซสเซอร์จำนวนมากยังใช้จำนวนรอบเท่ากันในการจัดการและเข้าถึงข้อมูลที่มีขนาดไม่เท่าคำดังนั้นจึงไม่คุ้มที่จะต้องกังวลเว้นแต่คุณจะวัดจาก JVM และแพลตฟอร์มเฉพาะ
drrob

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

2

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

void spin() {
 int i;
 for (i = 0; i < 100; i++) {
 ; // Loop body is empty
 }
}

เดียวกันจะถูกแปลงเป็นรหัสเครื่องดังต่อไปนี้

0 iconst_0 // Push int constant 0
1 istore_1 // Store into local variable 1 (i=0)
2 goto 8 // First time through don't increment
5 iinc 1 1 // Increment local variable 1 by 1 (i++)
8 iload_1 // Push local variable 1 (i)
9 bipush 100 // Push int constant 100
11 if_icmplt 5 // Compare and loop if less than (i < 100)
14 return // Return void when done

ตอนนี้ให้พิจารณาเปลี่ยน int เป็น short ดังต่อไปนี้

void sspin() {
 short i;
 for (i = 0; i < 100; i++) {
 ; // Loop body is empty
 }
}

รหัสเครื่องที่เกี่ยวข้องจะเปลี่ยนไปดังนี้:

0 iconst_0
1 istore_1
2 goto 10
5 iload_1 // The short is treated as though an int
6 iconst_1
7 iadd
8 i2s // Truncate int to short
9 istore_1
10 iload_1
11 bipush 100
13 if_icmplt 5
16 return

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

ตอนนี้เหตุผลที่อ้างว่าไม่ให้การสนับสนุนโดยตรงดังนี้:

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

ยกมาจากข้อกำหนด JVM ที่แสดงไว้ที่นี่ (หน้า 58)


รหัสเหล่านี้ถูกถอดแยกส่วน เช่นคำแนะนำเสมือน JVM javacคอมไพเลอร์ไม่ได้รับการปรับแต่งให้เหมาะสมและคุณไม่สามารถสรุปข้อสรุปที่เชื่อถือได้ว่าโปรแกรมจะดำเนินการอย่างไรในชีวิตจริง คอมไพเลอร์ JIT รวบรวม bytecodes เหล่านี้ตามคำแนะนำของเครื่องเนทีฟจริงและทำการปรับให้เหมาะสมอย่างจริงจังในกระบวนการนี้ หากคุณต้องการวิเคราะห์ประสิทธิภาพของโค้ดคุณต้องตรวจสอบคำแนะนำของโค้ดเนทีฟ (และมีความซับซ้อนเนื่องจากคุณต้องคำนึงถึงพฤติกรรมการกำหนดเวลาของไปป์ไลน์ x86_64 แบบหลายขั้นตอน)
Stephen C

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

นี่คือข้อเท็จจริงประการหนึ่งที่จะสนับสนุนคำพูดของฉัน คุณจะไม่พบตัวเลขเวลา (ที่น่าเชื่อถือ) ใด ๆ ที่บอกคุณว่าแต่ละคำสั่งของ JVM bytecode ใช้เวลากี่นาฬิกา ไม่เผยแพร่โดย Oracle หรือซัพพลายเออร์ JVM อื่น ๆ อย่างแน่นอน นอกจากนี้โปรดอ่านstackoverflow.com/questions/1397009
Stephen C

ฉันพบเอกสารเก่า (2008) ที่มีคนพยายามพัฒนาแบบจำลองอิสระของแพลตฟอร์มเพื่อทำนายประสิทธิภาพของลำดับไบต์โค้ด พวกเขาอ้างว่าการคาดการณ์ของพวกเขาลดลง 25% เมื่อเทียบกับการวัด RDTSC .... ใน Pentium และพวกเขากำลังเรียกใช้ JVM โดยปิดการรวบรวม JIT! อ้างอิง: sciencedirect.com/science/article/pii/S1571066108004581
Stephen C

ฉันแค่สับสนที่นี่ คำตอบของฉันไม่สนับสนุนข้อเท็จจริงที่คุณระบุไว้ในส่วนการทบทวนหรือไม่?
Manish Bansal

0

ความแตกต่างแทบไม่เห็น! มันเป็นคำถามของการออกแบบความเหมาะสมความสม่ำเสมอความเคยชิน ฯลฯ ... บางครั้งมันก็เป็นเรื่องของรสนิยม เมื่อสิ่งที่คุณสนใจคือโปรแกรมของคุณจะเริ่มทำงานและแทนที่ไฟล์floatสำหรับintความถูกต้องที่จะไม่เป็นอันตรายต่อความถูกต้องฉันไม่เห็นประโยชน์ที่จะทำอย่างใดอย่างหนึ่งเว้นแต่คุณจะสามารถแสดงให้เห็นว่าการใช้ประเภทใดประเภทหนึ่งจะเปลี่ยนแปลงประสิทธิภาพ การปรับแต่งประสิทธิภาพตามประเภทที่แตกต่างกันใน 2 หรือ 3 ไบต์เป็นสิ่งสุดท้ายที่คุณควรใส่ใจ Donald Knuth เคยกล่าวไว้ว่า: "การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากเหง้าของความชั่วร้ายทั้งหมด" (ไม่แน่ใจว่าเป็นเขาแก้ไขถ้าคุณมีคำตอบ)


5
Nit: การfloat ไม่สามารถเป็นตัวแทนของจำนวนเต็มทั้งหมดintสามารถ; และไม่สามารถintแสดงค่าที่ไม่ใช่จำนวนเต็มที่floatสามารถ นั่นคือในขณะที่ค่า int ทั้งหมดเป็นส่วนย่อยของค่าแบบยาว แต่ค่า int ไม่ใช่ส่วนย่อยของ float และ float คือไม่ใช่ส่วนย่อยของ int

ฉันคาดว่าผู้ตอบควรจะเขียนsubstituting a float for a doubleหากผู้ตอบควรแก้ไขคำตอบ หากไม่ใช่ผู้ตอบควรแขวนคอด้วยความอับอายและกลับไปสู่พื้นฐานด้วยเหตุผลที่ระบุโดย @pst และเหตุผลอื่น ๆ อีกมากมาย
เครื่องหมายประสิทธิภาพสูง

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