Mengapa penggabungan string lebih cepat daripada gabungan array?

114

Hari ini, saya membaca utas ini tentang kecepatan penggabungan string.

Anehnya, rangkaian string adalah pemenangnya:

http://jsben.ch/#/OJ3vo

Hasilnya berlawanan dengan apa yang saya pikirkan. Selain itu, ada banyak artikel tentang ini yang menjelaskan sebaliknya seperti ini .

Saya dapat menebak bahwa browser dioptimalkan untuk merangkai concatpada versi terbaru, tetapi bagaimana mereka melakukannya? Bisakah kita mengatakan bahwa lebih baik digunakan +saat menggabungkan string?


Memperbarui

Jadi, di browser modern penggabungan string dioptimalkan sehingga menggunakan +tanda lebih cepat daripada menggunakan joinsaat Anda ingin menggabungkan string.

Tapi @Arthur menunjukkan itu joinlebih cepat jika Anda benar-benar ingin menggabungkan string dengan pemisah.


Pembaruan -
Chrome 2020 : Array joinhampir seperti 2 times fasterString concat + Lihat: https://stackoverflow.com/a/54970240/984471

Sebagai catatan:

  • Array joinlebih baik jika Anda punyalarge strings
  • Jika kita perlu menghasilkan several small stringsdalam keluaran akhir, lebih baik menggunakan string concat +, karena jika tidak menggunakan Array akan membutuhkan beberapa konversi Array ke String pada akhirnya yang merupakan kelebihan kinerja.

Sanghyun Lee
sumber
1
Kode ini seharusnya menghasilkan 500 terabyte sampah, tetapi berjalan dalam 200 ms. Saya pikir mereka hanya mengalokasikan sedikit lebih banyak ruang untuk sebuah string, dan ketika Anda menambahkan string pendek ke dalamnya, biasanya itu cocok dengan ruang ekstra.
Ivan Kuckir

Jawaban:

149

Optimasi string browser telah mengubah gambar penggabungan string.

Firefox adalah browser pertama yang mengoptimalkan penggabungan string. Dimulai dengan versi 1.0, teknik array sebenarnya lebih lambat daripada menggunakan operator plus di semua kasus. Browser lain juga telah mengoptimalkan penggabungan string, jadi Safari, Opera, Chrome, dan Internet Explorer 8 juga menunjukkan kinerja yang lebih baik menggunakan operator plus. Internet Explorer sebelum versi 8 tidak memiliki pengoptimalan seperti itu, sehingga teknik array selalu lebih cepat daripada operator plus.

- Menulis JavaScript yang Efisien: Bab 7 - Situs Web yang Lebih Cepat

Mesin javascript V8 (digunakan di Google Chrome) menggunakan kode ini untuk melakukan penggabungan string:

// ECMA-262, section 15.5.4.6
function StringConcat() {
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
    throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
  }
  var len = %_ArgumentsLength();
  var this_as_string = TO_STRING_INLINE(this);
  if (len === 1) {
    return this_as_string + %_Arguments(0);
  }
  var parts = new InternalArray(len + 1);
  parts[0] = this_as_string;
  for (var i = 0; i < len; i++) {
    var part = %_Arguments(i);
    parts[i + 1] = TO_STRING_INLINE(part);
  }
  return %StringBuilderConcat(parts, len + 1, "");
}

Jadi, secara internal mereka mengoptimalkannya dengan membuat InternalArray ( partsvariabel), yang kemudian diisi. Fungsi StringBuilderConcat dipanggil dengan bagian ini. Ini cepat karena fungsi StringBuilderConcat adalah kode C ++ yang sangat dioptimalkan. Terlalu panjang untuk mengutip di sini, tapi cari di file runtime.cc untuk RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat)melihat kodenya.

Daan
sumber
4
Anda meninggalkan hal yang sangat menarik, array hanya digunakan untuk memanggil Runtime_StringBuilderConcat dengan jumlah argumen yang berbeda. Tetapi pekerjaan sebenarnya dilakukan di sana.
evilpie
41
Pengoptimalan 101: Anda harus membidik yang paling lambat! misalnya, arr.join vs str+di chrome Anda mendapatkan (dalam operasi per detik) 25k/s vs 52k/s. di firefox baru Anda dapatkan 76k/s vs 212k/s. begitu str+juga CEPAT. tapi mari kita lihat browser lain. Opera memberikan 43k / s vs 26k / s. IE memberi 1300/s vs 1002/s. lihat apa yang terjadi? satu - satunya browser yang MEMBUTUHKAN pengoptimalan akan lebih baik menggunakan apa yang lebih lambat di semua yang lain, di mana itu tidak masalah sama sekali. Jadi, Tak satu pun dari artikel itu yang memahami apa pun tentang kinerja.
gcb
45
@gcb, satu-satunya browser yang bergabung lebih cepat sebaiknya tidak digunakan. 95% pengguna saya memiliki FF dan Chrome. Saya akan mengoptimalkan kasus penggunaan 95%.
Paul Draper
7
@PaulDraper jika 90% pengguna menggunakan browser cepat dan salah satu opsi yang Anda pilih akan memberi mereka 0,001, tetapi 10% pengguna Anda akan mendapatkan 2 d jika Anda memilih untuk menghukum pengguna lain dari 0,001 itu ... keputusan jelas. jika Anda tidak dapat melihatnya, saya minta maaf untuk siapa pun yang Anda kodekan.
gcb
7
Browser yang lebih lama pada akhirnya akan hilang, tetapi kemungkinan seseorang kembali untuk mengonversi semua gabungan array itu tidak mungkin. Lebih baik membuat kode untuk masa depan selama itu bukan merupakan ketidaknyamanan besar bagi pengguna Anda saat ini. Kemungkinan ada hal yang lebih penting untuk dikhawatirkan daripada kinerja penggabungan saat berurusan dengan browser lama.
Thomas Higginbotham
23

Firefox cepat karena menggunakan sesuatu yang disebut Ropes ( Ropes: an Alternative to Strings ). Tali pada dasarnya hanyalah DAG, di mana setiap Node adalah string.

Jadi misalnya, jika Anda mau a = 'abc'.concat('def'), objek yang baru dibuat akan terlihat seperti ini. Tentu saja ini tidak persis seperti yang terlihat di memori, karena Anda masih perlu memiliki bidang untuk jenis string, panjang dan mungkin lainnya.

a = {
 nodeA: 'abc',
 nodeB: 'def'
}

Dan b = a.concat('123')

b = {
  nodeA: a, /* {
             nodeA: 'abc',
             nodeB: 'def'
          } */
  nodeB: '123'
}           

Jadi dalam kasus yang paling sederhana, VM hampir tidak melakukan pekerjaan apa pun. Satu-satunya masalah adalah ini memperlambat operasi lain pada string yang dihasilkan sedikit. Juga ini tentu saja mengurangi overhead memori.

Di sisi lain ['abc', 'def'].join('')biasanya hanya akan mengalokasikan memori untuk meletakkan string baru dalam memori. (Mungkin ini harus dioptimalkan)

evilpie
sumber
6

Saya tahu ini utas lama, tetapi pengujian Anda salah. Anda melakukan output += myarray[i];sementara itu seharusnya lebih seperti output += "" + myarray[i];karena Anda lupa, bahwa Anda harus merekatkan barang menjadi satu dengan sesuatu. Kode concat harus seperti ini:

var output = myarray[0];
for (var i = 1, len = myarray.length; i<len; i++){
    output += "" + myarray[i];
}

Dengan begitu, Anda melakukan dua operasi, bukan satu karena elemen merekatkan bersama.

Array.join() lebih cepat.

Arthur
sumber
Saya tidak mendapatkan jawaban Anda. Apa perbedaan antara Puting "" +dan Original?
Sanghyun Lee
Ini dua operasi, bukan satu pada setiap iterasi yang membutuhkan lebih banyak waktu.
Arthur
1
Dan mengapa kita perlu mengatakan itu? Kami sudah merekatkan barang outputtanpa itu.
Sanghyun Lee
Karena begitulah cara join bekerja. Misalnya, Anda juga dapat melakukan Array.join(",")yang tidak akan berfungsi dengan forloop Anda
Arthur
Oh saya mengerti. Sudahkah Anda menguji untuk melihat apakah join () lebih cepat?
Sanghyun Lee
5

Untuk jumlah data yang besar, penggabungan lebih cepat, jadi pertanyaannya salah.

let result = "";
let startTime = new Date().getTime();
for (let i = 0; i < 2000000; i++) {
    result += "x";
}
console.log("concatenation time: " + (new Date().getTime() - startTime));

startTime = new Date().getTime();
let array = new Array(2000000);
for (let i = 0; i < 2000000; i++) {
    array[i] = "x";
}
result = array.join("");
console.log("join time: " + (new Date().getTime() - startTime));

Diuji di Chrome 72.0.3626.119, Firefox 65.0.1, Edge 42.17134.1.0. Perhatikan bahwa ini lebih cepat bahkan dengan pembuatan array disertakan!

hampir
sumber
~ Agustus 2020. Benar. Di Chrome: Array Join time: 462. String Concat (+) time: 827. Join hampir 2 kali lebih cepat.
Manohar Reddy Poreddy
3

Tolok ukurnya ada yang sepele. Menggabungkan tiga item yang sama berulang kali akan menjadi sebaris, hasilnya akan terbukti deterministik dan memoized, sampah handler hanya akan membuang objek array (yang ukurannya hampir tidak ada) dan mungkin hanya mendorong dan mengeluarkan tumpukan karena tidak ada referensi eksternal dan karena string tidak pernah berubah. Saya akan lebih terkesan jika pengujiannya adalah sejumlah besar string yang dibuat secara acak. Seperti dalam satu atau dua pertunjukan string.

Array. Bergabunglah dengan FTW!

Jeremy Moritz
sumber
2

Saya akan mengatakan bahwa dengan string lebih mudah untuk mengalokasikan buffer yang lebih besar. Setiap elemen hanya 2 byte (jika UNICODE), jadi meskipun Anda konservatif, Anda dapat mengalokasikan buffer yang cukup besar untuk string. Dengan arrayssetiap elemen lebih "kompleks", karena setiap elemen adalah Object, jadi implementasi konservatif akan mengalokasikan ruang untuk lebih sedikit elemen.

Jika Anda mencoba menambahkan for(j=0;j<1000;j++)sebelum masing-masing, forAnda akan melihat bahwa (di bawah chrome) perbedaan kecepatan menjadi lebih kecil. Pada akhirnya itu masih 1,5x untuk penggabungan string, tetapi lebih kecil dari 2,6 yang sebelumnya.

DAN harus menyalin elemen, karakter Unicode mungkin lebih kecil dari referensi ke Objek JS.

Sadarilah bahwa ada kemungkinan bahwa banyak implementasi mesin JS memiliki pengoptimalan untuk array tipe tunggal yang akan membuat semua yang saya tulis tidak berguna :-)

xanatos.dll
sumber
1

Tes ini menunjukkan hukuman sebenarnya menggunakan string yang dibuat dengan penggabungan tugas vs dibuat dengan metode array.join. Meskipun kecepatan keseluruhan penugasan masih dua kali lebih cepat di Chrome v31 tetapi tidak lagi sebesar saat tidak menggunakan string yang dihasilkan.

srgstm
sumber
0

Ini jelas tergantung pada implementasi mesin javascript. Bahkan untuk versi berbeda dari satu mesin Anda bisa mendapatkan hasil yang sangat berbeda. Anda harus melakukan patokan Anda sendiri untuk memverifikasi ini.

Saya akan mengatakan itu String.concatmemiliki kinerja yang lebih baik di versi terbaru V8. Namun untuk Firefox dan Opera, Array.joinadalah pemenangnya.

Vanuan
sumber
-1

Dugaan saya adalah bahwa, sementara setiap versi mengenakan biaya banyak penggabungan, versi gabungan juga membangun array.

Marcelo Cantos
sumber