Saya memiliki pertanyaan terkait kinerja tentang penggunaan StringBuilder. Dalam loop yang sangat panjang saya memanipulasi StringBuilder
dan meneruskannya ke metode lain seperti ini:
for (loop condition) {
StringBuilder sb = new StringBuilder();
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
Apakah instantiating StringBuilder
pada setiap siklus loop adalah solusi yang baik? Dan apakah memanggil delete lebih baik, seperti berikut ini?
StringBuilder sb = new StringBuilder();
for (loop condition) {
sb.delete(0, sb.length);
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
java
performance
string
stringbuilder
Pier Luigi
sumber
sumber
Dalam filosofi penulisan kode yang solid, selalu lebih baik untuk meletakkan StringBuilder Anda di dalam loop Anda. Dengan cara ini ia tidak keluar dari kode yang dimaksudkan.
Kedua, peningkatan terbesar di StringBuilder berasal dari memberikan ukuran awal untuk menghindarinya tumbuh lebih besar saat loop berjalan
sumber
Lebih cepat lagi:
Dalam filosofi penulisan kode yang solid, cara kerja bagian dalam metode harus disembunyikan dari objek yang menggunakan metode tersebut. Jadi tidak ada bedanya dari perspektif sistem apakah Anda mendeklarasikan ulang StringBuilder di dalam atau di luar loop. Karena mendeklarasikannya di luar loop lebih cepat, dan tidak membuat kode lebih rumit untuk dibaca, maka gunakan kembali objek daripada memulihkannya.
Meskipun kodenya lebih rumit, dan Anda tahu pasti bahwa pembuatan instance objek adalah penghambat, beri komentar.
Tiga proses dengan jawaban ini:
Tiga proses dengan jawaban lain:
Meskipun tidak signifikan, pengaturan
StringBuilder
ukuran buffer awal akan memberikan keuntungan yang kecil.sumber
Oke, sekarang saya mengerti apa yang sedang terjadi, dan itu masuk akal.
Saya mendapat kesan bahwa
toString
baru saja melewati yang mendasarichar[]
ke dalam konstruktor String yang tidak mengambil salinan. Salinan kemudian akan dibuat pada operasi "tulis" berikutnya (misalnyadelete
). Saya yakin ini adalah kasusStringBuffer
di beberapa versi sebelumnya. (Ini bukan sekarang.) Tapi tidak -toString
hanya meneruskan array (dan indeks dan panjang) keString
konstruktor publik yang mengambil salinan.Jadi dalam kasus "reuse the
StringBuilder
" kita benar-benar membuat satu salinan data per string, menggunakan array karakter yang sama di buffer sepanjang waktu. Jelas membuat baruStringBuilder
setiap kali membuat buffer yang mendasari baru - dan kemudian buffer itu disalin (agak sia-sia, dalam kasus khusus kami, tetapi dilakukan untuk alasan keamanan) saat membuat string baru.Semua ini mengarah pada versi kedua yang pasti lebih efisien - tetapi pada saat yang sama saya masih mengatakan itu kode yang lebih jelek.
sumber
Karena saya rasa itu belum ditunjukkan, karena pengoptimalan yang dibangun ke dalam kompiler Java Sun, yang secara otomatis membuat StringBuilders (StringBuffers pra-J2SE 5.0) ketika melihat rangkaian String, contoh pertama dalam pertanyaan tersebut setara dengan:
Mana yang lebih mudah dibaca, IMO, pendekatan yang lebih baik. Upaya Anda untuk mengoptimalkan dapat menghasilkan keuntungan di beberapa platform, tetapi berpotensi kehilangan yang lain.
Tetapi jika Anda benar-benar mengalami masalah dengan kinerja, maka pastikan, optimalkan. Saya akan mulai dengan secara eksplisit menentukan ukuran buffer dari StringBuilder, menurut Jon Skeet.
sumber
JVM modern benar-benar pintar tentang hal-hal seperti ini. Saya tidak akan menebak-nebak dan melakukan sesuatu yang hacky yang kurang dapat dipelihara / dibaca ... kecuali Anda melakukan benchmark yang tepat dengan data produksi yang memvalidasi peningkatan kinerja yang tidak sepele (dan mendokumentasikannya;)
sumber
Berdasarkan pengalaman saya dengan mengembangkan perangkat lunak pada Windows, saya akan mengatakan membersihkan StringBuilder selama loop Anda memiliki kinerja yang lebih baik daripada membuat Instansiasi StringBuilder dengan setiap iterasi. Menghapusnya membebaskan memori itu untuk segera ditimpa tanpa alokasi tambahan yang diperlukan. Saya tidak cukup paham dengan pengumpul sampah Java, tapi menurut saya membebaskan dan tidak ada realokasi (kecuali string Anda berikutnya menumbuhkan StringBuilder) lebih bermanfaat daripada instantiasi.
(Pendapat saya bertentangan dengan apa yang disarankan orang lain. Hmm. Saatnya untuk mengukurnya.)
sumber
Alasan mengapa melakukan 'setLength' atau 'delete' meningkatkan kinerja sebagian besar adalah kode 'mempelajari' ukuran buffer yang tepat, dan lebih sedikit untuk melakukan alokasi memori. Secara umum, saya menyarankan agar kompilator melakukan optimasi string . Namun, jika kinerjanya kritis, saya akan sering menghitung sebelumnya ukuran buffer yang diharapkan. Ukuran StringBuilder default adalah 16 karakter. Jika Anda tumbuh lebih dari itu, maka ukurannya harus diubah. Mengubah ukuran adalah saat performa hilang. Berikut patokan mini lain yang menggambarkan hal ini:
Hasilnya menunjukkan penggunaan ulang objek sekitar 10% lebih cepat daripada membuat buffer dengan ukuran yang diharapkan.
sumber
LOL, pertama kali saya melihat orang membandingkan kinerja dengan menggabungkan string di StringBuilder. Untuk tujuan itu, jika Anda menggunakan "+", itu bisa lebih cepat; D. Tujuan menggunakan StringBuilder untuk mempercepat pengambilan seluruh string sebagai konsep "lokalitas".
Dalam skenario di mana Anda sering mengambil nilai String yang tidak perlu sering diubah, Stringbuilder memungkinkan kinerja pengambilan string yang lebih tinggi. Dan itulah tujuan menggunakan Stringbuilder .. tolong jangan MIS-Test tujuan intinya ..
Beberapa orang berkata, Pesawat terbang lebih cepat. Oleh karena itu, saya mengujinya dengan sepeda saya, dan ternyata pesawat bergerak lebih lambat. Tahukah Anda bagaimana saya mengatur pengaturan percobaan; D
sumber
Tidak secara signifikan lebih cepat, tetapi dari pengujian saya itu menunjukkan rata-rata menjadi beberapa mil lebih cepat menggunakan 1.6.0_45 64 bit: gunakan StringBuilder.setLength (0) daripada StringBuilder.delete ():
sumber
Cara tercepat adalah dengan menggunakan "setLength". Ini tidak akan melibatkan operasi penyalinan. Cara membuat StringBuilder baru harus benar-benar keluar . Lambatnya StringBuilder.delete (int start, int end) adalah karena ia akan menyalin array lagi untuk bagian yang mengubah ukuran.
Setelah itu, StringBuilder.delete () akan memperbarui StringBuilder.count ke ukuran baru. Sedangkan StringBuilder.setLength () hanya menyederhanakan update StringBuilder.count ke ukuran yang baru.
sumber
Yang pertama lebih baik untuk manusia. Jika yang kedua sedikit lebih cepat pada beberapa versi dari beberapa JVM, lalu apa?
Jika kinerja sangat penting, lewati StringBuilder dan tulis milik Anda sendiri. Jika Anda seorang programmer yang baik, dan mempertimbangkan bagaimana aplikasi Anda menggunakan fungsi ini, Anda seharusnya dapat membuatnya lebih cepat. Bermanfaat? Mungkin tidak.
Mengapa pertanyaan ini dipandang sebagai "pertanyaan favorit"? Karena pengoptimalan kinerja sangat menyenangkan, tidak peduli apakah itu praktis atau tidak.
sumber
Saya rasa tidak masuk akal untuk mencoba mengoptimalkan kinerja seperti itu. Hari ini (2019) kedua statment berjalan sekitar 11 detik untuk 100.000.000 loop di Laptop I5 saya:
==> 11000 msec (deklarasi di dalam loop) dan 8236 msec (deklarasi loop luar)
Bahkan jika saya menjalankan program untuk pengurangan alamat dengan beberapa miliar pengulangan dengan selisih 2 detik. untuk 100 juta loop tidak ada bedanya karena program tersebut berjalan berjam-jam. Ketahuilah juga bahwa semuanya berbeda jika Anda hanya memiliki satu pernyataan tambahan:
==> 3416 msec (loop dalam), 3555 msec (loop luar) Pernyataan pertama yang membuat StringBuilder dalam loop lebih cepat dalam kasus itu. Dan, jika Anda mengubah urutan eksekusi, ini jauh lebih cepat:
==> 3638 msec (loop luar), 2908 msec (loop dalam)
Salam, Ulrich
sumber
Nyatakan sekali, dan tetapkan setiap kali. Ini adalah konsep yang lebih pragmatis dan dapat digunakan kembali daripada pengoptimalan.
sumber