programming language/javascript

Javascript에는 StringBuilder가 필요 없나?

공대키메라 2022. 4. 30. 15:45

이 글은 필자의 궁금증으로 작성했습니다. 공부합시다!

1. 발단

키메라 : 저 js 에서 StirngBuilder로 이렇게 작업했어요. 어때요? ㅎㅎ :)

동기A : 굳이 이렇게 해야 하는 이유가 있나요?

키메라 : StringBuilder를 사용하면 메모리를 아낄 수 있자나요

동기A : js에서도 그런가요? 그리고 마지막에 join하나 더하나 똑같은거 아닌가요? 

키메라 : 보기 더 좋지 않나요? 자바처럼 함수 만들어서 쓰는게 깔끔하고 백틱을 사용해서 깔끔하게 보이게 하고 싶었어요.

동기A : 글쎄요 저는 별론데요? 굳이 그렇게 쓸 필요가 없어보이는데요?

키메라: ????

동기A : ????

2. 개요

js에서 StringBuilder를 만들어서 사용했는데, 내가 사용을 하면서도 무슨 단순히 보기 좋다는 이유로 사용하고 있다는 점을 발견했다. 궁금증 많은 동기에게 설명을 해주고 싶었지만 정작 쓰는 나도 모르다니...

 

그래서 이에 대해 파해쳐보고자 한다. 

 

필자는 최근에 회사에서 Node.js 기반 lambda 개발 작업을 하고 있었다.

내가 할 일은 기존 작업자에게 메시지를 보내는 것인데, 

이 코드의 템플릿에 따라서 정해진 코드값을 가진 템플릿을 조회해, 원하는 형식으로 변경하여 저장하는 것이다.

희안한 점은 쿼리문 자체가 DB에 저장이 되어 있었다. 또한, 그 쿼리를 저장하고 있는 테이블에 조건이 있었다. 

조건 컬럼이 있으면 해당 파라미터와 잘 조합해서 sql문을 직접 비즈니스 로직에서 생성 후, query로 생성되도록 하는 작업이다. 

여기서 문제는 너무 잦은 문자열 연산이 걱정되었다. 

 

또한, += 로 이어서 문자열을 덛붙이는 것도 보기가 너무 싫었다.

 

그래서 해당 코드를 찾아보았다.

StringBuilderTest.js

function StringBuilder(value) {
    this.strings = new Array();
    this.append(value);
}

StringBuilder.prototype.append = function (value) {
    if (value) {
        this.strings.push(value);
    }
}

StringBuilder.prototype.clear = function () {
    this.strings.length = 0;
}

StringBuilder.prototype.toString = function () {
    return this.strings.join("");
}

var sb = new StringBuilder();

sb.append("This is");
sb.append("much better looking");
sb.append("than using +=");

// joins the string
var myString = sb.toString();
console.log(myString)
// Cleans out the string buffer in the StringBuilder.
// This effectively makes it empty in case you did not 
// know what cleaning out a buffer in this context
// meant.
sb.clear();

 

출처 : https://gist.github.com/benjamin-wss/0a8a55673f2a5f21766c

 

그래서 인터넷에 js StringBuilder라고 검색을 하면 많은 내용이 나온다. 

 

출처 : https://www.google.com/search?q=js+string+builder&rlz=1C1GCEU_koKR1002KR1002&oq=js+&aqs=chrome.0.69i59l3j69i57j69i60j69i65j69i60l2.759j0j7&sourceid=chrome&ie=UTF-8

 

출처 : http://www.taeyo.net/Forum/Content.aspx?SEQ=1613&TBL=KNOWHOW&PGN=4

 

수 많은 글이 있었지만 실제로 이것이 과연... 단순 concatination보다 속도가 빠른지, 어느면에서 장점이 있는지 자세히 설명한 글을 찾기가 힘들었다.(특히 한국인이 쓴 글은 진짜 없음 - 필자 한국인임. 한국인 사랑함)

 

뒤져본 결과 다음 사이트에서 어느정도 답을 얻을 수 있었다.

 

다음 사이트의 내용을 정리해서 소개하고자 한다.

 

출처 : https://josephmate.github.io/java/javascript/stringbuilder/2020/07/27/javascript-does-not-need-stringbuilder.html

3. 자바 스크립트는 StringBuilder가 필요 없다!

다음 내용은 위 사이트 글을 필자가 번역한 것이다.

 

오늘 필자(위 사이트의)는 많은 수의 concatenation을 위해 StringBuilder가 js에서 필요없다는 것을 배웠다.
자바 프로그래머로서 이점은 나에게 충격적이다(나도...)  

Effective Java

Effective 자바에서 string concatenation의 성능을 알으라고 한다. 많은 블로그 글들이 이러한 내용과 stack overflow에 대해 설명한다. 

Experiments

처음 이에 대해 배웠을 때, 나는 믿을 수 없었다! 그래서 실험을 했다. 
나는 자바와 자바스크립트에서 concatenation이 얼마나 걸리는지 알아보고자 했다. 

두 개 언어 비교에 그렇게 흥미가 있지는 않았는데, 그래도 어떻게 runtime이 concatenation이 많을 때 반응하는지 궁금했다. 

나는 실험 결과는 다음 테이블에 정리했다. 

https://josephmate.github.io/java/javascript/stringbuilder/2020/07/27/javascript-does-not-need-stringbuilder.html

2^23에서 native java String concatenation은 작동하는데 너무 오래 걸린다. 2^22는 2시간이 걸려서 포기해버렸다!

 

input size의 각각 doubling은 double runtime이상의 결과를 초래한다. (우리는 단순 N^2의 복잡도를 예상했다.)

 

나는 Java8과 Java14 둘다 최적화 될 것으로 예상하며 실험했다. 

 

심지어 stack overflow 글도 최적화가 될 거라고 말했음에도 불구하고, optimization은 loop안에 concatenation을 고칠 수 없었다.

 

... 생략

 

Javascript의 growth는 선형처럼 보인다. 그래프를 조사해보자. 단순한 java concatenation은 너무 빠르게 grow하고, 스케일이 남아있는 메소드의 grwoth를 분석하기 어렵게 만들어서 그래프에서 제외했다.

 

이 그래프만 보면 그래도 장점이 있는것 처럼 보인다. 글쎄... 다음글에는 왜 필요하지 않은지 이유를 말해주고 있다.

 

(이게 그래프를 보면 밀리 세컨드 단위이다. 밀리세컨드는 1초의 1/1000인데 미미한 차이가 아닌가... 싶다. )

 

Why Javascript Does not Need a StringBuilder

앞선 실험에서, 왜 그런지 알길 원했다.  이러한 호기심이 Dr.Axel Rauschemayer가 StringBuilder를 EXMAScript의 다음 버전에 추가하길 바라는 이메일로 이끌었다.


여기서 악셀 라우슈마예르라는 아저씨가 있는데 javascipt장인으로 보인다. 


이게 (StringBuilder)가 다음 버전에 지원할만 하니, 아니면 라이브러리로 지원하는게 낫니?

내가 아는 concatenatiojn방식은 2개인데,
+=와 배열 안에 push() , 후 join으로 합치는 것이다. 

첫번째 방식은 효율적일 가능성이 업지만, 2번은 모든 플랫폼에서 괜찮다!

라이브러리도 괜찮지만, 자주 쓰인다면 standard library에 넣고 싶다

 

Tom Schuster가 그럴 필요 없다고 답했다

(1) (+=)은 실제로 modern engine에서 정말 잘 최적화 되어있다. 

아마도 존재하는 작동 문제에 메소드를 제공하는것은 좋은 생각이 아닌거 같다. 

 

나는 더 이에 대해 찾아봤고, ropes가 Immutable 한 String concatenation를 O(logN) 시간에 해결하도록 도와주는 data 구조라는것을 배웠다.

 

FireFox의 자바스크립트 Virtual Machine sourcecode는 Tom Schutster가 email에서 말한 것에 대해 설명한다. 

 

To avoid O(n^2) char buffer copying, a “rope” node (JSRope) can be created to represent a delayed string concatenation. Concatenation (called flattening) is performed if and when a linear char array is requested. In general, ropes form a binary dag whose internal nodes are JSRope string headers with no associated char array and whose leaf nodes are linear strings.

대충 필요없다는 말. 

여기서 Rope 데이터 구조라는 말이 나오는데 키메라는 이게 무슨 말인지 몰랐다.

 

이에 대한 정보를 찾아보았다. (여기 클릭)

 

로드 데이터 구조... 처음듣네!

 


로프 데이터 구조에 대한 자세한 설명은 위키피디아에서 확인이 가능하다.

 

Concatenation은 여태 로프(Rope)안에서 concats의 수인 O(logN)시간이 걸린다. 

 

결과적으로 N 번의 concat을 위해, 복잡도는 이 stack overflow글에서 설명됐듯이 O가 된다.

(stack ovreflow 글 읽기 클릭)

 

정말 큰 데이터에 StringBuilder를 사용하는건 좋지 않지만, 2^27의 연산 내에서는 Rope가 충분하다. 2^27 까지는 선형적으로 작동하기 때문이다.

 

이것 이상으로 무언가를 할 거라면, 특정한 상황에서 builder를 사용하는것이 더 나을 수 있다.


여기서 결론이 나왔다. 적은 연산 횟수에서는 크게 차이가 없다. 하지만 특정 상황( 2^27 이상인 경우)에는 고려해볼 만한다. 궁금한 사람은 직접 들어가서 보면 좋을 것 같다. 

3. 힙 메모리 절약이 되나?

눈썰미가 좋은 분들은 처음 부분에 다른 개발자 분이 적은 글을 캡쳐했을 때 힙 메모리 관리를 위해 이것을 StringBuilder를 사용한다 했는데 과연 그것이 진짜인지 궁금했다. 

 

이에 대한 글도 찾았는데 이 글을 읽으면 그 점에 동의할 수 있을 것이다. 선배 개발자님 감사합니다.

(여기 클릭!)

 

4. 결론

그렇게 복잡하지 않은 상황에서는 성능 차이가 크게 없으니 그대로 써도 됀다. 그래도 힙 메모리가 터질 경우가 생길 만큼 복잡하게 돌아가면 고려하는 것도 좋다. 

 

이렇게 보고 정리해도 아리쏭하다. 피드백이 있으면 언제든 환영!