javascript 반복문에 대한 고찰(1) - 성능

정(JJeong)·2025년 3월 21일

JavaScript 고찰

목록 보기
5/5

궁금증의 시작

코테 예제를 풀다가 다음 문제를 만났다.

코테 입문 문제

풀면서 처음 떠오른 아이디어는 ASCII 코드를 이용하자! 숫자와, 영문 모두 시작 코드에서 순차적으로 코드 넘버가 증가하니 활용하기 좋을 것 같았다.

a~j를 객체나 배열로 만들어 사용하는 것도 생각나긴 했지만 ASCII 코드를 써보고 싶었다.
코테를 풀면서 MDN을 보다보면 이것저것 메소드를 써보고 싶어서..ㅎㅎ

그래서 고심한 후 첫번째 해답을 적었다.

  1. 매개변수 age를 문자로 구성된 배열로 바꾼다(스프레드).
  2. 반복문으로 매개변수의 code값을 구한다.
  3. 표에 따라 숫자 0에서 영어 a는 49의 코드 차이가 나니 49를 더한다.
  4. 해당 코드값을 다시 문자로 변환한다.
  5. 반환된 새로운 배열을 다시 join을 이용해 문자열로 만든다.

이런 생각으로 아래 답변을 작성했다.

첫번째 답안

결과는 통과! 그런데 풀고나서 보니 각 값은 필요없고 인덱스만 있으면 돼서 '스프레드 연산자를 써서 배열로 만들고 지나갈 필요가 있었을까?'란 생각이 들었다.

그렇다면 그냥 for문을 쓰면되는데, 이 때 생긴 궁금증.

"for문과 map 중에 뭐가 성능이 좋았지??"

어차피 인덱스 값만 필요하다면 둘 중 성능이 좋은 걸 채택하면 될 문제다.

찾아본 결과 성능은 for문이 훨씬 좋다.
그 이유를 간단히 짚어보자면 map은 새로운 배열을 만드는데 리소스를 더 사용하기 때문이다.

두번째 답안

그래서 다시 for문으로 아래와 같이 답을 적었다.
근데 성능차는 거의 나지 않는다. 오히려 약간 느려 내 답안 업데이트가 되지 않았다.

이는 배열이 작을 경우엔 map도 리소스가 크지 않아 큰 성능차가 안날 수 있다는 것.
하지만 결국 반복할 값이 많아지면 성능차가 발생한단 소리이다.

그래서 업데이트는 되지 않았지만 이 답변을 더 나은 답변으로 봐야하지 않을까 싶다.



파생된 궁금증

위처럼 테스트를 하고 나니 여기에서 파생되어 추가적인 궁금증이 생겼다.
'다른 반복문의 성능은 어떨까?' 한 번 궁금증이 생긴 김에 수 많은 반복문들의 성능을 다시 짚고 넘어가잔 생각이 들었다.

분명 예전에 스치듯 봤었는데 기억이 잘 안나니 복기해야한다.

반복문의 성능

요즘 내 최고 서치 엔진이자 비서인 perplexity에게 물었다.

퍼플렉시티 가라사대 for(index) > for(length) > forEach > for...of > map method 순이라고 하는데

신기했던게 forEach가 for...of 보다 빠르다는 거였다. 개인적으론 메소드 계열이 좀 더 성능이 낮을거라고 판단했었다.
근데 Pro버전을 쓰고 있다한들 할루시네이션이 있을 수도 있으니 Perplexity가 첨부한 자료를 좀 더 보면서 재검증을 해보자.


실제 검증과 선택

거인의 어깨 위에 올라타라.

최근 참여했던 컨퍼런스에서 연사분이 하신 말이다. 내가 테스트해도 되지만 이미 해본 사람이 많으니 검증된 자료를 참고해보자.

무엇이 가장 빠른가?

js 반복문 성능 비교

Dev.to내에서 Sabbir Siddiqui라는 분의 아티클이다. 나와 같은 궁금증으로 이미 테스트를 진행했다.
(물론 대상은 for문 들이다)

counter 변수를 증가시키는 반복문의 실행 횟수는 10^8번 진행되었다. 빠른 순으로 보자면,

  1. for-loop-cached
  2. for-loop
  3. forEach
  4. for-of

가장 빠른 것은 for문에서 반복 범위에 대한 값 자체를 미리 캐시한 상태에서 하는 것이었다.
그냥 직관적으로만 보자면 변수를 한번 선언하고 진행하는 것이라 느릴 것 같지만 그렇지 않다는 것.

그리고 신기했던 foEach와 for-of문의 비교에서도 정말 forEach가 우위를 보였다.
메소드 접근이 무조건 느릴 것이라고 생각했던 내 선입견이 잘못 됐음을 느낀다.

그리고 아티클을 읽다보니 댓글에 추가적으로 다른 반복문까지 포함하여 성능 비교를 한 표가 있다.

댓글 언급 내용

이 댓글을 보면 whlie문과 do-while문을 포함해 비교했다. 두 가지 반복문은 for-loop와 비슷한 성능을 보인다는 것을 알 수 있다.

그럼 이제 위 반복문 중에 무조건 빠른거 쓰면 되는 걸까???


그러면 무조건 for-loop-cached?

위 기준대로 하면 무조건 빠른 for-loop-cached를 쓰면 될 것 같다. 하지만 하나 중요한 점은 우리가 반복문을 얼마나 돌릴 것인지 표본을 봐야한다.

위 예시는 10^8건, 즉 1억 건 정도 되는 매우 큰 표본의 반복문 시행이다.

서비스가 크면 저 정도를 다룰 수도 있으나 우리는 보통 그렇지 않을 것이다.(아닌가..?)
그런데 이걸 만약 10^3건 정도, 즉 1만 건 정도로 낮추면 차이는 미미하다. 신경쓰지 않아도 될 수준.

이 와중에 forOf는 확실히 느리다.

1천 건 시행(1만 건 이하)

forOf: 0.2
forEach: 0
forLoop: 0
forLoopCached: 0

10^3건 이하는 표본이 또 너무 적으니 기준을 올려보자. 10^3 ~ 10^6건 사이로 본다면 어떠한가?

10만 건 시행

forOf: 5.4
forEach: 0.4
forLoop: 2
forLoopCached: 0.6

위 결과는 10^4로 한 단계 높힌 10만건 기준이다. 이 정도는 실무에서 충분히 다뤄볼만한 양이다.
음 신기하게 이때는 cache를 하지 않은 forEach가 제일 빠르다.

참으로 신기방기 하네... 이땐 forLoop가 느렸다.
(while과 doWhile은 forLoop와 비슷하므로 다시 설명은 생략)

10만 건 이상 시행

10만 건 이상을 볼 때 다시 두가지 범위로 나눠 봐야한다.
이는 앞서 언급한 댓글에서 가져온 표를 참고해보면 왜 그런지 알 수 있다.

Case 1) 1천만 건 이하

표를 참조해 보면 1천 만건에선 속도가 다음과 같다.

forOf: 43.4
forEach: 15.1
forLoop: 26.4
forLoopCached: 26.2
while: 26.2
doWhile: 25.3

forEach > for-loop-cached >= for-loop == while == doWhile

즉, 1천만 건 이하 범위에서는 forEach가 효율적으로 보인다.

Case 2) 1억 건 이상

이건 앞서 메인 아티클에서 봤던 것과 같이 forEach의 성능이 확 떨어지면서 이 외 forLoop문과 while문 계열이 성능 유지가 되는 것을 알 수 있다.


그래서 어떤걸 사용하나?

속도 비교 오케이, 그럼 이제 어떨 때 어떤 걸 써야하나?
앞서 표본 단위를 바꿔가며 성능을 확인했다시피 무조건 많은 양의 표본에서 고성능을 보인 기능을 써야하는 것은 아니다.

각자의 서비스와 데이터량이 얼마냐에 따라 그에 맞춰 쓰는 것이 적절하다.
그렇지 않다면 JS에서 굳이 저 많은 반복문 사양을 만들어 놓을 이유가 무엇이 있겠는가?

물론 절대적 기준은 없기 때문에 개인적으로 기준을 새워 사용할 필요성이 있어 보인다.

나의 기준

내 기준은 두 가지이다.

  1. 데이터의 양
  2. 사용 편의성

가장 중요한 건 데이터 양에 따른 성능 그 자체이다. a라는 데이터가 있을 때 각 반복문 중 가장 성능이 좋은 것을 채택하면 된다.

앞서 테스트 결과에 따른 성능을 참조하자.


많은 데이터에서

1차적으로 데이터 양이 1억 건 이상이 되는 서비스라면 for-loop-cached를 사용할 것 같다.
그런데 아직까진 이 정도 규모의 데이터를 다뤄보진 못했다.

실제로 다루고 있는 사람도 몇이나 될까 싶긴한데,,,, 내가 경험하지 못한 서비스와 데이터는 무궁무진하니 어쨋든 첫번째 데이터 기준선은 1억건으로 둔다.


적은 데이터에서

그런데 만약 성능이 성능 비교에서 봤다시피 데이터 양에 따라 그 차이가 미미할 수 있다.
일단 1만 건 이하에서는 차이가 없다시피 했다. (for-of 제외: 미미한 와중에도 느리다)

그리고 그 이상을 보더라도 1천만 건 이하 정도에서는 많은 데이터일 때와 다르게 forLoop가 앞도적 성능을 보이진 않는다.

이럴 땐 두번째 기준인 사용 편의성을 봐야한다.

개발할 때 첫 째는 UX이지만 그 다음(어쩌면 다다다다다ㅏㅏㅏㅏ+nㅏ음ㅎㅎ)으로 DX를 고려하지 않을 수 없다. 일단 우리도 개발할 때 편하고, 빠르게 작성하면 좋지 않겠는가?

그렇다면 이때 난 역시 forEach를 선호할 것 같다.
일단 1만 건 이하의 데이터에서는 차이가 미비하기 때문에 메소드로 접근 가능한 forEach가 편의성 면에서 좋아보이고,
1천만 건 이하에서도 보면 편의성 뿐만 아니라 성능 면에서도 다른 반복문 로직보다 빠르거나 미미한 차이를 보이는 것을 알 수 있다. 오호라.. 그렇담 우린 이때? "오히려 좋아..!"

아니 쓰기 간편한 메소드가 성능도 낫다고? forEach를 안 쓸 이유가 없어보인다.




그 외 반복문 성능

위 내용은 순수하게 반복 시행을 목적으로 둔 로직들이었다. 근데 Array나 String 쪽으로 가면 그 외 반복문 메소드가 존재한다.

그 녀석들의 성능은 어떨까? 일단 서두에서 언급한 map()만 보더라도 성능이 위에 녀석들보다 떨어진다.
하지만 사용처가 명확하다. 콜백 함수 실행의 결과로 새로운 배열을 반환해니까.

그리고 사실 위에서 for-of만 언급했는데 for-in문도 있다. 그런데 얘는 위 아티클에서 다루지 않았는데 다루고 있는 다른 아티클이 있으니 위에서 보지 못했던 반복문의 성능을 마저 확인해보자.

성능 추가 비교

미디엄 아티클

이번엔 Medium에서 Chameera Dulanga의 성능 비교 아티클을 참조했다.
이 아티클의 경우엔 브라우져 별(Chrome, fireFox) 성능 비교를 하고 있어서 위에서 정리한 것과는 별개로 다시 한번 체크해보자.

  1. 같은 로직을 각 반복문에서 시행했을 때 브라우저별 성능 속도를 기준으로 한다.
  2. 시행 표본은 10만 건을 기준으로 한다.

테스트 결과

1. for-loop
- Chrome Output: 3ms
- Firefox Output: 2ms

2. for-in
- Chrome Output: 18ms
- Firefox Output: 12ms

3. for-of
- Chrome Output: 6ms
- Firefox Output: 4ms

4. forEach
- Chrome Output: 4ms
- Firefox Output: 2ms

5. map
- Chrome Output: 7ms
- Firefox Output: 3ms

6. while
- Chrome Output: 6ms
- Firefox Output: 37ms

7. doWhile
- Chrome Output: 9ms
- Firefox Output: 36ms

map메소드를 포함한 결과들이다(자세한 로직은 아래 참고 문헌란의 링크 확인).
이를 다시 성능 별 나열해보면,

1) Chrome 기준

  • for-loop > forEach > for-of == while > map > doWile > for-in

2) firefox 기준

  • for-loop == forEach > map > for-of > for-in > doWile > while

먼저 firefox 기준 비교는 제외토록 하겠다. while문과 doWhile문에서 저렇게 성능이 떨어지는 걸 보면 이건 js보단 브라우저 성능의 문제일 것 같아서 저걸 비교 척도로 놓긴 무리가 있어보인다. 그리고 대중성 측면에서도 chrome 비교를 주목하는 것이 나아보인다.

그렇담 chrome을 보자. 역시나 for-loopforEach가 높은 성능을 보인다.
그런데 10만 건 기준이기 때문에 앞서 우리가 봤듯이 어느 수준 이상에서는 for-loop가 더 높은 성능을 보이는 지점이 올 것이다.

그리고 지금 재비교를 하는 이유는 앞에서 못봤던 두가지 반복문을 더 보기 위함이다.
바로 map메소드와 for-in문이다. 두가지를 주목해보자.

map 메소드

일단 map메소드의 경우엔 역시 상대적으로 다른 반복문보다는 속도가 떨어지지만 10만 건 기준에선 doWhile과 for-in보다 나은 성능을 보였다.

그러나 예상컨데 데이터 양이 많아지면 역시 map메소드의 성능도 doWhile 이하로 떨어지지 않을까 싶다.

하지만 새로운 배열을 반환한다는 면에서 효용성은 활용처에 따라 무궁무진하다.

for-in 반복문

chrome기준에선 가장 성능이 떨어지는 방법이다. 이건 알다시피 객체의 반복문에서 사용되는데 아무래도 key값과 value값을 모두 맵핑해야해서 이런 성능 결과가 나오는 것이 아닌가 싶다.

개발자 측면에서는 작성 편의성이 있지만, 불과 10만 건에서도 이 정도의 성능 차이라면 for-loop와 forEach를 쓰는 것이 더 나은 방향일 것으로 보인다.




결론

이번엔 JavaScript에서 사용하는 반복문 방법들의 성능을 비교해보고, 나아가 어떤 기준으로 반복문을 사용하면 좋을지 고민해보았다.

결론적으로는 1천만 건 내외에서는 forEach를 사용하고, 그 이상의 데이터에서는 for-loopfor-loop-cached를 사용하는 것이 좋아보인다.

그리고 상대적으로 성능은 떨어지지만 map()for-in의 경우엔 각각 특화된 효용성이 있기 때문에 해당 경우엔 사용할 가치가 있고,

whliedoWhile의 경우엔 성능적 관점 보다는 code flow나 로직의 경우에 따라 효용성을 체크하고 사용할 수 있지 않을까 싶다. 가령 코드 가독성 측면을 높힌다던지?

이렇게 결론을 내리고 이번 글을 마치겠다. 이상..!


Ref.

profile
2년차 응애 FE 개발자입니다👶

0개의 댓글