Saya perhatikan saya selalu menggunakan int dan doubles tidak peduli seberapa kecil atau besar angkanya. Jadi di java, apakah lebih efisien untuk digunakan byte
atau short
daripada int
dan float
daripada double
?
Jadi asumsikan saya memiliki program dengan banyak int dan ganda. Akankah layak untuk melalui dan mengubah int saya menjadi byte atau short jika saya tahu jumlahnya akan cocok?
Saya tahu java tidak memiliki tipe unsigned tetapi adakah hal tambahan yang dapat saya lakukan jika saya tahu angkanya hanya akan positif?
Yang saya maksud dengan efisien adalah pemrosesan. Saya berasumsi bahwa pengumpul sampah akan jauh lebih cepat jika semua variabel berukuran setengah dan perhitungan itu mungkin juga akan lebih cepat. (Saya kira karena saya sedang mengerjakan android, saya perlu sedikit khawatir tentang ram juga)
(Saya akan berasumsi pengumpul sampah hanya berurusan dengan Objek dan bukan primitif tetapi masih menghapus semua primitif di objek yang ditinggalkan, kan?)
Saya mencobanya dengan aplikasi android kecil yang saya miliki tetapi tidak benar-benar melihat perbedaannya sama sekali. (Meskipun saya tidak "secara ilmiah" mengukur apa pun.)
Apakah saya salah dalam menganggap itu harus lebih cepat dan lebih efisien? Saya benci menjalani dan mengubah segalanya dalam program besar untuk mengetahui bahwa saya telah membuang-buang waktu.
Apakah ini layak dilakukan dari awal ketika saya memulai proyek baru? (Maksud saya, saya pikir setiap sedikit akan membantu tetapi sekali lagi jika demikian, mengapa sepertinya tidak ada yang melakukannya.)
sumber
Itu tergantung pada implementasi JVM, serta perangkat keras yang mendasarinya. Kebanyakan perangkat keras modern tidak akan mengambil byte tunggal dari memori (atau bahkan dari cache tingkat pertama), yaitu menggunakan tipe primitif yang lebih kecil umumnya tidak mengurangi konsumsi bandwidth memori. Demikian juga, CPU modern memiliki ukuran word 64 bit. Mereka dapat melakukan operasi pada lebih sedikit bit, tetapi itu bekerja dengan membuang bit ekstra, yang juga tidak lebih cepat.
Satu-satunya keuntungan adalah tipe primitif yang lebih kecil dapat menghasilkan tata letak memori yang lebih ringkas, terutama saat menggunakan array. Ini menghemat memori, yang dapat meningkatkan lokalitas referensi (sehingga mengurangi jumlah cache yang hilang) dan mengurangi overhead pengumpulan sampah.
Namun secara umum, menggunakan tipe primitif yang lebih kecil tidak lebih cepat.
Untuk mendemonstrasikannya, perhatikan tolok ukur berikut:
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); } } }
yang dicetak di buku catatan saya yang agak lama (menambahkan spasi untuk menyesuaikan kolom):
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
Seperti yang Anda lihat, perbedaan kinerja cukup kecil. Mengoptimalkan algoritma jauh lebih penting daripada pilihan tipe primitif.
sumber
short
danbyte
lebih efisien bila disimpan dalam array yang cukup besar untuk masalah (semakin besar array, semakin besar perbedaan efisiensi; abyte[2]
mungkin lebih atau kurang efisien daripadaint[2]
, tetapi tidak cukup untuk menjadi masalah dengan cara apa pun), tetapi nilai-nilai individual disimpan secara lebih efisien sebagaiint
.Menggunakan
byte
alih-alihint
dapat meningkatkan kinerja jika Anda menggunakannya dalam jumlah besar. Ini eksperimennya: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)"); }
}
Kelas ini menguji kecepatan pembuatan file
TestClass
. Setiap tes melakukannya 20 juta kali dan ada 50 tes.Inilah 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; }
Saya telah menjalankan
SpeedTest
kelas dan pada akhirnya mendapatkan ini:Average Duration = 8.9625E8 ns = 896 ms (Approximately)
Sekarang saya mengubah int menjadi byte di TestClass dan menjalankannya lagi. Inilah hasilnya:
Average Duration = 6.94375E8 ns = 694 ms (Approximately)
Saya percaya percobaan ini menunjukkan bahwa jika Anda menggunakan sejumlah besar variabel, menggunakan byte alih-alih int dapat meningkatkan efisiensi
sumber
byte
bisa >> lebih lambat << dariint
.byte umumnya dianggap 8 bit. pendek umumnya dianggap 16 bit.
Dalam lingkungan "murni", yang bukan java karena semua implementasi byte dan long, dan short, dan hal menyenangkan lainnya biasanya tersembunyi dari Anda, byte memanfaatkan ruang dengan lebih baik.
Namun, komputer Anda mungkin bukan 8 bit, dan mungkin juga bukan 16 bit. ini berarti bahwa untuk mendapatkan 16 atau 8 bit secara khusus, perlu menggunakan "tipu muslihat" yang membuang-buang waktu untuk berpura-pura memiliki kemampuan untuk mengakses jenis-jenis tersebut bila diperlukan.
Pada titik ini, itu tergantung pada bagaimana perangkat keras diimplementasikan. Namun dari yang pernah saya pikirkan, kecepatan terbaik dicapai dari menyimpan hal-hal dalam potongan yang nyaman untuk digunakan CPU Anda. Prosesor 64 bit suka berurusan dengan elemen 64 bit, dan apa pun yang kurang dari itu sering kali memerlukan "keajaiban teknik" untuk berpura-pura suka berurusan dengan elemen tersebut.
sumber
Salah satu alasan short / byte / char menjadi kurang berkinerja adalah kurangnya dukungan langsung untuk tipe data ini. Dengan dukungan langsung, artinya, spesifikasi JVM tidak menyebutkan set instruksi apa pun untuk tipe data ini. Instruksi seperti menyimpan, memuat, menambahkan, dll. Memiliki versi untuk tipe data int. Tetapi mereka tidak memiliki versi untuk pendek / byte / char. Misalnya pertimbangkan kode java di bawah ini:
void spin() { int i; for (i = 0; i < 100; i++) { ; // Loop body is empty } }
Hal yang sama diubah menjadi kode mesin seperti di bawah ini.
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
Sekarang, pertimbangkan untuk mengubah int menjadi pendek seperti di bawah ini.
void sspin() { short i; for (i = 0; i < 100; i++) { ; // Loop body is empty } }
Kode mesin yang sesuai akan berubah sebagai berikut:
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
Seperti yang bisa Anda amati, untuk memanipulasi tipe data pendek, ia masih menggunakan versi instruksi tipe data int dan secara eksplisit mengubah int menjadi short bila diperlukan. Sekarang, karena ini, kinerjanya berkurang.
Nah, alasan yang dikutip untuk tidak memberikan dukungan langsung sebagai berikut:
Dikutip dari spesifikasi JVM yang hadir di sini (Halaman 58).
sumber
javac
kompiler, dan Anda tidak dapat menarik kesimpulan yang dapat diandalkan darinya tentang bagaimana program akan bekerja dalam kehidupan nyata. Kompilator JIT mengkompilasi bytecode ini ke instruksi mesin asli yang sebenarnya , dan melakukan beberapa pengoptimalan yang cukup serius dalam prosesnya. Jika Anda ingin menganalisis kinerja kode, Anda perlu memeriksa instruksi kode asli. (Dan ini rumit karena Anda perlu mempertimbangkan perilaku pengaturan waktu dari pipeline x86_64 multi-tahap.)Perbedaannya hampir tidak terlihat! Ini lebih merupakan masalah desain, kesesuaian, keseragaman, kebiasaan, dll ... Terkadang itu hanya masalah selera. Ketika semua yang Anda pedulikan adalah bahwa program Anda bangun dan berjalan dan mengganti dengan
float
yangint
tidak akan merugikan kebenaran, saya melihat tidak ada keuntungan dalam pergi untuk satu atau yang lain kecuali Anda dapat menunjukkan bahwa menggunakan salah satu jenis mengubah kinerja. Performa tuning berdasarkan jenis yang berbeda dalam 2 atau 3 byte benar-benar hal terakhir yang harus Anda perhatikan; Donald Knuth pernah berkata: "Optimasi prematur adalah akar dari segala kejahatan" (tidak yakin itu dia, edit jika Anda punya jawabannya).sumber
float
tidak bisa mewakili semua bilangan bulatint
kaleng; juga tidak bisaint
mewakili nilai non-integer yangfloat
bisa. Artinya, meskipun semua nilai int adalah himpunan bagian dari nilai panjang, int bukan bagian dari float dan float bukan bagian dari int.substituting a float for a double
, jika demikian penjawab harus mengedit jawabannya. Jika tidak penjawab harus menundukkan kepala karena malu dan kembali ke dasar untuk alasan yang diuraikan oleh @pst dan untuk banyak alasan lainnya.