티스토리 뷰

개요

TODO LIST를 만들면서 for문과 map 함수를 둘 다 쓰게 되었는데 어떤 것이 빠를지 궁금해져서 찾아보던 중 괜찮은 글을 보게되어 공부하며 해석 및 글로 정리하게 되었다.

https://leanylabs.com/blog/js-forEach-map-reduce-vs-for-for_of/

 

내용

프로그래머는 항상 JavaScript의 단순 loop보다 .map, .reduce, .forEach method를 선호해야 할까?

선언적 프로그래밍은 단순 loop에 비해 작성하기 쉽고, 간결하다는 장점이 있지만 성능이 중요한 경우에는 일반적으로 선언적 loop보다 단순 loop가 3배 이상 빠르다.

대부분의 응용 프로그램에서 큰 차이를 나타내지 않지만, 게임 엔진, 비디오 처리 등 많은 양의 데이터를 처리할 때 전체 성능에 막대한 영향을 미친다.

 

테스트 환경

funciton testArray() {
	const result = [];
    for (let i=0; i < 1000000; i++){
    	result.push({
        	a: i,
            b: i/2,
            r: 0
        })
    }
}
  • Node.js Version - 14.16.0
  • Intel Core i5-8250
  • 16GB RAM
  •  Lenovo T480s 노트북
  • 테스트 애플리케이션 = 벤치마크 라이브러리 (www.npmjs.com/package/benchmark)
  • 100만 개체의 배열로 테스트

 

1. Array.ForEach  VS  for  VS  for..of

테스트 방법

array.forEach((x) => {
	x.r = x.a + x.b;
});

a와 b의 값을 합산하여 r에 저장한다.

r 변수는 벤치마크 라이브러리의 영향을 미치기 때문에 개체 구조가 변경되지 않도록 Array 생성 중에 의도적으로 개체에 필드를 만든다.

 

결과

초당 작업 (높을수록 좋음)

loop는 .forEach에 비해 거의 3배 더 빠른 결과가 나왔다.

루프 for..of는 나머지보다 약간 앞서있으나 중요하진 않고, 배열 길이를 캐싱하거나 임시 변수에 반복적으로 액세스하기 위한 요소를 저장하는 것과 같이 일부 다른 언어에서 작동하는 for 루프는 V8에서 실행되는 JavaScript에 전혀 영향을 미치지 않았다.

.forEach는 for..of 루프와 크게 다르지 않기 때문에 대부분의 경우 어떤것을 사용하든 별 의미가 없으나, 모든 배열 요소에서 호출할 함수가 이미 있는 경우에는 사용할 가치가 있다.

해당 코드의 경우에는 성능 저하가 전혀 없는 코드이다.

array.forEach(func);

 

2. Array.map  VS  for  VS  for...of

테스트 방법

return array.map((x) => x.a + x.b);

map을 이용하여 배열의 a와 b를 합산하여 다른 배열에 맵핑한다.

 

const result = [];
for (const {a, b} of array) {
	result.push(a+b);
}
return result;

for..of는 빈 배열을 만들고 모든 새 요소를 push한다.

 

const result = new Array(array.length);
for (let i=0; i <array.length; ++i) {
	result[i] = array[i].a + array[i].b;
}
return result;

해당 코드는 최적의 접근 방식은 아니지만, for는 대상 크기로 배열을 사전 할당하고 인덱스를 사용하여 모든 요소를 설정한다.

 

결과

초당 작업 (높을수록 좋음)

이번에도 loop가 훨씬 빠르다는 결과가 나왔다.

 이번 테스트에서 구조 분해가 성능에 어떤 영향을 미치는지 확인할 수 있었는데, .map을 사용하면 벤치마크가 동일했고, for..of를 사용하면 결과가 그다지 다르지 않았지만, 이것은 벤치마크의 문제일 수도 있다.

 

3.  Array.reduce  VS  for VS  for..of

테스트 방법

return array.reduce((p, x) => p + x.a + x.b, 0);

전체 배열의 a와 b의 합계를 계산한다.

let result = 0;
for (const { a, b } of array) {
   result += a + b;
}
return result;

 

결과

초당 작업 (높을수록 좋음)

for와 for..of 모두 reduce보다 3.5배 빠르다는 결과가 나왔다.

하지만 for loop의 코드양은 reduce에 비해 훨씬 길다.

단순한 합계를 위해 많은 양의 코드 라인을 작성하는 것은 반드시 써야하는 이유가 있어야 하므로 성능이 그렇게 중요한 경우가 아니라면 .reduce가 훨씬 좋다.

해당 테스트는 loop 사이에 차이가 없음을 보여주었다.

 

결론

벤치마크 라이브러리는 loop를 사용한 명령형 프로그래밍이 .map과 .reduce 보다 더 나은 성능을 보인다는 것을 입증하였다.

그러나 단순한 합계보다 더 복잡한 코드의 경우 계산 자체에 훨씬 많은 시간이 걸리므로 상대적인 차이는 크지 않다.

 

또한 명령형 프로그래밍 코드는 대부분의 경우 훨씬 코드 라인이 길다.

간단한 합계 코드를 위해 5개의 코드 라인은 너무 많은 양이고, 반면 reduce는 한 줄에 불과하며, .forEach는 for또는 for..of와 코드 라인이 비슷하지만 더 느리다.

두 loop 간에는 성능 차이가 크지 않으며 알고리즘에 더 잘 맞는 것을 선택하여 사용하면 된다.

TypeScript의 기반 프로그래밍 언어인 AssmblyScript와 달리 for loop의 미세한 최적화는 JavaScript 배열에 적합하지 않은데 이미 V8에서 최적화 처리를 하고있기 때문이다.

 

좋은 개발자는 코드가 어떻게 동작하는지 알고 모든 상황에서 최상의 선택을 해야한다.

단순성을 지닌 선언적 프로그래밍을 대부분 선택하지만 '광범위한 프로파일링으로 발견된 병목 현상을 최적화' 하거나 '성능이 중요한 코드'인 경우에는 명령형 프로그래밍을 선택하는 것에 의미가 있을 것이다.

 

마지막으로 조기 최적화는 모든 악의 근원임을 기억해야한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함