회사에서 유저에게 제공될 예약목록을 보여주는 기능을 구현하고 있습니다. 예약 건수가 별로 안되는데 timeout이 생기는 일이 종 종 생겨 Duration을 줄이기 위해 코드를 뜯어고쳐보고 있습니다. 최대한 쿼리 하는 부분을 줄였는데도 Duration을 잡을 수가 없었습니다.. 그래서 근본적인 로직을 다시 한번 분석해보기로 했습니다.
컴파일 비교는 [https://jsben.ch/BQhED] 여기서 확인했습니다.
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,15,16,17,18,23,123,12,31,23,123,123];
var newVar;
이 코드를 통해 진행해보았습니다. (배열 하나를 생성하여 확인해보았습니다.)
성능은 확실히 기본적인 for문과 while문이 뛰어났습니다. 그 이유는 무엇일까요? 이 실험을 기반으로 따지자면 for, while문이 for in, for of, forEach 문의 비해 1.4배 정도 효율성이 좋은 걸 확인할 수 있습니다. 배열의 길이가 더 길어질수록 효율성이 더 올라갈 것 같네요.
이 처럼 이런 차이가 생기는 이유는 무엇일까요?? 차근차근 하나씩 확인해보겠습니다.
for...in문
열거 가능한 non-Symbol 속성에 대해서만 반복합니다. 배열의 반복이 일관된 순서로 요소를 방문하지 못할 수도 있습니다.
Array나 Object 등 내장 constructor를 통해 만들어진 객체는 String의 indexOf(), Object의 toString()와 같이 Object.prototype, String.prototype 로부터 열거가 가능하지 않은 속성들을 상속해왔습니다. for...in문은 객체 자체의 모든 열거 가능한 속성들과 프로토타입 체인으로부터 상속받은 속성들에 대해 반복할 것입니다.
for...in은 객체를 반복하기 위해서 만들어졌지만 배열의 반복만을 위해서 사용하기에는 추천하지 않습니다. 배열이 데이터의 저장에 있어서는 더 실용적이지만, 키-값 쌍이 선호되는 데이터의 경우(속성이 "key"의 역할을 함) 특정 값을 가진 키가 있는지 확인하려는 경우에 for...in을 사용할 수 있습니다.
for...of문
반복 가능한 객체(ex) Array, Map, Set, String 등등..)에 대해서 반복하고 각 개별 속성 값에 대해 실행되는 문이 있는 사용자 정의 반복 후크를 호출하는 루프를 생성합니다. for..in과 차이가 있다면 for...in구문은 열거 가능한 non-Symbol 속성에 대해서만 반복하고, for... of구문은 컬렉션 전용입니다. 모든 객체보다는, [Symbol.iterator] 속성이 있는 모든 컬렉션 요소에 대해 이 방식으로 반복합니다.
forEach문
forEach()는 주어진 callback을 배열에 있는 각 요소에 대해 오름차순으로 한 번씩 실행합니다. 삭제했거나 초기화하지 않은 인덱스 속성에 대해서는 실행하지 않습니다. (예: 희소 배열)
forEach문은 예외를 던지지 않고는 forEach()를 중간에 멈출 수 없습니다. 중간에 멈춰야 한다면 forEach()가 적절한 방법이 아닐 수도 있습니다.
공통적으로 모두 시간 복잡도는 O(N)으로 똑같습니다. 하지만, 쓰는 환경, 필요도에 따라 성능의 차이가 3배 이상 날 수 있습니다. 주어진 상황마다 최적의 방법을 찾아서 구현하는 것이 중요한 것 같습니다.
TMI - 결국 최적의 방법으로 찾아서 Duration을 줄였지만 근본적인 원인은 메모리였습니다! ㅋㅋ aws 서버의 로직 메모리 성능을 향상했더니 적어도 timeout은 나오지 않았습니다. 메모리를 효율적으로 할당하는 것도 정말 중요하다고 생각합니다. 많은 것들을 배우면서 성장하고 있는 느낌이... 하하....
댓글