# JSON.stringify를 두 배 이상 빠르게 만든 방법

> Clean Markdown view of GeekNews topic #22356. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=22356](https://news.hada.io/topic?id=22356)
- GeekNews Markdown: [https://news.hada.io/topic/22356.md](https://news.hada.io/topic/22356.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-08-05T20:33:45+09:00
- Updated: 2025-08-05T20:33:45+09:00
- Original source: [v8.dev](https://v8.dev/blog/json-stringify)
- Points: 10
- Comments: 1

## Summary

V8 엔진의 **JSON.stringify** 함수가 **2배 이상의 성능 향상**을 이루었습니다. **부작용 없는 객체**를 위한 Fast Path 도입과 **문자열 직렬화의 SIMD, 버퍼 분할** 최적화, 그리고 **Dragonbox 기반 숫자 변환**으로, 일반적인 데이터 직렬화와 API 응답 등 대부분의 **웹 환경에서 자동으로 속도 이점을 제공합니다**. 이 개선은 **V8 13.8(Chrome 138) 이상**부터 적용되며, 네트워크 처리 등 대량 데이터의 실시간 변환 상황에서 큰 영향을 줄 수 있습니다.

## Topic Body

- V8 엔진에서 **JSON.stringify** 함수의 성능을 두 배 이상 높여, 데이터 직렬화 속도 개선 효과를 얻음  
- **부작용 없는 객체**를 위한 최적화 경로를 도입해 다수의 방어적인 검사 로직을 생략함으로써 일반적인 데이터 객체에서 큰 속도 향상을 이룸  
- **문자열 처리** 시 1바이트/2바이트 구분, SIMD 활용, 임시 버퍼 구조 변경 등 하드웨어와 메모리 측면 전반에서 고도화된 방법을 적용  
- **숫자 변환 과정**에서는 기존 Grisu3 알고리듬을 Dragonbox로 교체하여 Number.toString() 호출 전반에서도 신속한 변환 가능성 확보  
- 일부 인자·형태에서는 일반 직렬화 경로로 돌아가지만, **대부분 웹 개발 상황**에서는 자동으로 최적화 효과를 누릴 수 있음  
  
---  
  
### 개요  
  
- `JSON.stringify`는 **자바스크립트에서 데이터를 문자열로 변환**하는 핵심 함수  
- 이 함수의 성능 향상은 네트워크 요청이나 localStorage 저장 등 웹에서 매우 중요한 작업들에도 긍정적 영향을 줌  
- 최신 V8 엔지니어링을 통해 이 기능의 속도가 **두 배 이상 개선**되었으며, 주요 최적화 방안을 상세히 소개함  
  
### 부작용 없는 Fast Path 경로  
  
- 최적화의 핵심은 **부작용(side-effect)이 없는 상황**에서만 사용할 수 있는 **빠른 직렬화 경로 적용**  
- 이런 상황에서는 재귀가 아니라 **반복적(iterative) 구조**로 객체 순회함으로써, 스택 오버플로우 검사가 필요 없고, 더 깊은 객체 직렬화 시도도 가능함  
- 데이터 객체가 단순할 때 V8은 느린 일반 로직 대신 이 Fast Path를 활용해 많은 검사를 생략하고 속도를 올림  
  
### 다양한 문자열 표현 처리  
  
- V8은 **1바이트/2바이트 문자(ASCII/비-ASCII)** 에 따라 문자열을 다르게 저장하며, 하나라도 비-ASCII가 있으면 전체가 2바이트로 관리됨  
- 문자열 직렬화 성능을 위해 **문자열 타입별로 별도의 알고리듬 버전**을 만들어 컴파일함  
- 처리 중에 문자열 인스턴스 타입을 확인해야 하므로, 2바이트 문자열이 감지되면 적합한 2바이트 직렬화기가 상태를 넘겨받음  
- 덕분에 **문자열 인코딩별 경로 전환 부하는 사실상 없음**  
- 결과는 1바이트, 2바이트 버퍼를 각각 만든 뒤 마지막에 단순히 병합함  
  
### SIMD로 문자열 직렬화 최적화  
  
- 자바스크립트 문자열에는 JSON 직렬화 시 이스케이프가 필요한 문자가 포함될 수 있음  
- 긴 문자열은 **SIMD 하드웨어 명령**(ARM64 Neon 등)으로 여러 바이트를 한 번에 검사함  
- 짧은 문자열은 **SWAR 방식**으로, 범용 레지스터에서 비트 연산을 통해 여러 문자를 동시에 처리함  
- 어떤 방식이든, 대부분의 경우 별다른 변환 없이 전체 문자열을 빠르게 복사 가능함  
  
### Express Lane(초고속 경로) 추가  
  
- Fast Path 내에서도 **프로퍼티 검사 등 반복적인 작업 없이 키 복사만으로 직렬화**가 가능하도록 Express Lane 마련함  
- 오브젝트의 **hidden class 플래그**를 활용해 키에 Symbol이 없고, 모두 enumerable 및 이스케이프 필요 없이 직렬화된 경우 'fast-json-iterable'로 마킹함  
- 동일한 hidden class를 갖는 다른 객체 직렬화 시 별도 검사 없이 **바로 키 복사 수행**  
- 이 기법은 `JSON.parse`에서도 빠른 키 비교에 응용  
  
### 더 빠른 double-to-string 알고리듬  
  
- 숫자를 문자열로 변환하는 과정도 높은 빈도와 복잡성을 가짐  
- **기존 Grisu3 알고리듬을 Dragonbox로 교체**해, `Number.prototype.toString()` 전체 호출에서도 성능향상 효과가 발생함  
  
### 임시 버퍼 구조 최적화  
  
- 문자열 빌드 시 기존에는 **단일 연속 버퍼**를 사용해, 공간이 부족할 때마다 전체 복사 작업이 발생하는 과부하가 있었음  
- 새로운 방법은 **분할(segmented) 버퍼 구조**로, 여러 소규모 버퍼를 필요에 따라 이어붙임  
- 덕분에 공간 부족 시 **전체 복사 필요 없이 새로운 버퍼 할당만** 진행됨  
  
### 한계  
  
- Fast Path는 단순한 데이터 직렬화에 한해 동작함  
- 아래 조건에 부합하지 않을 경우 일반 경로 사용  
  - **replacer** 혹은 **space** 인자 사용 불가(Pretty-Print, 변형 불가)  
  - **toJSON** 커스텀 메서드 없는 단순 객체여야 함  
  - **인덱스 기반 프로퍼티**가 있으면 느린 경로로 이동  
  - **ConsString 등 특수 문자열**은 처리하지 않음  
- 대부분 데이터 직렬화, API 응답 생성, 설정 캐싱 등 일반적인 용도엔 자동으로 최적화 효과가 적용됨  
  
### 결론  
  
- `JSON.stringify`의 **기본 설계부터 메모리 처리, 문자 처리까지** 전 영역에서 접근 방식을 재구성하여 JetStream2 벤치마크 기준 **2배 이상의 속도 상승**을 달성함  
- 해당 개선사항은 **V8 버전 13.8(Chrome 138) 이상**에서 바로 경험 가능함

## Comments



### Comment 42154

- Author: neo
- Created: 2025-08-05T20:33:46+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=44786005) 
* JSON 인코딩이 NodeJS에서 프로세스 간 통신에 큰 장애물임을 느낌
  - 대부분 결국엔 이벤트 루프 지연을 줄이고자 작업을 다른 스레드로 넘기려고 시도하지만, 메인 스레드의 CPU 부하는 결국 3배가 됨을 알게 됨
  - 배열을 하나씩 stringify하는 예시도 많이 봄, 내부적으로도 이런 방식이 적용되는 듯함
  - V8 팀이 이 부분을 더 강화해줬으면 하는 바람이 있음
  - 일부 데이터 셋에 대해 bail out 없이 처리할 수 있는지, 또는 CString 처리 문제는 어찌되는지 궁금함, faststr 기능이 부활하는지도 관심 있음
  * 작년에 Node 퍼포먼스 분석을 처음 했을 때, JSON.stringify가 Node 서비스에서 성능을 가로막는 가장 큰 요인 중 하나였음
    - 딕셔너리 키로 stringify를 써야 하고, apollo/express는 전체 응답을 한 번에 문자열로 직렬화해서 스트리밍하지 않음
    - JVM이나 Go에서 온 입장에서는 Node에서 이런 부분이 꽤 아마추어같이 느껴졌음
  * Python도 똑같은 문제를 가짐
    - 일반 패턴을 위한 높은 수준의 API 위에서 효율적인 IPC 프리미티브가 있으면 좋겠다고 생각함
  * JSON 인코딩이 통신에 큰 장애라는 의견에 공감함
    - 전 세계적으로 통신에서 JSON 처리로 인한 연산 오버헤드가 얼마나 큰지, bytes를 고정 포맷 등 더 파싱 효율적인 방식(예: ASN.1)으로 그냥 보내면 더 나은지 궁금함
  * V8팀이 이 부분을 더 적극적으로 강화하는 데에는 반대하며, 이 문제가 있는 개발자들이 다른 도구를 찾기를 추천함
    - Node/V8은 백엔드나 고성능 계산 문제에 그다지 적합하지 않다고 생각함
    - Javascript는 웹 사용에 맞춰져 오래 그럴 것이므로, V8팀이 이런 문제를 해결해 줄 필요는 없음
    - Typescript 팀도 Go로 전향했고, 언어 간 변환 자동화도 가능함
  * Worker로 작업을 오프로드해서 직렬화/역직렬화 시간보다 세이브한 시간이 많았던 경우는 거의 단 한 번뿐이었음
    - 데이터가 크면 크고 비싼 메시지 전달이 병렬화 이익과 맞먹게 됨

* 최근 10여 년간 부동소수점 숫자 직렬화 성능이 얼마나 향상됐는지 아주 놀라웠음  
  - https://github.com/jk-jeon/dragonbox?tab=readme-ov-file#performance
  * IEEE 부동소수점 값을 decimal UTF-8 문자열로 바꿨다 다시 역변환하는 과정은 느릴 뿐 아니라 매우 불안정함
    - 2진수와 10진수로 정확히 표현 가능한 값이 다르기에 미세한 오차가 생길 수 있음

* `JSON.stringify`에서 replacer나 space 인자가 있으면 fast path가 적용되지 않는다고 함
  - 그렇다면 `JSON.stringify(data, null, 0)`을 써도 fast path가 가능한지 아니면 인자가 undefined이어야만 되는지 궁금함

* SWAR escaping 알고리즘[1]이 Folly JSON에 구현했던 것과 아주 비슷함[2]
  - Folly는 4바이트가 아니라 8바이트 단위로 처리하고, escape가 필요한 첫 위치도 반환해 fast path에서 오버헤드를 최소화함
  - [1] https://source.chromium.org/chromium/_/chromium/v8/v8/+/5cbc55a855df103fa257fb0db1cc72db72706488:src/json/json-stringifier.cc;dlc=c0b1106da565c4baecb23dc06272caefc50b6760
  - [2] https://github.com/facebook/folly/commit/2f0cabfb48b8a8df84f6453ef055e03ee1a34d51

* 작업 자체 가치는 의심하지 않지만, 실제 V8 생태계에서 JSON.stringify가 런타임을 지배했던 구체적 문제나 데이터가 더 궁금함
  * 반드시 실행 시간 비중이 압도적일 필요는 없고, 매일 수억 페이지에서 호출되는 상황이니 전 세계적 전력 절감 효과는 상당할 것임

* v8의 성능이 충분히 칭찬받지 못한다고 생각함, 요즘 JS가 어마어마하게 빨라졌음
  * 정말 감탄스러움, “10억 달러면 아무 문제도 풀 수 있다”의 좋은 예라고 생각함
    - 앞으로 JS가 “strict”, “stricter”처럼 더 진화해서 컴파일/JIT에 쉽고 단순한 언어가 되면 좋겠음
  * 반면, v8은 너무 극한까지 최적화돼서 전 세계에 제대로 내부를 이해하는 사람이 100명 남짓이고, 대부분 개발자는 “왜 내 JS는 안 빠르지?”라는 마음일 것이라고 느낌

* 다른 생태계와 비교해서 이게 얼마나 뛰어난 것인지 궁금함
  - 10년 넘게 JSON 직렬화를 해왔지만 너무 빨라서 걱정해본 적이 거의 없음
  - simdjson은 코어당 초당 GB 처리 가능하며, 프리페칭/분기예측 등 최적화 감안하면 JSON 직렬화는 대부분 현실 workload에서 무시해도 될 수준이라 생각함
  - JSON의 가장 큰 단점은 IO 오버헤드임, serializer 속도가 아무리 빨라도 100MB blob을 매번 저장소에 저장해야 하면 무용지물임

* “No indexed properties on objects” — fast path는 일반적인 문자열 기반 키(key) 객체에만 최적화돼 있다고 하는데, array-like 인덱스 속성이 있으면 slow path로 돌아감
  - 이유가 무엇인지 궁금함
  * integer처럼 보이는 키가 있는 객체는 JSON 배열로 직렬화된다는 뜻일까? 설마 그런가…?

* segmented buffer 방식이 마음에 듦, 예전엔 fast-json-stringify 같은 userland 라이브러리로 rope 트릭을 직접 짰어야 했는데 이젠 네이티브라 훨씬 좋음
  - bailout 조건(예: replacer, space, 커스텀 .toJSON()) 많이 경험하는지 궁금함, 이런 경우 느린 경로로 바로 fallback되는 것인지 문의함

* V8은 탁월하지만, JS 자체 때문인지 LuaJIT이나 JVM보다 성능에서 부족함
  - JVM은 워밍업 시간이 길긴 하지만, 그럼에도 불구하고 JS보다는 나음
  * JS가 원인임, V8이 luajit과 JVM보다 훨씬 진보됐다고 생각함
    - Java는 실시간 제약이 적고(컴파일러가 있음) 그 점이 장점임
  * 많은 JS overhead는 dynamic 특성 때문임
    - asm.js는 객체의 shape 변경 같은 동적 동작을 금지해서 많은 체크를 스킵할 수 있었음
  * “JVM조차”라는 표현에 이의를 제기함, JVM은 최고 수준임
