Dize birleştirme neden dizi birleştirmeden daha hızlıdır?


114

Bugün bu konuyu okudum dize birleştirme hızı hakkındaki .

Şaşırtıcı bir şekilde, dizi birleştirme kazanan oldu:

http://jsben.ch/#/OJ3vo

Sonuç düşündüğümün tam tersi oldu. Ayrıca, gibi zıt açıklamak bu konuda çok sayıda madde vardır bu .

Tarayıcıların concaten son sürümü dizmek için optimize edildiğini tahmin edebilirim , ancak bunu nasıl yapıyorlar? +Dizeleri birleştirirken kullanmanın daha iyi olduğunu söyleyebilir miyiz ?


Güncelleme

Bu nedenle, modern tarayıcılarda dizgi birleştirme optimize edilmiştir, böylece +işaretleri kullanmak join, birleştirmek istediğinizde kullanmaktan daha hızlıdır. dizeleri .

Ama @Arthur işaret olduğunu joindaha hızlı aslında istiyorsanız olduğunu katılmak bir ayırıcı ile dizeleri.


Güncelleme - 2020
Chrome: Dizi joinneredeyse 2 times fasterString concat'dir + Bkz .: https://stackoverflow.com/a/54970240/984471

Not olarak:

  • Dizi joinvarsa daha iyidirlarge strings
  • several small stringsNihai çıktıda üretmeye ihtiyacımız varsa , dizge concat ile gitmek daha iyidir +, aksi takdirde Array ile gitmek , sonunda performans aşırı yüklemesi olan birkaç Array to String dönüşümüne ihtiyaç duyar.


1
Bu kodun 500 terabayt çöp üretmesi gerekiyor, ancak 200 ms'de çalışıyor. Sanırım bir dizge için biraz daha fazla alan ayırıyorlar ve ona kısa bir dize eklediğinizde, genellikle fazladan bir alana sığıyor.
Ivan Kuckir

Yanıtlar:


149

Tarayıcı dizesi optimizasyonları, dizi birleştirme resmini değiştirdi.

Firefox, dize birleştirmeyi optimize eden ilk tarayıcıydı. 1.0 sürümünden başlayarak, dizi tekniği aslında her durumda artı işlecini kullanmaktan daha yavaştır. Diğer tarayıcılar da dize birleştirmeyi optimize etti, bu nedenle Safari, Opera, Chrome ve Internet Explorer 8, plus operatörünü kullanarak daha iyi performans gösteriyor. Sürüm 8'den önceki Internet Explorer'da böyle bir optimizasyon yoktu ve dolayısıyla dizi tekniği her zaman artı operatöründen daha hızlıdır.

- Etkili JavaScript Yazma: Bölüm 7 - Daha Hızlı Web Siteleri

V8 javascript motoru (Google Chrome'da kullanılır) dize birleştirme yapmak için bu kodu kullanır :

// 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, "");
}

Böylece, dahili olarak bir InternalArray ( partsdeğişken) oluşturarak onu optimize ederler ve bu daha sonra doldurulur. StringBuilderConcat işlevi bu parçalarla çağrılır. Hızlıdır çünkü StringBuilderConcat işlevi büyük ölçüde optimize edilmiş C ++ kodudur. Burada alıntı yapmak çok uzun, ancak kodu görmek için runtime.cc dosyasında arama yapın RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat).


4
Gerçekten ilginç olan şeyi dışarıda bıraktınız, dizi yalnızca farklı argüman sayılarıyla Runtime_StringBuilderConcat'i çağırmak için kullanılır. Ama asıl iş orada yapılır.
evilpie

41
Optimizasyon 101: En az yavaş olanı hedeflemelisiniz! örneğin, arr.join vs str+kromda elde edersiniz (saniye başına işlem olarak) 25k/s vs 52k/s. firefox'ta yeni alırsınız 76k/s vs 212k/s. böylece str+HIZLI olduğunu. ama diğer tarayıcılara bakalım. Opera, 43k / s ile 26k / s verir. IE verir 1300/s vs 1002/s. ne olacağını gördün mü? Sadece tarayıcı İHTİYACI optimizasyonu daha iyi hiç önemli değil tüm diğerleri, daha yavaş olanı kullanılarak kapalı olacağını söyledi. Yani, bu makalelerin hiçbiri performans hakkında hiçbir şey anlamıyor.
gcb

45
@gcb, Join'in daha hızlı olduğu tek tarayıcı kullanılmamalıdır. Kullanıcılarımın% 95'i FF ve Chrome'a ​​sahip. % 95 kullanım senaryosu için optimize edeceğim.
Paul Draper

7
@PaulDraper, kullanıcıların% 90'ı hızlı bir tarayıcıdaysa ve seçeceğiniz seçeneklerden herhangi biri onlara 0.001 saniye kazandıracak, ancak diğer kullanıcıları bu 0.001'lerden cezalandırmayı seçerseniz, kullanıcılarınızın% 10'u 2 saniye kazanacak ... karar temiz. Göremiyorsanız, kodladığınız kişi için üzgünüm.
gcb

7
Daha eski tarayıcılar sonunda ortadan kalkacak, ancak birisinin tüm bu dizi birleşimlerini dönüştürmek için geri dönme ihtimali pek muhtemel değil. Mevcut kullanıcılarınız için büyük bir rahatsızlık olmadığı sürece gelecek için kod yazmak daha iyidir. Eski tarayıcılarla uğraşırken birleştirme performansından çok endişelenilmesi gereken daha önemli şeyler olma ihtimali vardır.
Thomas Higginbotham

23

Firefox hızlıdır çünkü Halatlar ( Halatlar: Dizelere Bir Alternatif ) kullanır. Halat, temelde sadece bir DAG'dir ve her Düğümün bir dizge olduğu yerde.

Örneğin, yaparsanız a = 'abc'.concat('def'), yeni oluşturulan nesne şöyle görünecektir. Elbette bu tam olarak bellekte göründüğü gibi değil, çünkü dizge türü, uzunluğu ve belki başka bir alan için hala bir alana ihtiyacınız var.

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

Ve b = a.concat('123')

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

Yani en basit durumda, sanal makinenin neredeyse hiç iş yapmaması gerekir. Tek sorun, bunun ortaya çıkan dizedeki diğer işlemleri biraz yavaşlatmasıdır. Ayrıca bu tabii ki bellek yükünü de azaltır.

Öte yandan ['abc', 'def'].join(''), genellikle yeni dizgiyi bellekte düz bir şekilde yerleştirmek için bellek ayırır. (Belki bu optimize edilmelidir)


6

Bunun eski bir konu olduğunu biliyorum, ancak testiniz yanlış. Sen yapıyorsun output += myarray[i];daha fazla gibi olması gerekirken output += "" + myarray[i];bir konuda öğeleri birlikte tutkal olduğuna göre, unuttum çünkü. Concat kodu aşağıdaki gibi olmalıdır:

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

Bu şekilde, öğelerin birbirine yapıştırılması nedeniyle bir yerine iki işlem yaparsınız.

Array.join() daha hızlı.


Cevabını alamadım "" +Orijinal ile koymak arasındaki fark nedir ?
Sanghyun Lee

Her yinelemede bir yerine iki işlem daha fazla zaman alır.
Arthur

1
Ve neden bunu koymamız gerekiyor? Zaten outputonsuz öğeleri yapıştırıyoruz .
Sanghyun Lee

Çünkü Join böyle çalışır. Örneğin, siz de yapabilirsiniz Array.join(",")sizin çalışmaz hangi fordöngü
Arthur

Oh anladım. Join () 'nin daha hızlı olup olmadığını görmek için zaten test ettiniz mi?
Sanghyun Lee

5

Büyük miktarda veri birleştirme daha hızlıdır, bu nedenle soru yanlış belirtilir.

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

Chrome 72.0.3626.119, Firefox 65.0.1, Edge 42.17134.1.0'da test edilmiştir. Dizi oluşturma dahil edildiğinde bile daha hızlı olduğunu unutmayın!


~ Ağu 2020. Doğru. Chrome'da: Dizi Birleştirme süresi: 462. String Concat (+) süresi: 827. Join neredeyse 2 kat daha hızlıdır.
Manohar Reddy Poreddy

3

Oradaki kriterler önemsiz. Aynı üç öğeyi tekrar tekrar birleştirmek satır içine alınacak, sonuçlar deterministik kanıtlanacak ve hatırlanacak, çöp işleyici sadece dizi nesnelerini atacak (boyut olarak hiçbir şeyin yanında olmayacak) ve muhtemelen sadece dış referanslar ve çünkü dizeler asla değişmez. Test rastgele oluşturulmuş çok sayıda dizge olsaydı daha çok etkilenirdim. Bir ya da iki dizide olduğu gibi.

Array.join FTW!


2

Dizelerle daha büyük bir arabelleği önceden tahsis etmenin daha kolay olduğunu söyleyebilirim. Her eleman sadece 2 bayttır (eğer UNICODE ise), bu nedenle muhafazakar olsanız bile, dizge için oldukça büyük bir tampon önceden tahsis edebilirsiniz. İle arraysher unsur olduğu için her öğe, daha "karmaşık" bir Objectmuhafazakar uygulama daha az elemanları için yer preallocate böylece.

for(j=0;j<1000;j++)Her birinin önüne bir eklemeye çalışırsanız for, hız farkının (krom altında) azaldığını göreceksiniz. Sonunda, dize birleştirme için hala 1.5x idi, ancak daha önceki 2.6'dan daha küçüktü.

VE öğeleri kopyalamak zorunda, bir Unicode karakteri muhtemelen bir JS Nesnesine yapılan referanstan daha küçüktür.

JS motorlarının birçok uygulamasının, yazdığım her şeyi işe yaramaz hale getirecek tek tip diziler için bir optimizasyona sahip olma olasılığı olduğunu unutmayın :-)


1

Bu test , dizi.join yöntemiyle yapılan yerine atama bitiştirme ile yapılmış bir dizeyi gerçekten kullanmanın cezasını gösterir. Genel atama hızı Chrome v31'de hala iki kat daha hızlı olsa da, artık ortaya çıkan dizeyi kullanmadığınız zamanki kadar büyük değil.


0

Bu açıkça javascript motor uygulamasına bağlıdır. Bir motorun farklı sürümleri için bile önemli ölçüde farklı sonuçlar elde edebilirsiniz. Bunu doğrulamak için kendi kıyaslamanızı yapmalısınız.

String.concatV8'in son sürümlerinde daha iyi performansa sahip olduğunu söyleyebilirim . Ancak Firefox ve Opera Array.joiniçin bir kazanan.


-1

Tahminimce, her sürüm birçok birleştirme maliyetini aşarken, birleştirme sürümleri buna ek olarak diziler oluşturuyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.