Kita harus membuat Strings setiap saat untuk keluaran log dan sebagainya. Lebih dari versi JDK kami telah belajar kapan harus menggunakan StringBuffer
(banyak menambahkan, aman thread) dan StringBuilder
(banyak menambahkan, aman non-thread).
Apa saran untuk menggunakan String.format()
? Apakah efisien, atau apakah kita dipaksa untuk tetap dengan penggabungan satu baris di mana kinerja itu penting?
misalnya gaya lama yang jelek,
String s = "What do you get if you multiply " + varSix + " by " + varNine + "?";
vs. merapikan gaya baru (String.format, yang mungkin lebih lambat),
String s = String.format("What do you get if you multiply %d by %d?", varSix, varNine);
Catatan: kasus penggunaan spesifik saya adalah ratusan string log 'satu-liner' di seluruh kode saya. Mereka tidak melibatkan lingkaran, jadi StringBuilder
terlalu berat. Saya tertarik secara String.format()
khusus.
Jawaban:
Saya menulis sebuah kelas kecil untuk menguji yang memiliki kinerja lebih baik dari keduanya dan + datang di depan format. dengan faktor 5 hingga 6. Cobalah sendiri
Menjalankan hal di atas untuk N yang berbeda menunjukkan bahwa keduanya berperilaku linier, tetapi
String.format
5-30 kali lebih lambat.Alasannya adalah bahwa dalam implementasi saat ini
String.format
pertama mem-parsing input dengan ekspresi reguler dan kemudian mengisi parameter. Penggabungan dengan plus, di sisi lain, akan dioptimalkan oleh javac (bukan oleh JIT) dan digunakanStringBuilder.append
secara langsung.sumber
Saya mengambil kode hhafez dan menambahkan tes memori :
Saya menjalankan ini secara terpisah untuk setiap pendekatan, operator '+', String.format dan StringBuilder (memanggil toString ()), sehingga memori yang digunakan tidak akan terpengaruh oleh pendekatan lain. Saya menambahkan lebih banyak rangkaian, menjadikan string sebagai "Blah" + i + "Blah" + i + "Blah" + i + "Blah".
Hasilnya adalah sebagai berikut (rata-rata 5 berjalan masing-masing):
Waktu Pendekatan (ms) Memori yang dialokasikan (panjang)
operator '+' 747 320.504
String.format 16484 373.312
StringBuilder 769 57.344
Kita dapat melihat bahwa String '+' dan StringBuilder secara praktis identik dengan waktu, tetapi StringBuilder jauh lebih efisien dalam penggunaan memori. Ini sangat penting ketika kami memiliki banyak panggilan log (atau pernyataan lain yang melibatkan string) dalam interval waktu yang cukup singkat sehingga Pengumpul Sampah tidak akan bisa membersihkan banyak instance string yang dihasilkan oleh operator '+'.
Dan sebuah catatan, BTW, jangan lupa untuk memeriksa level logging sebelum membuat pesan.
Kesimpulan:
sumber
+
operator mengkompilasi keStringBuilder
kode yang setara . Microbenchmark seperti ini bukan cara yang baik untuk mengukur kinerja - mengapa tidak menggunakan jvisualvm, ada di jdk karena suatu alasan.String.format()
akan lebih lambat, tetapi karena waktu untuk mem-parsing string format daripada alokasi objek apa pun. Menunda pembuatan artefak penebangan hingga Anda yakin mereka membutuhkannya adalah saran yang bagus, tetapi jika itu akan memiliki dampak kinerja itu di tempat yang salah.And a note, BTW, don't forget to check the logging level before constructing the message.
bukan saran yang bagus. Dengan asumsi kita sedang membicarakanjava.util.logging.*
secara spesifik, memeriksa level logging adalah ketika Anda berbicara tentang melakukan pemrosesan lanjutan yang akan menyebabkan efek buruk pada suatu program yang tidak Anda inginkan ketika sebuah program tidak memiliki logging yang diaktifkan ke tingkat yang sesuai. Pemformatan string bukan tipe pemrosesan sama sekali. Pemformatan adalah bagian darijava.util.logging
framework, dan logger itu sendiri memeriksa level logging sebelum pemformat dipanggil.Semua tolok ukur yang disajikan di sini memiliki beberapa kekurangan , sehingga hasilnya tidak dapat diandalkan.
Saya terkejut bahwa tidak ada yang menggunakan JMH untuk benchmark, jadi saya melakukannya.
Hasil:
Unit adalah operasi per detik, semakin banyak semakin baik. Kode sumber patokan . OpenJDK IcedTea 2.5.4 Java Virtual Machine digunakan.
Jadi, gaya lama (menggunakan +) jauh lebih cepat.
sumber
Gaya jelek lama Anda secara otomatis dikompilasi oleh JAVAC 1.6 sebagai:
Jadi sama sekali tidak ada perbedaan antara ini dan menggunakan StringBuilder.
String.format jauh lebih berat karena ia menciptakan Formatter baru, mem-parsing string format input Anda, membuat StringBuilder, menambahkan segalanya ke sana dan memanggil toString ().
sumber
+
danStringBuilder
memang. Sayangnya ada banyak informasi yang salah dalam jawaban lain di utas ini. Saya hampir tergoda untuk mengubah pertanyaanhow should I not be measuring performance
.String.format Java berfungsi seperti ini:
jika tujuan akhir untuk data ini adalah streaming (misalnya, merender halaman web atau menulis ke file), Anda dapat merakit potongan format langsung ke aliran Anda:
Saya berspekulasi bahwa optimizer akan mengoptimalkan pemrosesan string format. Jika demikian, Anda memiliki kinerja amortisasi yang setara untuk membuka gulungan String.format secara manual ke dalam StringBuilder.
sumber
String.format
loop dalam (berjalan jutaan kali) menghasilkan lebih dari 10% dari waktu eksekusi saya dihabiskan dijava.util.Formatter.parse(String)
. Ini tampaknya menunjukkan bahwa dalam loop internal, Anda harus menghindari panggilanFormatter.format
atau apa pun yang menyebutnya, termasukPrintStream.format
(cacat dalam lib standar Java, IMO, terutama karena Anda tidak dapat men-cache string format yang diuraikan).Untuk memperluas / memperbaiki jawaban pertama di atas, sebenarnya bukan terjemahan yang akan membantu String.format.
Apa String.format akan membantu adalah ketika Anda mencetak tanggal / waktu (atau format numerik, dll), di mana ada perbedaan lokalisasi (l10n) (yaitu, beberapa negara akan mencetak 04Feb2009 dan yang lain akan mencetak Feb042009).
Dengan terjemahan, Anda hanya berbicara tentang memindahkan string eksternal (seperti pesan kesalahan dan apa-tidak) ke dalam bundel properti sehingga Anda dapat menggunakan bundel yang tepat untuk bahasa yang tepat, menggunakan ResourceBundle dan MessageFormat.
Melihat semua hal di atas, saya akan mengatakan bahwa concatenation, String.format vs plain datang ke apa yang Anda inginkan. Jika Anda lebih suka melihat panggilan ke .format daripada gabungan, maka tentu saja, ikuti saja.
Bagaimanapun, kode lebih banyak dibaca daripada yang tertulis.
sumber
Dalam contoh Anda, masalah kinerja tidak terlalu berbeda tetapi ada masalah lain yang perlu dipertimbangkan: yaitu fragmentasi memori. Bahkan operasi gabungan membuat string baru, bahkan jika itu sementara (butuh waktu untuk membuatnya dan itu lebih banyak bekerja). String.format () lebih mudah dibaca dan melibatkan lebih sedikit fragmentasi.
Juga, jika Anda sering menggunakan format tertentu, jangan lupa Anda dapat menggunakan kelas Formatter () secara langsung (semua String.format () yang digunakan adalah instantiate turunan satu kali pakai Formatter).
Selain itu, hal lain yang harus Anda perhatikan: berhati-hatilah menggunakan substring (). Sebagai contoh:
String besar itu masih ada dalam memori karena itulah cara kerja substring Java. Versi yang lebih baik adalah:
atau
Bentuk kedua mungkin lebih berguna jika Anda melakukan hal-hal lain pada saat yang bersamaan.
sumber
Secara umum Anda harus menggunakan String.Format karena relatif cepat dan mendukung globalisasi (dengan asumsi Anda benar-benar mencoba menulis sesuatu yang dibaca oleh pengguna). Ini juga memudahkan untuk mengglobal jika Anda mencoba menerjemahkan satu string lawan 3 atau lebih per pernyataan (terutama untuk bahasa yang memiliki struktur tata bahasa yang sangat berbeda).
Sekarang jika Anda tidak pernah berencana untuk menerjemahkan apa pun, maka andalkan mengandalkan konversi + operator bawaan Java
StringBuilder
. Atau gunakan JavaStringBuilder
secara eksplisit.sumber
Perspektif lain dari sudut pandang Hanya logging.
Saya melihat banyak diskusi terkait dengan masuk pada utas ini sehingga saya berpikir untuk menambahkan pengalaman saya sebagai jawaban. Mungkin seseorang akan merasakan manfaatnya.
Saya kira motivasi logging menggunakan formatter berasal dari menghindari rangkaian string. Pada dasarnya, Anda tidak ingin memiliki overhead string concat jika Anda tidak akan mencatatnya.
Anda tidak perlu melakukan concat / format kecuali Anda ingin login. Katakanlah jika saya mendefinisikan metode seperti ini
Dalam pendekatan ini pembatalan / formatter tidak benar-benar dipanggil sama sekali jika itu pesan debug dan debugOn = false
Meskipun masih akan lebih baik menggunakan StringBuilder daripada formatter di sini. Motivasi utama adalah untuk menghindari semua itu.
Pada saat yang sama saya tidak suka menambahkan blok "jika" untuk setiap pernyataan logging sejak itu
Oleh karena itu saya lebih suka membuat kelas utilitas pencatatan dengan metode seperti di atas dan menggunakannya di mana-mana tanpa khawatir tentang kinerja dan masalah lain yang terkait dengannya.
sumber
Saya baru saja memodifikasi tes hhafez untuk memasukkan StringBuilder. StringBuilder adalah 33 kali lebih cepat dari String.format menggunakan klien jdk 1.6.0_10 pada XP. Menggunakan -server switch menurunkan faktor menjadi 20.
Meskipun ini mungkin terdengar drastis, saya menganggapnya hanya relevan dalam kasus yang jarang terjadi, karena angka absolutnya cukup rendah: 4d untuk 1 juta panggilan String.format sederhana agak ok - selama saya menggunakannya untuk logging atau Suka.
Pembaruan: Seperti yang ditunjukkan oleh sjbotha dalam komentar, tes StringBuilder tidak valid, karena tidak ada final
.toString()
.Faktor percepatan yang benar dari
String.format(.)
keStringBuilder
adalah 23 pada mesin saya (16 dengan-server
sakelar).sumber
Ini adalah versi modifikasi dari entri hhafez. Ini termasuk opsi pembuat string.
}
Waktu setelah untuk loop 391 Waktu setelah untuk loop 4163 Waktu setelah untuk loop 227
sumber
Jawabannya sangat tergantung pada bagaimana kompiler Java spesifik Anda mengoptimalkan bytecode yang dihasilkannya. String tidak dapat diubah dan, secara teoritis, setiap operasi "+" dapat membuat yang baru. Tapi, kompiler Anda hampir pasti mengoptimalkan langkah-langkah sementara dalam membangun string panjang. Sangat mungkin bahwa kedua baris kode di atas menghasilkan bytecode yang sama persis.
Satu-satunya cara nyata untuk mengetahuinya adalah dengan menguji kode iteratif di lingkungan Anda saat ini. Tulis aplikasi QD yang menggabungkan string dua arah secara iteratif dan lihat bagaimana mereka saling berhadapan.
sumber
Pertimbangkan
"hello".concat( "world!" )
untuk menggunakan sejumlah kecil string dalam rangkaian. Ini bisa lebih baik untuk kinerja daripada pendekatan lain.Jika Anda memiliki lebih dari 3 string, daripada mempertimbangkan menggunakan StringBuilder, atau hanya String, tergantung pada kompiler yang Anda gunakan.
sumber