Pertama-tama saya ingin menjelaskan bahwa ini bukan pertanyaan bahasa-X-versus-bahasa-Y untuk menentukan mana yang lebih baik.
Saya telah menggunakan Java untuk waktu yang lama dan saya bermaksud untuk terus menggunakannya. Sejalan dengan ini, saya saat ini sedang mempelajari Scala dengan minat besar: terlepas dari hal-hal kecil yang membuat saya terbiasa dengan kesan saya adalah bahwa saya benar-benar dapat bekerja dengan sangat baik dalam bahasa ini.
Pertanyaan saya adalah: bagaimana peranti lunak yang ditulis dalam Scala dibandingkan dengan peranti lunak yang ditulis dalam Java dalam hal kecepatan eksekusi dan konsumsi memori? Tentu saja, ini adalah pertanyaan yang sulit dijawab secara umum, tetapi saya berharap bahwa konstruksi tingkat yang lebih tinggi seperti pencocokan pola, fungsi tingkat tinggi, dll, memperkenalkan beberapa overhead.
Namun, pengalaman saya saat ini di Scala terbatas pada contoh kecil di bawah 50 baris kode dan saya belum menjalankan tolok ukur apa pun hingga saat ini. Jadi, saya tidak punya data nyata.
Jika ternyata Scala memiliki beberapa overhead overhead Java, apakah masuk akal untuk menggabungkan proyek Scala / Java, di mana orang mengkode bagian yang lebih kompleks di Scala dan bagian yang kritis terhadap kinerja di Jawa? Apakah ini praktik umum?
EDIT 1
Saya telah menjalankan patokan kecil: buat daftar bilangan bulat, kalikan setiap bilangan bulat dengan dua dan masukkan ke dalam daftar baru, cetak daftar yang dihasilkan. Saya menulis implementasi Java (Java 6) dan implementasi Scala (Scala 2.9). Saya telah menjalankan keduanya di Eclipse Indigo di bawah Ubuntu 10.04.
Hasilnya sebanding: 480 ms untuk Java dan 493 ms untuk Scala (rata-rata lebih dari 100 iterasi). Berikut cuplikan yang saya gunakan.
// Java
public static void main(String[] args)
{
long total = 0;
final int maxCount = 100;
for (int count = 0; count < maxCount; count++)
{
final long t1 = System.currentTimeMillis();
final int max = 20000;
final List<Integer> list = new ArrayList<Integer>();
for (int index = 1; index <= max; index++)
{
list.add(index);
}
final List<Integer> doub = new ArrayList<Integer>();
for (Integer value : list)
{
doub.add(value * 2);
}
for (Integer value : doub)
{
System.out.println(value);
}
final long t2 = System.currentTimeMillis();
System.out.println("Elapsed milliseconds: " + (t2 - t1));
total += t2 - t1;
}
System.out.println("Average milliseconds: " + (total / maxCount));
}
// Scala
def main(args: Array[String])
{
var total: Long = 0
val maxCount = 100
for (i <- 1 to maxCount)
{
val t1 = System.currentTimeMillis()
val list = (1 to 20000) toList
val doub = list map { n: Int => 2 * n }
doub foreach ( println )
val t2 = System.currentTimeMillis()
println("Elapsed milliseconds: " + (t2 - t1))
total = total + (t2 - t1)
}
println("Average milliseconds: " + (total / maxCount))
}
Jadi, dalam hal ini tampaknya overhead Scala (menggunakan rentang, peta, lambda) sangat minim, yang tidak jauh dari informasi yang disediakan oleh World Engineer.
Mungkin ada konstruksi Scala lain yang harus digunakan dengan hati-hati karena sangat berat untuk dieksekusi?
EDIT 2
Beberapa dari Anda menunjukkan bahwa println yang ada di loop dalam mengambil sebagian besar waktu eksekusi. Saya telah menghapusnya dan mengatur ukuran daftar menjadi 100.000 bukan 20.000. Rata-rata yang dihasilkan adalah 88 ms untuk Java dan 49 ms untuk Scala.
sumber
Jawaban:
Ada satu hal yang dapat Anda lakukan secara ringkas dan efisien di Jawa yang tidak dapat Anda lakukan di Scala: enumerasi. Untuk yang lainnya, bahkan untuk konstruksi yang lambat di perpustakaan Scala, Anda bisa mendapatkan versi efisien yang bekerja di Scala.
Jadi, untuk sebagian besar, Anda tidak perlu menambahkan Java ke kode Anda. Bahkan untuk kode yang menggunakan enumerasi di Jawa, sering ada solusi di Scala yang memadai atau bagus - Saya menempatkan pengecualian pada enumerasi yang memiliki metode tambahan dan yang nilai konstan intnya digunakan.
Adapun apa yang harus diperhatikan, berikut adalah beberapa hal.
Jika Anda menggunakan pola memperkaya perpustakaan saya, selalu konversikan ke kelas. Sebagai contoh:
Berhati-hatilah dengan metode pengumpulan - karena mereka sebagian besar polimorfik, JVM tidak mengoptimalkannya. Anda tidak perlu menghindarinya, tetapi perhatikan di bagian-bagian penting. Sadarilah bahwa
for
dalam Scala diimplementasikan melalui pemanggilan metode dan kelas anonim.Jika menggunakan kelas Java, seperti
String
,Array
atauAnyVal
kelas yang sesuai dengan primitif Java, lebih suka metode yang disediakan oleh Java ketika ada alternatif. Misalnya, gunakanlength
padaString
danArray
bukansize
.Hindari penggunaan konversi implisit yang ceroboh, karena Anda dapat menemukan diri Anda menggunakan konversi secara tidak sengaja alih-alih oleh desain.
Perpanjang kelas bukan sifat. Misalnya, jika Anda memanjang
Function1
,AbstractFunction1
sebaliknya.Gunakan
-optimise
dan spesialisasi untuk mendapatkan sebagian besar Scala.Memahami apa yang terjadi:
javap
adalah teman Anda, dan begitu pula sekelompok bendera Scala yang menunjukkan apa yang terjadi.Idi scala dirancang untuk meningkatkan kebenaran dan membuat kode lebih ringkas dan dapat dipelihara. Mereka tidak dirancang untuk kecepatan, jadi jika Anda perlu menggunakan
null
alih-alihOption
di jalur kritis, lakukanlah! Ada alasan mengapa Scala adalah multi-paradigma.Ingat bahwa ukuran kinerja sebenarnya adalah menjalankan kode. Lihat pertanyaan ini untuk contoh tentang apa yang mungkin terjadi jika Anda mengabaikan aturan itu.
sumber
Menurut Game Benchmarks untuk sistem single core, 32 bit, Scala berada pada median 80% secepat Java. Performanya kira-kira sama untuk komputer Quad Core x64. Bahkan penggunaan memori dan kepadatan kode sangat mirip dalam banyak kasus. Saya akan mengatakan berdasarkan analisis (agak tidak ilmiah) ini bahwa Anda benar dalam menyatakan bahwa Scala menambahkan beberapa overhead ke Jawa. Tampaknya tidak menambah ton overhead sehingga saya curiga diagnosis item pesanan lebih tinggi mengambil lebih banyak ruang / waktu adalah yang paling benar.
sumber
Int
,Char
, dll ketika bisa. Sementara loop sama efisiennya dalam Scala.Function
kelas. Jika Anda meneruskan lambda kemap
, kelas anonim perlu di-instantiated (dan beberapa penduduk lokal mungkin perlu dilewati), dan kemudian setiap iterasi memiliki overhead panggilan fungsi tambahan (dengan beberapa parameter yang lewat) dariapply
panggilan.scala.util.Random
hanya pembungkus di sekitar kelas JRE yang setara. Panggilan fungsi ekstra agak boros.java.lang.Math.signum(x)
jauh lebih langsung daripadax.signum()
, yang mengkonversi keRichInt
dan kembali.sumber
mengurangi waktu dari 800 ms hingga 20 pada sistem saya.
sumber