Logo
Published on
·11 min read

JavaScript - 배열 생성 방법 4가지, 성능 비교

이 글은 이전 블로그에서 2023년 3월 2일 작성한 내용을 옮겨오면서 내용을 추가/수정한 글입니다.

개요

JavaScript에서 범위를 지정하여 배열을 생성이 필요했습니다.

범위가 작으면 어떤 방법을 사용해도 차이가 크지 않지만, 제 경우 50000000(5천만) 이상의 범위를 지정하니 성능 차이가 꽤 났습니다.

그런 이유로 몇 가지 방법을 시도해보고 성능을 비교해보았습니다.

4가지 방법 및 결과

테스트한 방법은 4가지로, 1 ~ 50,000,000 범위의 배열 생성 속도 측정 결과입니다.
  1. 빈 배열 생성 후, push (2위, 대략 0.5초 정도)

  2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경 (3위, 대략 1.7초 정도)

  3. Array.from 사용 (4위, 대략 3.5초 정도)

  4. ArrayBuffer(Uint32Array) 사용 (1위, 대략 0.1초 정도)


테스트 방법 4가지는 아래와 같습니다.

1. 빈 배열 생성 후, push

function generateNumberPush(min, max) {
  let arr = []
  for (let i = min; i <= max; i++) {
    arr.push(i)
  }
  return arr
}
  • 속도 2위 (대략 0.5초 정도 소요)
  • 아주 큰 범위가 아니면 제일 무난한 방법으로 판단됩니다.
  • [ ] 대신 new Array()를 사용해도 됩니다. 다만, 테스트해 보니 [ ]이 약간 더 빨랐습니다. (이유는 아직 찾지 못했습니다.ㅠㅠ)

2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경

function generateNumberIndex(min, max) {
  let range = max - min + 1
  let arr = new Array(range)
  for (let i = 0; i < arr.length; i++) {
    arr[i] = i + min
  }
  return arr
}
  • 속도 3위 (대략 1.7초 정도 소요)
  • 밀집성이 보장되지 않은 JavaScript 배열에서 인덱스로 위치를 찾는 시간이 소요되는 것으로 파악됩니다.
  • 아래에 MDN 문서의 Array 설명을 인용해 봅니다.

JavaScript 배열은 길이도, 각 요소의 자료형도 고정되어 있지 않습니다. 배열의 길이가 언제든지 늘어나거나 줄어들 수 있고 데이터를 연속적이지 않은 곳에 저장할 수 있으므로, JavaScript 배열은 밀집성을 보장하지 않습니다. 보통 이런 성질들은 편리하지만, 사용하려는 목적에 맞지 않으면 형식화 배열(typed array)을 사용하는 것을 고려해보세요.

3. Array.from 사용

function generateNumberFrom(min, max) {
  let range = max - min + 1
  let arr = Array.from({ length: range }, (v, i) => i + min)
  return arr
}
  • 속도 4위 (대략 3.5초 정도 소요)
  • 작은 범위의 배열을 생성한다면, 코드가 간결해지는 것이 장점이 될 듯합니다.

4. ArrayBuffer(Uint32Array) 사용

function generateNumberBuffer(min, max) {
  let range = max - min + 1
  let buffer = new ArrayBuffer(range * Uint32Array.BYTES_PER_ELEMENT)
  let arr = new Uint32Array(buffer)

  for (let i = 0; i < arr.length; i++) {
    arr[i] = i + min
  }
  return arr
}
  • 속도 1위 (대략 0.1초 정도 소요)
  • 배열 생성 후 배열 접근에서도 압도적 1위
    (console.log로 생성된 배열을 표시할 경우, Array를 사용한 다른 방법들은 한참 걸립니다. Array의 경우 밀집성이 보장되지 않은 JavaScript라서 각 인덱스를 찾아 표시하는데도 시간이 걸리는 것으로 파악됩니다.)
  • ArrayBuffer에 대한 더 자세한 소개는 만화로 소개하는 ArrayBuffer 와 SharedArrayBuffer(번역글로, 원글은 A cartoon intro to ArrayBuffers and SharedArrayBuffers)글을 참고 바랍니다.
  • 해당 글 중 ArrayBuffer가 빠른 이유를 설명하는 부분을 아래에 인용하겠습니다.
ArrayBuffer 는 기본적으로 메모리 자체인 것처럼 동작

합니다. ArrayBuffer 는 C 같은 랭귀지를 사용할 때처럼 메모리를 직접 다루는 방식과 비슷한 효과를 만들어 냅니다.

성능 테스트 코드

아래는 위 방법들을 테스트한 코드입니다.

  • 함수 선언
function generateNumberPush(min, max) {
  let startTime = new Date().getTime()
  console.log(`1. 빈 배열 생성 후, push -- startTime:${startTime}`)

  // logic ----------------------------------
  let arr = []
  for (let i = min; i <= max; i++) {
    arr.push(i)
  }
  // logic ----------------------------------

  let endTime = new Date().getTime()
  let duration = (endTime - startTime) / 1000

  console.log(`1. 빈 배열 생성 후, push -- endTime:${endTime}`)
  console.log(
    `1. 빈 배열 생성 후, push 결과 -- duration:${duration}, length:${arr.length}, first:${
      arr[0]
    }, last:${arr[arr.length - 1]}`
  )
  console.log('----------------------------------')
  return arr
}

function generateNumberIndex(min, max) {
  let startTime = new Date().getTime()
  console.log(`2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경 -- startTime:${startTime}`)

  // logic ----------------------------------
  let range = max - min + 1
  let arr = new Array(range)
  for (let i = 0; i < arr.length; i++) {
    arr[i] = i + min
  }
  // logic ----------------------------------

  let endTime = new Date().getTime()
  let duration = (endTime - startTime) / 1000
  console.log(`2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경 -- endTime:${endTime}`)
  console.log(
    `2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경 결과 -- duration:${duration}, length:${
      arr.length
    }, first:${arr[0]}, last:${arr[arr.length - 1]}`
  )
  console.log('----------------------------------')
  return arr
}

function generateNumberFrom(min, max) {
  let startTime = new Date().getTime()
  console.log(`3. Array.from 사용 -- startTime:${startTime}`)

  // logic ----------------------------------
  let range = max - min + 1
  let arr = Array.from({ length: range }, (v, i) => i + min)
  // logic ----------------------------------

  let endTime = new Date().getTime()
  let duration = (endTime - startTime) / 1000
  console.log(`3. Array.from 사용 -- endTime:${endTime}`)
  console.log(
    `3. Array.from 사용 결과 -- duration:${duration}, length:${arr.length}, first:${arr[0]}, last:${
      arr[arr.length - 1]
    }`
  )
  console.log('----------------------------------')
  return arr
}

function generateNumberBuffer(min, max) {
  let startTime = new Date().getTime()
  console.log(`4. ArrayBuffer(Uint32Array) 사용 -- startTime:${startTime}`)

  // logic ----------------------------------
  let range = max - min + 1
  let buffer = new ArrayBuffer(range * Uint32Array.BYTES_PER_ELEMENT)
  let arr = new Uint32Array(buffer)

  for (let i = 0; i < arr.length; i++) {
    arr[i] = i + min
  }
  // logic ----------------------------------

  let endTime = new Date().getTime()
  let duration = (endTime - startTime) / 1000
  console.log(`4. ArrayBuffer(Uint32Array) 사용 -- endTime:${endTime}`)
  console.log(
    `4. ArrayBuffer(Uint32Array) 사용 결과 -- duration:${duration}, length:${arr.length}, first:${
      arr[0]
    }, last:${arr[arr.length - 1]}`
  )
  return arr
}

function test(min, max) {
  generateNumberPush(min, max)
  generateNumberIndex(min, max)
  generateNumberFrom(min, max)
  generateNumberBuffer(min, max)
}

콘솔에 위 함수들을 선언한 코드를 붙여넣고 아래와 같이 실행하면 다음과 유사한 결과를 얻을 수 있습니다.

  • 실행
// run ------------------------------
test(1, 50000000)
// run ------------------------------
  • 결과
1. 빈 배열 생성 후, push -- startTime:1692335226507
1. 빈 배열 생성 후, push -- endTime:1692335226829
1. 빈 배열 생성 후, push 결과 -- duration:0.322, length:50000000, first:1, last:50000000
----------------------------------
2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경 -- startTime:1692335226830
2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경 -- endTime:1692335228118
2. 원하는 범위의 배열 생성 후, 인덱스로 접근해 값 변경 결과 -- duration:1.288, length:50000000, first:1, last:50000000
----------------------------------
3. Array.from 사용 -- startTime:1692335228118
3. Array.from 사용 -- endTime:1692335231499
3. Array.from 사용 결과 -- duration:3.381, length:50000000, first:1, last:50000000
----------------------------------
4. ArrayBuffer(Uint32Array) 사용 -- startTime:1692335231499
4. ArrayBuffer(Uint32Array) 사용 -- endTime:1692335231608
4. ArrayBuffer(Uint32Array) 사용 결과 -- duration:0.109, length:50000000, first:1, last:50000000
자바스크립트 완벽 가이드, 인사이트  문제 해결력을 높이는 알고리즘과 자료 구조:코딩 테스트 프로그래밍 경진대회 전 필독서!, 길벗
(위 링크는 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.)