[node.js] 문자열 이어붙이기 벤치마크 (Node.js String Concatenation Benchmark: Comparing string += vs. Buffer.concat())
node.js 2025. 2. 6. 15:41
node.js 의 http/https 서버에서 요청된 body buffer를 req.on('data') 에서 이어 붙일 때 문자열 += 연산으로 하는것과 buffer 배열에 append 한 뒤 마지막에 문자열 하나로 만드는 것 중 어느게 더 효율적인지 테스트 해 봄.
const { performance } = require('perf_hooks');
if (!global.gc) {
console.error("Run the script with '--expose-gc' option: node --expose-gc x.js");
process.exit(1);
}
// 설정값
const NUM_CHUNKS = 10000; // 생성할 버퍼 개수
const CHUNK_SIZE = 1024*10; // 각 버퍼 크기 (바이트)
const ITERATIONS = 100; // 반복 실행 횟수
// 랜덤 데이터 생성 함수
function createRandomBuffers(numChunks, chunkSize) {
const buffers = [];
for (let i = 0; i < numChunks; i++) {
buffers.push(Buffer.alloc(chunkSize, 'a')); // 'a'로 채운 버퍼
}
return buffers;
}
// 메모리 및 시간 측정 함수 (반복 실행 및 통계 계산)
function benchmark(fn, buffers, methodName) {
let times = [];
let memoryUsages = [];
// 초기 GC 실행 (메모리 측정 안정화)
global.gc();
let baselineMem = process.memoryUsage().heapUsed;
for (let i = 0; i < ITERATIONS; i++) {
global.gc(); // GC 실행하여 메모리 정확도 높임
const startMem = process.memoryUsage().heapUsed;
const startTime = performance.now();
let result = fn(buffers); // 벤치마크 실행
const endTime = performance.now();
global.gc(); // 최종 GC 실행 (메모리 측정)
const endMem = process.memoryUsage().heapUsed;
// 문자열 결과를 해제하여 영향 제거
result = null;
global.gc();
const memoryUsed = Math.max(0, (endMem - startMem) / 1024 / 1024);
const timeTaken = endTime - startTime;
times.push(timeTaken);
memoryUsages.push(memoryUsed);
//console.log(`[${methodName}] Iteration ${i + 1}: ${timeTaken.toFixed(2)}ms, ${memoryUsed.toFixed(2)}MB`);
}
// 통계 계산 함수
function calculateStats(values) {
const mean = values.reduce((a, b) => a + b, 0) / values.length;
const stdDev = Math.sqrt(
values.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b, 0) / values.length
);
return {
mean: mean.toFixed(2),
stdDev: stdDev.toFixed(2),
min: Math.min(...values).toFixed(2),
max: Math.max(...values).toFixed(2),
};
}
// 최종 결과 출력
console.log(`\n=== ${methodName} Results ===`);
console.log(`Execution Time (ms) → Mean: ${calculateStats(times).mean}, StdDev: ${calculateStats(times).stdDev}, Min: ${calculateStats(times).min}, Max: ${calculateStats(times).max}`);
console.log(`Memory Usage (MB) → Mean: ${calculateStats(memoryUsages).mean}, StdDev: ${calculateStats(memoryUsages).stdDev}, Min: ${calculateStats(memoryUsages).min}, Max: ${calculateStats(memoryUsages).max}`);
console.log('================================\n');
}
// 문자열 방식 (s += chunk.toString())
function benchmarkStringConcat(buffers) {
let s = '';
for (let buf of buffers) {
s += buf.toString();
}
return s;
}
// 버퍼 방식 (Buffer.concat())
function benchmarkBufferConcat(buffers) {
return Buffer.concat(buffers).toString();
}
// 실행
const buffers = createRandomBuffers(NUM_CHUNKS, CHUNK_SIZE);
benchmark(benchmarkBufferConcat, buffers, 'Buffer.concat()');
benchmark(benchmarkStringConcat, buffers, 'String Concatenation');
실행결과는 아래와 같음.
# 명시적 gc를 위해 --expose-gc 옵션
node --expose-gc benchmark.js
# 결과
=== Buffer.concat() Results ===
Execution Time (ms) → Mean: 27.19, StdDev: 3.17, Min: 24.88, Max: 55.72
Memory Usage (MB) → Mean: 97.65, StdDev: 0.07, Min: 96.98, Max: 97.67
================================
=== String Concatenation Results ===
Execution Time (ms) → Mean: 30.79, StdDev: 1.99, Min: 28.96, Max: 49.10
Memory Usage (MB) → Mean: 98.11, StdDev: 0.01, Min: 98.02, Max: 98.12
================================
미미한 차이긴 하나 Buffer.concat()이 약간 더 효율적임.
'node.js' 카테고리의 다른 글
[node.js] v17 이상에서 TLSv1.0 서버 https 요청시 에러 발생 (0) | 2024.12.11 |
---|---|
[node.js] WriteStream 사용시 fd close (0) | 2021.07.12 |
[node.js] http/https로 application/x-www-form-urlencoded POST로 request (0) | 2021.06.03 |
[node.js] MongoDB find/aggregate 대량 document 가져올 때 MongoNetworkError: cursor killed or timed out 발생시 (0) | 2020.05.27 |
[node.js] load average throttle (0) | 2019.09.26 |