문자열 연결이 배열 결합보다 빠른 이유는 무엇입니까?


114

오늘 저는 문자열 연결 속도에 대해이 글을 읽었습니다 .

놀랍게도 문자열 연결이 승자였습니다.

http://jsben.ch/#/OJ3vo

결과는 제가 생각했던 것과 반대였습니다. 게다가, 같은 반대로 설명이에 대한 많은 기사가 .

브라우저가 concat최신 버전의 문자열 에 최적화되어 있다고 생각할 수 있지만 어떻게 그렇게합니까? +문자열을 연결할 때 사용하는 것이 더 낫다고 말할 수 있습니까 ?


최신 정보

따라서 최신 브라우저에서는 문자열 연결이 최적화되어 있으므로 +기호를 사용 join하는 것이 문자열 을 연결 하려는 경우 보다 빠릅니다 .

그러나 @Arthur는 지적join빨리 당신이 실제로하고 싶은 경우입니다 가입 구분와 문자열을.


업데이트-2020
Chrome : 배열 join은 거의 2 times faster문자열 연결입니다. + 참조 : https://stackoverflow.com/a/54970240/984471

참고로 :

  • join당신이 가지고 있다면 배열 이 더 좋습니다large strings
  • several small strings최종 출력에서 생성 이 필요한 경우 string concat을 사용하는 것이 좋습니다. +그렇지 않으면 Array를 사용하려면 끝에 여러 개의 Array to String 변환이 필요하므로 성능 오버로드가 필요합니다.


1
이 코드 는 500 테라 바이트의 쓰레기를 생성해야하지만 200ms 안에 실행됩니다. 나는 그들이 문자열에 대해 약간 더 많은 공간을 할당한다고 생각하고 짧은 문자열을 추가하면 일반적으로 여분의 공간에 맞습니다.
Ivan Kuckir 2017-08-13

답변:


149

브라우저 문자열 최적화로 인해 문자열 연결 그림이 변경되었습니다.

Firefox는 문자열 연결을 최적화 한 최초의 브라우저입니다. 버전 1.0부터 배열 기술은 실제로 모든 경우에 더하기 연산자를 사용하는 것보다 느립니다. 다른 브라우저도 문자열 연결을 최적화했기 때문에 Safari, Opera, Chrome 및 Internet Explorer 8도 더하기 연산자를 사용하여 더 나은 성능을 보여줍니다. 버전 8 이전의 Internet Explorer에는 이러한 최적화 기능이 없었으므로 배열 기술은 항상 더하기 연산자보다 빠릅니다.

효율적인 JavaScript 작성 : 7 장 – 더 빠른 웹 사이트

V8 자바 스크립트 엔진 (Google 크롬에서 사용됨)은 다음 코드 를 사용 하여 문자열 연결을 수행합니다.

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

따라서 내부적으로는 InternalArray ( parts변수) 를 생성하여이를 최적화 한 다음 채워집니다. StringBuilderConcat 함수는 이러한 부분과 함께 호출됩니다. StringBuilderConcat 함수는 고도로 최적화 된 C ++ 코드이기 때문에 빠릅니다. 여기에 인용하기에는 너무 길지만 코드를 보려면 runtime.cc 파일 에서 검색 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat)하십시오.


4
정말 흥미로운 것은 남겨두고 배열은 다른 인수 개수로 Runtime_StringBuilderConcat을 호출하는 데만 사용됩니다. 그러나 실제 작업은 거기에서 이루어집니다.
evilpie 2011-09-04

41
최적화 101 : 가장 느린 것을 목표로해야합니다! 예를 들어, arr.join vs str+크롬에서는 (초당 작업 수)을 얻습니다 25k/s vs 52k/s. firefox new에서 76k/s vs 212k/s. 그래서 str+더 빠릅니다. 하지만 다른 브라우저를 살펴 보겠습니다. Opera는 43k / s 대 26k / s를 제공합니다. IE는 1300/s vs 1002/s. 무슨 일이 일어나나요? 최적화가 필요한 유일한 브라우저는 전혀 문제가되지 않는 다른 모든 브라우저에서 더 느린 브라우저를 사용하는 것이 좋습니다. 따라서 이러한 기사 중 어느 것도 성능에 대해 이해하지 못합니다.
gcb 2013-09-21

45
@gcb, 가입 속도가 더 빠른 유일한 브라우저는 사용하지 마십시오. 내 사용자의 95 %는 FF와 Chrome을 사용합니다. 95 % 사용 사례를 최적화 할 것입니다.
Paul Draper

7
@PaulDraper 사용자의 90 %가 빠른 브라우저를 사용하고 어떤 옵션을 선택하든 0.001 초를 얻지 만 0.001 초 중 다른 사용자에게 페널티를 주면 10 %의 사용자가 2 초를 얻습니다. 명확합니다. 당신이 그것을 볼 수 없다면, 나는 당신이 누구를 위해 코딩하든 죄송합니다.
gcb

7
구형 브라우저는 결국 사라질 것이지만 누군가가 모든 어레이 조인을 변환하기 위해 돌아갈 가능성은 거의 없습니다. 현재 사용자에게 큰 불편을주지 않는 한 미래를 위해 코딩하는 것이 좋습니다. 오래된 브라우저를 다룰 때 연결 성능보다 걱정할 더 중요한 사항이 있습니다.
Thomas Higginbotham

23

Firefox는 Ropes ( Ropes : an Alternative to Strings ) 라는 것을 사용하기 때문에 빠릅니다 . 로프는 기본적으로 모든 노드가 문자열 인 DAG입니다.

예를 들어를하면 a = 'abc'.concat('def')새로 생성 된 객체는 다음과 같습니다. 물론 이것이 메모리에서 정확히 어떻게 보이는지가 아닙니다. 문자열 유형, 길이 및 기타에 대한 필드가 필요하기 때문입니다.

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

b = a.concat('123')

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

따라서 가장 간단한 경우 VM은 거의 작업을 수행하지 않아도됩니다. 유일한 문제는 결과 문자열에 대한 다른 작업이 약간 느려진다는 것입니다. 또한 이것은 물론 메모리 오버 헤드를 줄입니다.

반면에 ['abc', 'def'].join('')일반적으로 메모리를 할당하여 새 문자열을 메모리에 평평하게 배치합니다. (최적화되어야 할 수도 있음)


6

이것이 오래된 스레드라는 것을 알고 있지만 테스트가 잘못되었습니다. 잊었 기 때문에 물건을 무언가와 함께 붙이는 output += myarray[i];것과 비슷해야하는 동안 당신은하고 있습니다 output += "" + myarray[i];. 연결 코드는 다음과 같아야합니다.

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

그런 식으로 요소를 함께 붙이기 때문에 하나가 아닌 두 가지 작업을 수행합니다.

Array.join() 가 더 빠르다.


나는 당신의 대답을 얻지 못합니다. 퍼팅 "" +과 원본의 차이점은 무엇입니까 ?
이상현

더 많은 시간이 걸리는 각 반복에서 하나가 아닌 두 가지 작업입니다.
아서

1
그리고 우리는 왜 그것을 넣어야합니까? 우리는 이미 output그것없이 항목을 접착 하고 있습니다.
Sanghyun 리

이것이 조인이 작동하는 방식이기 때문입니다. 예를 들어, 당신은 또한 할 수있는 Array.join(",")당신과 함께 작동하지 않습니다하는 for루프
아서

오, 알았어. join ()이 더 빠른지 이미 테스트 했습니까?
Sanghyun 리

5

많은 양의 데이터 결합이 더 빠르기 때문에 질문이 잘못 설명됩니다.

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에서 테스트되었습니다. 어레이 생성이 포함되어 있어도 더 빠릅니다!


~ 2020 년 8 월. 사실. Chrome : 배열 결합 시간 : 462. 문자열 연결 (+) 시간 : 827. 결합이 거의 2 배 빠릅니다.
Manohar Reddy Poreddy

3

벤치 마크는 사소합니다. 동일한 세 항목을 반복적으로 연결하면 인라인 처리되고 결과는 결정적이고 메모되며, 가비지 처리기는 배열 객체 (크기가 거의 없음)를 버리고 아마도 스택에서 밀어 내고 튀어 나올 것입니다. 외부 참조와 문자열이 절대 변경되지 않기 때문입니다. 테스트가 무작위로 생성 된 문자열이 많으면 더 인상적 일 것입니다. 공연이나 2 분의 문자열처럼.

Array.join FTW!


2

문자열을 사용하면 더 큰 버퍼를 미리 할당하는 것이 더 쉽습니다. 각 요소는 2 바이트 (UNICODE 인 경우)에 불과하므로 보수적 인 경우에도 문자열에 대해 꽤 큰 버퍼를 미리 할당 할 수 있습니다. 로 arrays각 요소를이 때문에 각 원소보다 "복합체"인 Object보수적 구현이 적은 요소를위한 공간을 미리 할당 할 수 있도록한다.

for(j=0;j<1000;j++)각각 앞에 a를 추가하려고하면 for(크롬 아래에서) 속도 차이가 작아지는 것을 알 수 있습니다. 결국 문자열 연결의 경우 여전히 1.5 배 였지만 이전 2.6보다 작았습니다.

요소를 복사해야하는 경우 유니 코드 문자는 JS 개체에 대한 참조보다 작을 수 있습니다.

JS 엔진의 많은 구현이 내가 작성한 모든 것을 쓸모 없게 만드는 단일 유형 배열에 대한 최적화를 가질 가능성이 있음을 유의하십시오 :-)


1

이 테스트 는 할당 연결로 만든 문자열과 array.join 메서드로 만든 문자열을 실제로 사용할 때의 패널티를 보여줍니다. 전체 할당 속도는 Chrome v31에서 여전히 두 배 빠르지 만 결과 문자열을 사용하지 않을 때만 큼 크지는 않습니다.


0

이것은 분명히 자바 스크립트 엔진 구현에 달려 있습니다. 한 엔진의 다른 버전에 대해서도 상당히 다른 결과를 얻을 수 있습니다. 이를 확인하기 위해 자체 벤치 마크를 수행해야합니다.

String.concat최신 버전의 V8에서 더 나은 성능을 제공 한다고 말할 수 있습니다. 그러나 Firefox 및 Opera의 Array.join경우 승자입니다.


-1

내 생각에는 모든 버전이 많은 연결 비용을 부담하지만 조인 버전은 그 외에도 배열을 구축하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.