scala vs java, kinerja dan memori? [Tutup]

160

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?

John Smith
sumber
3
Saya telah mendengar klaim bahwa kinerjanya sangat dekat. Saya menduga itu sangat tergantung pada apa yang Anda lakukan. (seperti halnya untuk Java vs C)
Peter Lawrey
Jawaban untuk pertanyaan-pertanyaan semacam ini adalah "itu tergantung" - untuk hampir semua perbandingan sistem X vs sistem Y. Plus, ini adalah duplikat dari stackoverflow.com/questions/2479819/…
James Moore

Jawaban:

261

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.

Rex Kerr
sumber
172
+1 untuk paragraf terakhir. Ini adalah titik penting yang tersisa dari pertimbangan jauh terlalu sering.
Kevin Wright
2
Saya pikir pandangan dapat banyak membantu dengan masalah yang Anda sebutkan. Atau tidak benar dengan array, khususnya?
Nicolas Payette
1
@Kevin Wright - "Ini adalah poin penting yang terlalu sering diabaikan" - Ini adalah sesuatu yang mudah untuk dikatakan dan sulit untuk ditunjukkan, dan memberi tahu kami sesuatu tentang keterampilan Rex Kerr, bukan apa yang dicapai oleh orang lain yang kurang terampil.
igouy
1
>> dalam gaya idiomatik dengan kinerja superlatif << Ada ruang dalam permainan tolok ukur untuk program Scala idiomatik yang tidak mencapai kinerja "superlatif".
igouy
2
@RexKerr - bukankah contoh Java Anda mencari kunci pemetaan dua kali untuk setiap String yang mungkin di mana contoh Scala Anda hanya melakukannya sekali setelah Strings telah dipilih? Apakah mereka dioptimalkan dengan berbagai cara untuk set data yang berbeda?
Seth
103

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.

Tidak tidur
sumber
7
Kenapa Anda bukan anggota klub "bahasa keren bulan ini"? Komentar yang bagus. Saya sangat menikmati membaca paragraf terakhir.
stepanian
21
Luar biasa! Saya menjadi bosan dengan contoh-contoh yang dibuat-buat di mana kode Java yang digembungkan diikuti oleh beberapa contoh Scala (atau beberapa bahasa FP lainnya) yang dibangun dengan hati-hati dan kemudian dengan cepat menarik kesimpulan bahwa Scala harus lebih baik daripada Jawa karenanya. Lagi pula, siapa yang pernah menulis sesuatu yang signifikan di Scala! ;-) Dan jangan katakan Twitter ...
chrisjleu
2
Nah, solusi Rex preallocates memori untuk array, yang akan membuat kode dikompilasi berjalan lebih cepat (karena dengan pendekatan Anda, Anda membiarkan JVM secara berkala merealokasi array Anda saat ia tumbuh). Meskipun ada lebih banyak pengetikan yang terlibat, kinerja-bijaksana itu bisa menjadi pemenang.
Ashalynd
5
sementara kita melakukannya, di java8 itu akan menjadi:Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
bennyl
2
Apa yang membuat Scala "lebih baik" dalam beberapa hal daripada Java adalah kemampuan tipe-sistem diperluas yang membuatnya lebih mudah untuk mengekspresikan pola yang lebih umum sebagai tipe (seperti Monads, Functors, dll.). Ini memungkinkan Anda untuk membuat tipe yang tidak menghalangi Anda karena kontrak yang terlalu ketat, seperti yang sering terjadi di Jawa. Kontrak ketat yang tidak didasarkan pada pola aktual dalam kode adalah alasan Pembalikan pola Tanggung jawab diperlukan hanya untuk menguji unit Anda dengan benar (Injeksi Ketergantungan muncul pertama kali dan Neraka XML yang dibawanya). Addl. keringkasan yang dibawa fleksibilitas hanyalah bonus.
Yosia
67

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 @specialisedanotasi, 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

  • Kode scala membutuhkan waktu 1,25x lebih lama dari kode Java dalam satu utas
  • Dapat dengan mudah dibagi menjadi 4 core (sekarang umum bahkan di laptop)
  • untuk waktu jalankan paralel (1,24 / 4 =) 0,3125x Java asli

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" :)

Kevin Wright
sumber
4
Kebetulan, Anda mungkin ingin menyebutkan bahwa .parada di 2.9.
Rex Kerr
26
>> Akankah Anda mengatakan bahwa kode Scala sekarang relatif 25% lebih lambat, atau 3x lebih cepat? << Saya akan mengatakan mengapa bukan perbandingan hipotesis Anda dengan kode Java multi-threaded?
igouy
17
@igouy - Intinya adalah bahwa kata kode hipotetis tidak ada, sifat imperatif dari kode Java "lebih cepat" membuatnya lebih sulit untuk diparalelkan, sehingga rasio biaya / manfaat berarti tidak mungkin terjadi sama sekali. Scala idiomatik, di sisi lain, menjadi jauh lebih deklaratif sering dapat dibuat bersamaan dengan tidak lebih dari perubahan sepele.
Kevin Wright
7
Keberadaan program Java bersamaan tidak menyiratkan bahwa program Java yang khas dapat dengan mudah disesuaikan dengan concurrency. Jika ada, saya akan mengatakan bahwa gaya fork-join khusus sangat jarang di Jawa dan harus dikodekan secara eksplisit, sedangkan operasi sederhana seperti menemukan nilai minimum yang terkandung, atau jumlah nilai dalam koleksi dapat dilakukan secara sepele secara paralel di Scala hanya dengan menggunakan .par.
Kevin Wright
5
Tidak, saya mungkin tidak. Hal semacam ini merupakan blok pembangun yang mendasar bagi banyak algoritma, dan untuk melihatnya hadir pada level rendah dalam bahasa dan pustaka standar (pustaka standar yang sama yang akan digunakan semua program, bukan hanya yang khas) adalah bukti bahwa Anda Kita sudah lebih dekat untuk menjadi berbarengan dengan hanya memilih bahasa. Misalnya, pemetaan koleksi secara inheren cocok untuk paralisis, dan jumlah program Scala yang tidak menggunakan mapmetode ini akan semakin kecil.
Kevin Wright
31

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.

Peter Knego
sumber
34
Ukuran kode java / scala 3.39 / 2.21
hammar
22
Berhati-hatilah dengan angka-angka seperti ini, mereka terdengar sangat tepat sementara pada kenyataannya hampir tidak ada artinya. Bukan berarti Scala selalu 24% lebih cepat dari Jawa rata-rata, dll.
Jesper
3
Angka yang dikutip di atas menunjukkan sebaliknya: Java 24% lebih cepat dari scala. Tapi seperti yang Anda katakan - mereka microbenchmarks, yang tidak perlu cocok dengan apa yang terjadi di aplikasi nyata. Dan cara atau solusi masalah yang berbeda dalam bahasa yang berbeda mungkin menyebabkan program yang kurang sebanding pada akhirnya.
pengguna tidak diketahui
9
"Jika Scala membuat Anda dan tim Anda ..." Intinya: Anda akan tahu itu setelah sebelumnya :-)
igouy
Halaman Bantuan permainan tolok ukur menyediakan contoh tentang cara "Membandingkan kecepatan dan ukuran program untuk 2 implementasi bahasa". Untuk Scala dan Java halaman web perbandingan yang sesuai adalah - shootout.alioth.debian.org/u64q/scala.php
igouy
20

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)

  • Nilai-nilai malas aman-utas
  • Pass-by-name
  • Hal-hal yang monadik

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 Optionkasus dll Hal ini juga mempertahankan jenis koleksi sehingga bigEnough's type Array[File]- Array' s collectpelaksanaan 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 withFiltermetode yang saya gunakan di sini bukan filterperbaikan 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.

Seth
sumber
Item logging Anda adalah contoh yang baik untuk perangkap kinerja Scala: logger.debug ("trace") membuat objek baru untuk fungsi parameter-kurang.
jcsahnwaldt Reinstate Monica
Memang - bagaimana hal itu memengaruhi poin saya yang terkait?
Seth
Objek yang disebutkan di atas juga dapat digunakan untuk membuat struktur kontrol IoC transparan demi efisiensi. Ya, hasil yang sama secara teori dimungkinkan di Jawa tetapi itu akan menjadi sesuatu yang secara dramatis mempengaruhi / mengaburkan cara penulisan kode - maka argumen saya bahwa keahlian Scala untuk menunda banyak elemen pengembangan perangkat lunak membantu kita bergerak ke arah kode yang lebih cepat - lebih cenderung menjadi lebih cepat dalam praktik vs memiliki kinerja unit yang sedikit lebih cepat.
Seth
Ok, saya sudah membaca ulang ini dan saya memang menulis, "kecepatan eksekusi sederhana" - Saya akan menambahkan catatan. Poin bagus :)
Seth
3
Pernyataan dapat diprediksi jika (pada dasarnya gratis pada prosesor superscalar) vs alokasi objek + sampah. Kode Java jelas lebih cepat (perhatikan itu hanya mengevaluasi kondisi, eksekusi tidak akan mencapai pernyataan log.) Menanggapi "Untuk pekerjaan saya, berat objek sepele layak menghapus kemungkinan pesan log mendapatkan dievaluasi tanpa perlu sia-sia . "
Eloff
10

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).

ryeguy
sumber
7
Saya melihat kesalahan logis di sini: Kedua bahasa mengkompilasi ke bytecode, tetapi seorang programmer yang berpengalaman dan seorang pemula - kode mereka mengkompilasi ke bytecode juga - tetapi tidak dengan bytecode yang sama, jadi kesimpulannya, bahwa perbedaannya tidak boleh sebesar itu , bisa salah. Dan pada kenyataannya, di masa lalu, loop-sementara bisa jauh lebih cepat dalam scala daripada for-loop semantik yang setara (jika saya ingat dengan benar, ini jauh lebih baik hari ini). Dan keduanya dikompilasi menjadi bytecode, tentu saja.
pengguna tidak diketahui
@user tidak dikenal - "loop sementara bisa jauh lebih cepat dalam scala daripada loop setara semantik" - perhatikan bahwa program permainan benchmark Scala ditulis dengan while loop.
igouy
@igouy: Saya tidak berbicara tentang hasil dari microbenchmark ini, tetapi tentang argumentasi. Pernyataan Java and Scala both compile down to JVM bytecode, yang benar yang digabungkan dengan sopernyataan yang dimaksud diffence isn't that big.ingin saya perlihatkan, bahwa soitu hanyalah tipuan retoris, dan bukan kesimpulan argumentatif.
pengguna tidak diketahui
3
jawaban mengejutkan salah dengan suara sangat tinggi.
shabunc
4

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.

MickH
sumber