서론
1년 전 Node.js contribution을 하던 중 array를 TypedArray로 변경하여 100% 이상의 성능 향상을 가져온 PR 두 개를 본 적이 있다. 이에 TypedArray와 ArrayBuffer에 대해 자세히 알아보고자 한다.
Preliminaries: ArrayBuffer와 일반 배열의 메모리 구조
TypedArray를 이해하기 전에, 먼저 ArrayBuffer에 대해 알아보자. ArrayBuffer는 JavaScript에서 고정 크기의 연속 메모리 블록을 나타내는 객체다. 이는 바이너리 데이터를 효율적으로 저장하고 계산하는 자료 구조로 TypedArray나 DataView 같은 view를 통해 접근할 수 있다. ArrayBuffer 자체는 단순한 메모리 버퍼로, 생성 시 크기가 고정되며 동적으로 변경할 수 없다.
예를 들어
const buffer = new ArrayBuffer(16); // 16바이트의 고정 메모리 할당
이렇게 생성된 버퍼는 OS 수준에서 연속된 메모리 영역을 차지하며, CPU 캐시와 연산 최적화에 유리하다.
Array가 JavaScript에서 어떻게 저장되는를 생각해보면 array도 Object의 일종으로(object를 prototype으로 참조한다) reference type이다. 이는 변수가 실제 값을 저장하는 게 아니라, 메모리 Heap에 저장된 값의 주소를 가리킨다는 의미다. 때문에 일반 배열은 동적이지만, 메모리 접근이 비연속적일 수 있고, 타입 체크나 가비지 컬렉션 오버헤드가 발생한다.
TypedArray
TypedArray는 ArrayBuffer 위에 구축된 고정 타입 배열로, 특정 데이터 타입(예: 정수, 부동소수점)을 연속 메모리에 저장한다. 주요 종류로는 Uint8Array(8비트 부호 없는 정수), Float32Array(32비트 부동소수점) 등이 있으며, 바이너리 데이터 처리(이미지, 오디오, 네트워크)나 수치 연산(그래픽스, ML)에 최적화되어 있다.
TypedArray의 특징
TypedArray는 일반 배열과 달리 다음 조건을 만족한다:
- 고정 타입과 크기: 각 요소의 타입이 생성 시 결정되며 (e.g., Float32Array는 32비트 float), 길이가 고정된다. 값 설정 시 타입 변환/클램핑이 자동으로 일어난다.
- 연속 메모리 구조: ArrayBuffer의 연속 영역에 저장되어 CPU 캐시 히트율이 높다. 요소 접근이 네이티브 수준으로 빠르다.
- 뷰 기반 접근: 여러 TypedArray가 같은 ArrayBuffer를 공유할 수 있다. 한 쪽 변경이 다른 쪽에 즉시 반영된다.
- 메서드 지원: 일반 배열처럼 forEach, map, set, subarray 등을 지원하지만, immutable 반환을 강조한다.
새로운 TypedArray를 만드는 조건은 간단하다: ArrayBuffer를 기반으로 특정 타입을 지정하면 된다. 혹은 원하는 바이트 수를 입력하면 내부적으로 그에 맞는 arraybuffer를 생성해준다.
const arr = new Int8Array(8); // 8개 요소, 모두 0으로 초기화
const buffer = new ArrayBuffer(8);
const arr2 = new Int8Array(buffer);
TypedArray의 특장점
TypedArray가 일반 배열(Number 배열)보다 우수한 이유는 다음과 같다:
- 성능 향상: 연속 메모리와 고정 타입으로 타입 체크/boxing overhead가 없어, 대규모 연산에서 10~100배 빠르다. 예: Node.js PR에서 array를 TypedArray로 바꿔 100%+ 속도 업.
- 메모리 효율: 요소당 지정된 비트(예: Float32Array는 4바이트)만 사용. 일반 배열은 8바이트 + 객체 오버헤드.
- 벡터화 연산 지원: SIMD 최적화로 여러 요소를 동시에 처리. WebGL, Web Audio 등에서 필수.
- Mutable 동작: 요소 변경 시 기존 메모리를 overwrite. 새 객체 생성 없이 효율적.
비교 표로 요약해보면 다음과 같다.
| Array | TypedArray | |
| 메모리 구조 | 비연속적, 힙에 흩어짐 | 연속적, 고정 버퍼 |
| 타입 | 동적 (Number: 64비트 double) | 고정 (e.g., 32비트 float) |
| 길이 | 동적 (push/pop 가능) | 고정 |
| 성능 | 느림 (타입 체크, GC overhead) | 빠름 (캐시 히트, JIT 최적화) |
| 용도 | 범용 데이터 | 바이너리/수치 데이터 |
참고자료
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Language_overview#data_types
https://v8.dev/blog/elements-kinds
'JS' 카테고리의 다른 글
| 함수의 호출과 참조 전달의 차이(feat: React use) (0) | 2026.01.08 |
|---|---|
| 웹 프론트엔드 성능 지표: RAIL, Core Web Vitals (0) | 2025.12.17 |
| 브라우저에게 양보하기 (0) | 2025.09.28 |
| [JS] Javascript에서의 Closures (0) | 2025.09.19 |
| debounce와 throttle의 차이점 (0) | 2025.05.29 |