Map vs Object 뭐가 더 빠를까?

잭슨·2024년 9월 8일
0

javascriprt

목록 보기
11/11
post-thumbnail

개요

MDN 에서 MapObject 에 비해서 키-값 쌍의 빈번한 추가 및 제거가 일어날 때 성능이 좀 더 좋다고 나와있길래 두 객체의 성능 차이가 궁금하여 포스팅하게 되었다.

이 글은 Map과 Object의 모든 차이점에 대해 다루는 것이 아닌, 오직 성능 차이에만 집중해서 다룬다.MapObject 의 삽입(set), 삭제(delete), 조회(get)의 속도 차이를 테스트를 통해 알아본다.

Map

코드

const len = 10_000_000;
const test = new Map();

function setTest() {
    console.time("Map Set");
    for (let i = 0; i < len; i++) {
        test.set(i, i);
    }
    console.timeEnd("Map Set");
}

function getTest() {
    console.time("Map Get");
    for (let i = 0; i < test.size; i++) {
        test.get(i);
    }
    console.timeEnd("Map Get");
}

function deleteTest() {
    console.time("Map Delete");
    for (let i = 0; i < test.size; i++) {
        test.delete(i);
    }
    console.timeEnd("Map Delete");
}
  • setTest() : test 객체에 10,000,00010,000,000개의 데이터를 삽입(set)한다.
  • getTest() : test 객체에 있는 10,000,00010,000,000개의 데이터를 조회(get)한다.
  • deleteTest() : test 객체에서 10,000,00010,000,000개의 데이터를 삭제(delete)한다.

실행

삽입, 조회, 삭제를 4사이클 실행해보자.

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();
/*
Map Set: 1.726s
Map Get: 422.872ms
Map Delete: 417.62ms

Map Set: 982.507ms
Map Get: 431.394ms
Map Delete: 265.018ms

Map Set: 1.237s
Map Get: 392.827ms
Map Delete: 234.23ms

Map Set: 1.128s
Map Get: 535.67ms
Map Delete: 242.832ms
*/

두 번째 사이클에 삽입, 삭제 속도가 크게 줄어들었다가, 세 번째 사이클 부터는 약간 증가한다. 하지만 최적화가 이루어져 첫 번째 사이클보다는 삽입, 삭제의 속도가 더 빠르다.

❗ 주의할 점

만약 반복문에서 Map 객체의 size 속성 대신 len 변수를 사용할 경우 최적화가 이루어지지 않는 것인지 삽입, 삭제 속도가 빨라지지 않는다.

const len = 10_000_000;
const test = new Map();

function setTest() {
    console.time("Map Set");
    for (let i = 0; i < len; i++) {
        test.set(i, i);
    }
    console.timeEnd("Map Set");
}

function getTest() {
    console.time("Map Get");
    for (let i = 0; i < len; i++) {
        test.get(i);
    }
    console.timeEnd("Map Get");
}

function deleteTest() {
    console.time("Map Delete");
    for (let i = 0; i < len; i++) {
        test.delete(i);
    }
    console.timeEnd("Map Delete");
}


setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

/*
Map Set: 1.747s
Map Get: 445.897ms
Map Delete: 1.082s

Map Set: 1.611s
Map Get: 428.978ms
Map Delete: 1.074s

Map Set: 1.695s
Map Get: 386.608ms
Map Delete: 1.064s

Map Set: 1.683s
Map Get: 390.052ms
Map Delete: 977.108ms
*/

Object

코드

const len = 10_000_000;
const test = {};

function setTest() {
    console.time("Object Set");
    for (let i = 0; i < len; i++) {
        test[i] = i;
    }
    console.timeEnd("Object Set");
}

function getTest() {
    console.time("Object Get");
    for (let i = 0; i < len; i++) {
        test[i];
    }
    console.timeEnd("Object Get");
}

function deleteTest() {
    console.time("Object Delete");
    for (let i = 0; i < len; i++) {
        delete test[i];
    }
    console.timeEnd("Object Delete");
}

실행

삽입, 조회, 삭제를 4사이클 실행해보자.

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();
/*
Object Set: 206.636ms
Object Get: 10.581ms
Object Delete: 343.207ms

Object Set: 47.889ms
Object Get: 8.863ms
Object Delete: 460.919ms

Object Set: 767.949ms
Object Get: 10.762ms
Object Delete: 450.853ms

Object Set: 754.043ms
Object Get: 12.036ms
Object Delete: 442.485ms
*/

Map 객체와 마찬가지로 두 번째 사이클에서는 삽입 속도가 현저히 줄어든 걸 볼 수 있다. 하지만 세번째 사이클부터는 첫번째 사이클보다 속도가 느려지는 것이 Map 객체와 다르다.

Object 를 사용했을 때 삽입, 삭제, 조회의 속도 자체는 더 빠른 모습을 확인할 수 있다. 하지만 객체의 길이가 고정된 것이 아니라 동적으로 변경되고 삭제나 조회 작업이 객체의 길이에 의존할 경우 객체의 길이를 구하는 로직을 추가해야된다. 추가된 로직을 고려하면 다음과 같다.

const test = {};

function setTest() {
    const len = 10_000_000;
    console.time("Object Set");
    for (let i = 0; i < len; i++) {
        test[i] = i;
    }
    console.timeEnd("Object Set");
}

function getTest() {
    console.time("Object Get");
    const len = Object.keys(test).length; // 객체의 길이 구하기
    for (let i = 0; i < len; i++) {
        test[i];
    }
    console.timeEnd("Object Get");
}

function deleteTest() {
    console.time("Object Delete");
    const len = Object.keys(test).length; // 객체의 길이 구하기
    for (let i = 0; i < len; i++) {
        delete test[i];
    }
    console.timeEnd("Object Delete");
}

getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();

setTest();
getTest();
deleteTest();
/*
Object Set: 204.993ms
Object Get: 1.099s
Object Delete: 1.401s

Object Set: 47.024ms
Object Get: 1.124s
Object Delete: 1.494s

Object Set: 733.109ms
Object Get: 1.232s
Object Delete: 1.442s

Object Set: 729.083ms
Object Get: 985.013ms
Object Delete: 1.705s
*/

getTestdeleteTest에 객체의 길이를 구하는 로직이 추가되었다. 객체의 길이를 구하는 로직은 O(n)O(n)의 시간복잡도를 갖기 때문에 이만큼에 시간이 더 소요된다. 따라서 조회와 삭제의 시간이 급격히 늘어난 것을 확인할 수 있다.

정리

  • Map
    • 객체의 길이가 동적으로 변해도 size 속성을 이용하여 O(1)O(1)의 시간복잡도로 객체의 길이를 구할 수 있다.
    • 삽입 삭제가 빈번히 발생할 경우 최적화가 되어 속도가 더 빨라진다.
    • 아무래도 삽입(set), 삭제(delete), 조회(get) 시 마다 함수를 호출하니 오버헤드가 생겨서 삽입, 삭제, 조회 속도는 Object 보다 속도가 더 느리다.
  • Object
    • 삽입, 삭제, 조회 작업이 객체의 길이에 의존할 경우 객체의 길이를 구하는 로직을 추가해야 하기 때문에 속도가 느려진다.
    • 삽입 삭제가 빈번히 일어날 경우 최적화가 이루어지지 않아 속도가 더 느려진다.
    • 삽입, 삭제, 조회 시 별도의 함수 호출이 필요없기 때문에 Map 보다 빠르다.

요약

MapObject 중 선택 시 여러 고려사항이 있겠지만, 성능적인 측면에서는 다음과 같이 요약할 수 있을 것 같다.

삽입, 삭제, 조회 시 객체의 길이를 구해야 할 경우 Map 을 고려해볼 수 있다. 하지만 그게 아니라면 높은 확률로 Object 가 더 빠를 것이다.

profile
지속적인 성장

0개의 댓글