Saya ingin melihat Scala, dan ada satu pertanyaan mendasar yang sepertinya tidak bisa saya temukan jawabannya: secara umum, apakah ada perbedaan dalam kinerja dan penggunaan memori antara Scala dan Java?
Saya ingin melihat Scala, dan ada satu pertanyaan mendasar yang sepertinya tidak bisa saya temukan jawabannya: secara umum, apakah ada perbedaan dalam kinerja dan penggunaan memori antara Scala dan Java?
Jawaban:
Scala membuatnya sangat mudah untuk menggunakan sejumlah besar memori tanpa menyadarinya. Ini biasanya sangat kuat, tetapi kadang-kadang bisa mengganggu. Misalnya, Anda memiliki array string (dipanggil array
), dan peta dari string tersebut ke file (dipanggil mapping
). Misalkan Anda ingin mendapatkan semua file yang ada di peta dan berasal dari string dengan panjang lebih dari dua. Di Jawa, Anda mungkin
int n = 0;
for (String s: array) {
if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
if (s.length <= 2) continue;
bigEnough[n++] = map.get(s);
}
Wah! Kerja keras. Di Scala, cara paling ringkas untuk melakukan hal yang sama adalah:
val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)
Mudah! Tetapi, kecuali Anda cukup terbiasa dengan cara kerja koleksi, yang mungkin tidak Anda sadari adalah bahwa cara melakukan ini menciptakan array perantara ekstra (dengan filter
), dan objek tambahan untuk setiap elemen array (dengan mapping.get
, yang mengembalikan sebuah pilihan). Itu juga menciptakan dua objek fungsi (satu untuk filter dan satu untuk flatMap), meskipun itu jarang menjadi masalah besar karena objek fungsi kecil.
Jadi pada dasarnya, penggunaan memori, pada tingkat primitif, sama. Tapi perpustakaan Scala memiliki banyak metode ampuh yang memungkinkan Anda membuat sejumlah besar objek (biasanya berumur pendek) dengan sangat mudah. Pengumpul sampah biasanya cukup baik dengan sampah semacam itu, tetapi jika Anda benar-benar tidak menyadari memori apa yang sedang digunakan, Anda mungkin akan mengalami masalah lebih cepat di Scala daripada di Jawa.
Perhatikan bahwa kode Bahasa Bahasa Game Benchmark Game Scala ditulis dalam gaya agak Jawa untuk mendapatkan kinerja seperti Java, dan karenanya memiliki penggunaan memori mirip Java. Anda dapat melakukan ini di Scala: jika Anda menulis kode Anda agar terlihat seperti kode Java berkinerja tinggi, itu akan menjadi kode Scala berkinerja tinggi. (Anda mungkin dapat menulisnya dalam gaya Scala yang lebih idiomatis dan masih mendapatkan kinerja yang baik, tetapi itu tergantung pada spesifikasinya.)
Saya harus menambahkan bahwa per jumlah waktu yang dihabiskan untuk pemrograman, kode Scala saya biasanya lebih cepat daripada kode Java saya karena di Scala saya bisa mendapatkan bagian yang tidak penting yang membosankan dilakukan dengan sedikit usaha, dan menghabiskan lebih banyak perhatian saya untuk mengoptimalkan algoritma dan kode untuk bagian-bagian yang kritis terhadap kinerja.
Saya pengguna baru, jadi saya tidak dapat menambahkan komentar pada jawaban Rex Kerr di atas (memungkinkan pengguna baru untuk "menjawab" tetapi bukan "komentar" adalah aturan yang sangat aneh, btw).
Saya mendaftar hanya untuk menanggapi "phew, Java sangat bertele-tele dan kerja keras" sindiran jawaban populer Rex di atas. Meskipun Anda tentu saja dapat menulis kode Scala yang lebih ringkas, contoh Java yang diberikan jelas kembung. Sebagian besar pengembang Java akan mengkodekan sesuatu seperti ini:
List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
if(s.length() > 2 && mapping.get(s) != null) {
bigEnough.add(mapping.get(s));
}
}
Dan tentu saja, jika kita akan berpura-pura Eclipse tidak melakukan sebagian besar pengetikan yang sebenarnya untuk Anda dan bahwa setiap karakter yang disimpan benar-benar membuat Anda menjadi programmer yang lebih baik, maka Anda dapat membuat kode ini:
List b=new ArrayList();
for(String s:array)
if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));
Sekarang saya tidak hanya menghemat waktu untuk mengetikkan nama variabel lengkap dan kurung kurawal (membebaskan saya untuk menghabiskan 5 detik lagi untuk memikirkan pemikiran algoritmik yang mendalam), tetapi saya juga dapat memasukkan kode saya dalam kontes kebingungan dan berpotensi mendapatkan uang tambahan untuk liburan.
Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
Tulis Scala Anda seperti Java, dan Anda dapat mengharapkan kode bytode yang hampir sama dipancarkan - dengan metrik yang hampir sama.
Tuliskan lebih "idiomatis", dengan objek yang tidak berubah dan fungsi urutan yang lebih tinggi, dan itu akan sedikit lebih lambat dan sedikit lebih besar. Satu-satunya pengecualian untuk aturan praktis ini adalah ketika menggunakan objek generik di mana tipe params menggunakan @specialised
anotasi, ini akan membuat bytecode yang lebih besar yang dapat melebihi kinerja Java dengan menghindari tinju / unboxing.
Juga layak disebutkan adalah kenyataan bahwa lebih banyak memori / kurang kecepatan merupakan trade-off yang tak terhindarkan saat menulis kode yang dapat dijalankan secara paralel. Kode Scala Idiomatik jauh lebih deklaratif daripada kode Java biasa, dan seringkali hanya 4 karakter ( .par
) yang tidak sepenuhnya paralel.
Jadi jika
Apakah Anda kemudian mengatakan bahwa kode Scala sekarang relatif 25% lebih lambat, atau 3x lebih cepat?
Jawaban yang benar tergantung pada bagaimana Anda mendefinisikan "kinerja" :)
.par
ada di 2.9.
.par
.
map
metode ini akan semakin kecil.
Game Tingkatan Bahasa Komputer:
Tes kecepatan java / scala 1.71 / 2.25
Tes memori java / scala 66.55 / 80.81
Jadi, tolok ukur ini mengatakan bahwa java 24% lebih cepat dan scala menggunakan 21% lebih banyak memori.
Secara keseluruhan itu bukan masalah besar dan seharusnya tidak menjadi masalah di aplikasi dunia nyata, di mana sebagian besar waktu dikonsumsi oleh basis data dan jaringan.
Intinya: Jika Scala membuat Anda dan tim Anda (dan orang-orang mengambil alih proyek saat Anda pergi) lebih produktif, maka Anda harus melakukannya.
Orang lain telah menjawab pertanyaan ini sehubungan dengan loop ketat meskipun tampaknya ada perbedaan kinerja yang jelas antara contoh Rex Kerr yang telah saya komentari.
Jawaban ini benar-benar ditargetkan pada orang yang mungkin menyelidiki kebutuhan untuk optimasi loop ketat sebagai cacat desain.
Saya relatif baru di Scala (sekitar satu tahun), tetapi rasanya, sejauh ini, Anda dapat menunda banyak aspek desain, implementasi, dan eksekusi yang relatif mudah (dengan bacaan latar belakang yang cukup dan eksperimen :)
Fitur Desain yang Ditangguhkan:
Fitur Implementasi yang Ditangguhkan:
Fitur Eksekusi yang Ditangguhkan: (maaf, tidak ada tautan)
Fitur-fitur ini, bagi saya, adalah fitur-fitur yang membantu kita untuk menapaki jalur ke aplikasi yang cepat dan ketat.
Contoh Rex Kerr berbeda dalam aspek eksekusi yang ditangguhkan. Dalam contoh Java, alokasi memori ditangguhkan sampai ukurannya dihitung di mana contoh Scala menentang pencarian pemetaan. Bagi saya, mereka tampak seperti algoritma yang sama sekali berbeda.
Inilah yang menurut saya lebih merupakan apel yang setara dengan apel untuk contoh Java-nya:
val bigEnough = array.collect({
case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})
Tidak ada koleksi perantara, tidak ada Option
kasus dll Hal ini juga mempertahankan jenis koleksi sehingga bigEnough
's type Array[File]
- Array
' s collect
pelaksanaan mungkin akan melakukan sesuatu di sepanjang baris dari apa kode Java Mr Kerr tidak.
Fitur desain yang ditangguhkan yang saya sebutkan di atas juga akan memungkinkan pengembang API pengumpulan Scala untuk mengimplementasikan implementasi pengumpulan spesifik Array cepat tersebut dalam rilis mendatang tanpa melanggar API. Inilah yang saya maksudkan dengan menapaki jalan menuju kecepatan.
Juga:
val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)
The withFilter
metode yang saya gunakan di sini bukan filter
perbaikan masalah koleksi menengah namun masih ada masalah Option misalnya.
Salah satu contoh kecepatan eksekusi sederhana di Scala adalah dengan logging.
Di Jawa kita mungkin menulis sesuatu seperti:
if (logger.isDebugEnabled())
logger.debug("trace");
Di Scala, ini hanya:
logger.debug("trace")
karena parameter pesan untuk di-debug di Scala memiliki tipe " => String
" yang saya anggap sebagai fungsi parameter-kurang yang dijalankan ketika dievaluasi, tetapi yang oleh dokumentasinya disebut pass-by-name.
EDIT {Fungsi dalam Scala adalah objek sehingga ada objek tambahan di sini. Untuk pekerjaan saya, berat objek sepele layak dihapus kemungkinan pesan log semakin tidak perlu dievaluasi. }
Ini tidak membuat kode lebih cepat tetapi itu membuatnya lebih cenderung lebih cepat dan kami cenderung tidak memiliki pengalaman melalui dan membersihkan kode orang lain secara massal.
Bagi saya, ini adalah tema yang konsisten dalam Scala.
Hard code gagal menangkap mengapa Scala lebih cepat meskipun sedikit petunjuk.
Saya merasa bahwa ini adalah kombinasi dari penggunaan kembali kode dan langit-langit kualitas kode di Scala.
Di Jawa, kode yang luar biasa sering dipaksa untuk menjadi kekacauan yang tidak dapat dipahami sehingga tidak benar-benar layak dalam API kualitas produksi karena sebagian besar programmer tidak akan dapat menggunakannya.
Saya memiliki harapan besar bahwa Scala dapat memungkinkan einstein di antara kami untuk mengimplementasikan API yang jauh lebih kompeten, yang berpotensi diekspresikan melalui DSL. API inti di Scala sudah jauh di sepanjang jalur ini.
presentasi @higherkinded pada subjek - Pertimbangan Kinerja Scala yang melakukan beberapa perbandingan Java / Scala.
Alat:
Blogpost hebat:
Java dan Scala keduanya dikompilasi ke bytecode JVM, jadi perbedaannya tidak terlalu besar. Perbandingan terbaik yang bisa Anda dapatkan mungkin pada game benchmark bahasa komputer , yang pada dasarnya mengatakan bahwa Java dan Scala keduanya memiliki penggunaan memori yang sama. Scala hanya sedikit lebih lambat dari Jawa pada beberapa tolok ukur yang tercantum, tetapi itu bisa saja karena implementasi program berbeda.
Sebenarnya, mereka berdua sangat dekat sehingga tidak perlu dikhawatirkan. Peningkatan produktivitas yang Anda peroleh dengan menggunakan bahasa yang lebih ekspresif seperti Scala bernilai jauh lebih tinggi daripada hit kinerja yang minimal (jika ada).
Java and Scala both compile down to JVM bytecode,
yang benar yang digabungkan dengan so
pernyataan yang dimaksud diffence isn't that big.
ingin saya perlihatkan, bahwa so
itu hanyalah tipuan retoris, dan bukan kesimpulan argumentatif.
Contoh Java benar-benar bukan idiom untuk program aplikasi khas. Kode yang dioptimalkan seperti itu dapat ditemukan dalam metode pustaka sistem. Tetapi kemudian akan menggunakan array dengan tipe yang tepat, yaitu File [] dan tidak akan membuang IndexOutOfBoundsException. (Kondisi filter berbeda untuk penghitungan dan penambahan). Versi saya akan (selalu (!) Dengan kurung kurawal karena saya tidak suka menghabiskan satu jam mencari bug yang diperkenalkan dengan menyimpan 2 detik untuk menekan satu tombol di Eclipse):
List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
if(s.length() > 2) {
File file = mapping.get(s);
if (file != null) {
bigEnough.add(file);
}
}
}
Tapi saya bisa membawa Anda banyak contoh kode Java jelek dari proyek saya saat ini. Saya mencoba menghindari salinan umum & memodifikasi gaya pengkodean dengan memperhitungkan struktur dan perilaku umum.
Dalam kelas dasar DAO abstrak saya, saya memiliki kelas dalam abstrak untuk mekanisme caching umum. Untuk setiap tipe objek model konkret ada subkelas dari kelas dasar DAO abstrak, di mana kelas dalam subkelas untuk menyediakan implementasi untuk metode yang menciptakan objek bisnis ketika diambil dari database. (Kami tidak dapat menggunakan alat ORM karena kami mengakses sistem lain melalui API milik.)
Kode subclassing dan instantiation ini sama sekali tidak jelas di Java dan akan sangat mudah dibaca di Scala.