[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์ต์ ํ ๊ธฐ๋ฒ ๋ชจ์
"Optimizing Javascript for fun and for profit"๋ผ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ต์ ํ ๊ด๋ จ ๊ธ์ ์์ด ์๋ฌธ๊ณผ ๋ฒ์ญ๋ณธ์ ๋ฐํ์ผ๋ก, ๋ถ๊ฐ์ ์ธ ์ค๋ช ์ ์ถ๊ฐํ๊ณ ์ดํดํ๊ธฐ ์ฌ์ด ์ธ์ด๋ก ์ ๋ฆฌํด ๋ดค๋ค. ์์ ์ฝ๋๋ ์กฐ๊ธ ๋ ๋ค๋ฌ์ด์ ๊ฐ์ ํ๋ค.
๋ฌธ์์ด ๋น๊ต ํผํ๊ธฐ Avoid string comparisons
๋ฌธ์์ด ๋น๊ต๋ ๊ฐ ๋ฌธ์๋ฅผ ์์ฐจ์ ์ผ๋ก ๋น๊ตํด์ผ ํ๋ฏ๋ก O(n) ์๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ๋๋ค. ํนํ ๋ฌธ์์ด enum์ ์ฌ์ฉํ๋ ๊ฒ์ ์ฑ๋ฅ ์ต์ ํ ๊ด์ ์์ ํผํด์ผ ํ ํจํด ์ค ํ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ ์์ง์์ ์ ์(Integer)๋ ์ผ๋ฐ์ ์ผ๋ก ๊ฐ์ผ๋ก ์ ๋ฌ๋๋ฉฐ, ๋น๊ต ์ฐ์ฐ ์ ๋ฐ๋ก ๊ฐ์ ํ์ธํ ์ ์์ด ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค. ๋ฐ๋ฉด ๋ฌธ์์ด์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋ ์์น(ํฌ์ธํฐ)๋ฅผ ์ฐธ์กฐํด ๊ฐ์ ๊ฐ์ ธ์จ ํ ๋น๊ตํ๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ์ก์ธ์ค๊ฐ ํ์ํ๊ณ ์ด๋ก ์ธํด ๋ ๋ง์ ๋น์ฉ์ด ๋ฐ์ํ๋ค.
// ๋ฌธ์์ด enum - ๋๋ฆผ
enum Position {
TOP = 'TOP',
BOTTOM = 'BOTTOM',
}
// ์ ์ํ enum - ๋น ๋ฆ
enum Position {
TOP, // = 0
BOTTOM, // = 1
}
๋ค๋ฅธ ํํ ํผํ๊ธฐ Avoid different shapes
ํจ์ ํ๋ผ๋ฏธํฐ๋ก ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ ๋, ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ ํด๋น ํจ์๊ฐ ํญ์ ๋์ผํ ํํ(Shape, Hidden Class)๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ์ ๋ฌ๋ฐ๋๋ค๊ณ ๊ฐ์ ํ๊ณ ์ต์ ํ๋ฅผ ์๋ํ๋ค. ๊ฐ์ฒด์ ์์ฑ ๊ฐ์, ์์, ๋ฐ์ดํฐ ํ์ ๋ฑ์ด ์ผ์ ํ๊ฒ ์ ์ง๋๋ฉด ์์ง์ด ์ต์ ํ๋ ๋จธ์ ์ฝ๋๋ฅผ ์์ฑํ์ฌ ์ฑ๋ฅ์ ๊ทน๋ํํ ์ ์๋ค.
๋ฐ๋ฉด, ๋ค๋ฅธ ํํ์ ๊ฐ์ฒด(์: ์์ฑ์ ๊ฐ์๋ ์์๊ฐ ๋ค๋ฅด๊ฑฐ๋ ๋ฐ์ดํฐ ํ์ ์ด ๋ค๋ฅธ ๊ฒฝ์ฐ)๋ฅผ ์ ๋ฌํ๋ฉด, ์์ง์ ๊ธฐ์กด ์ต์ ํ๋ฅผ ํด์ ํ๊ฑฐ๋ ์๋ก์ด ์ต์ ํ ์ ๋ต์ ์๋ฆฝํด์ผ ํ๋ค. ์ด๋ก ์ธํด ์ฑ๋ฅ ์ ํ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๋ค.
์๋ฅผ ๋ค์ด ๋ฐํ์์ ์๋ ํจ์๊ฐ { x: number, y: number }
ํํ์ ๋ ๊ฐ์ฒด๋ฅผ ์ ๋ฌ๋ฐ๋๋ค๊ณ ๊ฐ์ ํด ๋ณด์. ์์ง์ ์ด ํจ์๊ฐ ์์ผ๋ก๋ ๋์ผํ ํํ์ ๊ฐ์ฒด๋ฅผ ๋ฐ์ ๊ฒ์ด๋ผ๊ณ ์ถ์ธกํ๊ณ , ํด๋น ํํ์ ๋ํด ์ต์ ํ๋ ๋จธ์ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
function add(a, b) {
return {
x: a.x + b.x,
y: a.y + b.y,
};
}
const point1 = { x: 1, y: 2 };
const point2 = { x: 3, y: 4 };
console.log(add(point1, point2)); // { x: 4, y: 6 }
์ ํจ์๊ฐ ์ผ๊ด๋ ํํ์ ๊ฐ์ฒด๋ง ์ฒ๋ฆฌํ๋ค๋ฉด ์ต์ ํ๊ฐ ์ ์ง๋๋ค. ํ์ง๋ง ์๋์ฒ๋ผ ๋ค๋ฅธ ํํ์ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ฉด, ์์ง์ ์ต์ ํ๋ฅผ ํด์ ํ๊ฑฐ๋ ๋ค์ ์ค์ ํด์ผ ํ๋ฏ๋ก ์ฑ๋ฅ ์ ํ๊ฐ ๋ฐ์ํ ์ ์๋ค.
const point3 = { x: 1, y: 2, z: 5 }; // ์ถ๊ฐ ์์ฑ z ํฌํจ
console.log(add(point1, point3)); // ์ต์ ํ ํด์ ๊ฐ๋ฅ
๋ํ, ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ props๋ฅผ ๋ค๋ฅธ ์์๋ ๊ตฌ์กฐ๋ก ์์ฑํ๋ ๊ฒฝ์ฐ์๋ ์ ์ฌํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ๋ฐ๋ผ์, ๊ฐ์ฒด์ ํํ๋ฅผ ์ผ๊ด๋๊ฒ ์ ์งํ๋ ๊ฒ์ด ์ค์ํ๋ค.
๋ฐฐ์ด/๊ฐ์ฒด ๋ฉ์๋ ํผํ๊ธฐ Avoid array/object methods
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ณ ๊ฐ๋ ์ฑ ์๊ฒ ์์ฑํ ์ ์๋ ์ฅ์ ์ด ์๋ค. ๊ทธ๋ฌ๋ Haskell, OCaml, Rust ๋ฑ ํจ์ํ ์ฝ๋๋ฅผ ํจ์จ์ ์ผ๋ก ์ปดํ์ผํ๋ ์ธ์ด๊ฐ ์๋ ์ด์ ๋ช ๋ นํ ์ฝ๋์ ๋นํด ์ฑ๋ฅ์ด ์ ํ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
์๋ ์์์์ ํจ์ํ ๋ฉ์๋๋ ๊ฐ ๋จ๊ณ์์ ๋ฐฐ์ด์ ๋ณต์ฌ๋ณธ์ ์์ฑํ๋๋ฐ, ์ด ๋ณต์ฌ๋ณธ์ ๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์ํด ์ฒ๋ฆฌ๋์ด์ผ ํ๋ฏ๋ก ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ๋ค. ๋ํ ๊ฐ ๋ฉ์๋ ํธ์ถ๋ง๋ค ๋ฐฐ์ด์ ๋ฐ๋ณต ์ฒ๋ฆฌ(N๋ฒ์ ์ฐ์ฐ์ ๋ํด N๋ฒ ๋ฐ๋ณต) ํด์ผ ํ๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
const result = [1.5, 3.5, 5.0]
.map((n) => Math.round(n))
.filter((n) => n % 2 === 0)
.reduce((a, n) => a + n, 0);
๋ฐ๋ฉด, for ๋ฃจํ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐฐ์ด์ ํ ๋ฒ๋ง ์ํํ๋ฉด์ ๋์ผํ ์์ ์ ์ํํ ์ ์๊ณ , ๋ถํ์ํ ๋ฐฐ์ด ๋ณต์ฌ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ ์ต์ํํ ์ ์๋ค.
let result = 0;
const numbers = [1.5, 3.5, 5.0];
for (let i = 0; i < numbers.length; i++) {
const rounded = Math.round(numbers[i]);
if (rounded % 2 === 0) result += rounded;
}
Object.values()
, Object.keys()
, Object.entries()
์ ๊ฐ์ ๊ฐ์ฒด ๋ฉ์๋๋ ๋ด๋ถ์ ์ผ๋ก ์ถ๊ฐ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ํ ๋น๊ณผ ๋ฐ์ดํฐ ์ ๊ทผ ์ฐ์ฐ์ ์๊ตฌํ๋ฏ๋ก ์ฑ๋ฅ์ ๋ถ์ ์ ์ธ ์ํฅ์ ์ค ์ ์๋ค.
๊ฐ์ ์ฐธ์กฐ ๋ฐฉ์ง Avoid indirection
๊ฐ์ ์ฐธ์กฐ๋ ๊ฐ์ ์ง์ ์ ๊ทผํ์ง ์๊ณ ์ค๊ฐ ๋งค๊ฐ์ฒด(๊ฐ์ฒด, ๋ฐฐ์ด ๋ฑ)๋ฅผ ํตํด ์ ๊ทผํ๋ ๋ฐฉ์์ ์๋ฏธํ๋ค. ๋ฐ๋๋ก ์ง์ ์ฐธ์กฐ๋ ๋ฉ๋ชจ๋ฆฌ์์ ๊ฐ์ ๋ฐ๋ก ๊ฐ์ ธ์ค๋ ๋ฐฉ์์ด๋ค. ๊ฐ์ ์ฐธ์กฐ๋ ์ ์ฐ์ฑ์ ์ ๊ณตํ์ง๋ง ๊ฐ์ฒด๋ฅผ ํ์ํ๊ณ ์์ฑ์ ์กฐํํ๋ ๊ณผ์ ์์ ์ถ๊ฐ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ๊ณผ ์ฐ์ฐ์ด ๋ฐ์ํ๋ค. ์ด๋ก ์ธํด ๋ฃจํ๋ ๋ฐ๋ณต ํธ์ถ์ด ๋ง์ ์ฝ๋์์ ์ฑ๋ฅ ์ ํ๋ฅผ ์ผ๊ธฐํ ์ ์๋ค.
์๋ ์ฝ๋์์ config.maxRetries
๋ config
๊ฐ์ฒด๋ฅผ ํตํด ๊ฐ์ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ ๊ฐ์ ์ฐธ์กฐ ๋ฐฉ์์ด๋ค. ์ด ๊ณผ์ ์์ ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ config
๊ฐ์ฒด๋ฅผ ๋จผ์ ํ์ํ ํ maxRetries
์์ฑ์ ์กฐํํ๋ค.
const config = { maxRetries: 5 };
function retryOperation() {
// config๋ฅผ ํตํด ๊ฐ์ ์ ์ผ๋ก ์ ๊ทผ
for (let i = 0; i < config.maxRetries; i++) {
console.log(`Retry #${i + 1}`);
}
}
์์ฒ๋ผ ๋ฐ๋ณต์ ์ผ๋ก ๊ฐ(maxRetries)์ ์ฐธ์กฐํ๋ ๊ฒฝ์ฐ, ํด๋น ๊ฐ์ ์ง์ญ ๋ณ์๋ก ์ ์ฅํ์ฌ ์ง์ ์ฐธ์กฐ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝํ๋ฉด ๋ถํ์ํ ๊ฐ์ฒด ํ์ ๊ณผ์ ์ ์ ๊ฑฐํ ์ ์๋ค.
const config = {
maxRetries: 5,
};
function retryOperation() {
const maxRetries = config.maxRetries; // ์ง์ญ ๋ณ์๋ก ๊ฐ์ ์ง์ ์ฐธ์กฐ
for (let i = 0; i < maxRetries; i++) {
console.log(`Retry #${i + 1}`);
}
}
๊ฐ์ ์ฐธ์กฐ๋ก ์ธํ ์ฑ๋ฅ ์ ํ๋ ์ค์ฒฉ ๊ฐ์ฒด๋ฅผ ๋ค๋ฃฐ ๋ ๋์ฑ ๋๋๋ฌ์ง๋ค. ์๋ ์ฝ๋์์ app ๊ฐ์ฒด์ ์๋ theme ๊ฐ์ ์ป๊ธฐ ์ํด ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ํ์ํ๊ณ ์๋ค.
const app = {
settings: {
ui: {
theme: 'dark',
},
},
};
function applyTheme() {
console.log(app.settings.ui.theme); // ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ ๊ฐ์ ์ฐธ์กฐ
}
์ด ๊ฒฝ์ฐ, ์ค์ฒฉ๋ ๊ฐ(theme)์ ์ง์ญ ๋ณ์๋ก ์ ์ฅํ๋ฉด ํ์ ๋น์ฉ์ ์ค์ผ ์ ์๊ณ ์ฝ๋ ๊ฐ๋ ์ฑ๋ ํฅ์๋๋ค.
const app = {
settings: {
ui: {
theme: 'dark',
},
},
};
function applyTheme() {
const theme = app.settings.ui.theme; // ์ค์ฒฉ๋ ๊ฐ์ ์ง์ญ ๋ณ์์ ์ ์ฅ
console.log(theme); // ์ง์ญ ๋ณ์๋ฅผ ์ง์ ์ฐธ์กฐ
}
์บ์ ๋๋ฝ ๋ฐฉ์งํ๊ธฐ Avoid cache misses
CPU๋ ๋ฐ์ดํฐ๋ฅผ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์บ์๋ผ๋ ๊ณ ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ์ฌ์ฉํ๋ค. ์บ์๋ RAM๋ณด๋ค ๋น ๋ฅด์ง๋ง ์ฉ๋์ด ์ ํ์ ์ด๋ค. ๋ง์ฝ CPU๊ฐ ์์ฒญํ ๋ฐ์ดํฐ๊ฐ ์บ์์ ์์ผ๋ฉด, ๋ ๋๋ฆฐ RAM์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ ์บ์ ๋๋ฝ(Cache Miss) ํ์์ด ๋ฐ์ํ๋ฉฐ ์ด๋ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง ์ ์๋ค.
์บ์ ๋๋ฝ์ ์ค์ด๊ณ CPU ์ฑ๋ฅ์ ๊ทน๋ํํ๊ธฐ ์ํด ๋ฐ์ดํฐ ์ ๊ทผ ๋ฐฉ์์ ์ต์ ํํ๊ฑฐ๋ ํ๋ฆฌํ์นญ(Prefetching) ๊ฐ์ ๊ธฐ์ ์ด ํ์ฉ๋๋ค.
ํ๋ฆฌํ์นญ Prefetching
ํ๋ฆฌํ์นญ์ CPU๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ค๋ ์ ๋ต์ผ๋ก, ๋ฐ์ดํฐ ์ ๊ทผ ํจํด์ด ์์ธก ๊ฐ๋ฅํ ๋ ํจ๊ณผ์ ์ผ๋ก ์๋ํ๋ค. ์๋ฅผ ๋ค์ด, CPU๊ฐ 1๋ฒ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ์ ๋, “2๋ฒ ๋ฐ์ดํฐ๋ ๊ณง ํ์ํ ๊ฒ”์ด๋ผ๊ณ ์์ํ๊ณ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ค๋ ๋ฐฉ์์ด๋ค. ์ด ๋ฐฉ์์ ์บ์ ๋๋ฝ์ ์ค์ผ ์ ์๊ณ , ํนํ ๋ฐ์ดํฐ๋ฅผ ์์ฐจ์ ์ผ๋ก ์ ๊ทผํ ๋ ๋ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
const K = 1024;
const length = 1 * K * K;
// length ๊ธธ์ด์ ๋ฐฐ์ด์ ์์ฑํ๊ณ ๊ฐ ์์์ ๊ฐ์ ์์ฐจ์ ์ผ๋ก ์ด๊ธฐํ
const points = new Array(length);
for (let i = 0; i < points.length; i++) {
points[i] = { x: 42, y: 0 };
}
// ๋ฐฐ์ด์ ๋ณต์ฌํ ๋ค ์์ ์์๋ฅผ ๋ฌด์์๋ก ์์ (์
ํ๋ ๋ฐฐ์ด ์์ฑ)
const shuffledPoints = shuffle(points.slice());
// ์์ฐจ์ ์ ๊ทผ: ๋ฐฐ์ด์ ์ฒ์๋ถํฐ ๋๊น์ง ์ฝ์ผ๋ฉด์ x๊ฐ ํฉ์ฐ (๋น ๋ฆ / 100%)
let sequentialSum = 0;
for (let i = 0; i < points.length; i++) {
sequentialSum += points[i].x;
}
// ๋ฌด์์ ์ ๊ทผ: ์
ํ๋ ๋ฐฐ์ด์ ์์๋ฅผ ๋๋คํ ์์๋ก ์ฝ์ผ๋ฉด์ x๊ฐ ํฉ์ฐ (๋๋ฆผ / 26%)
let randomSum = 0;
for (let i = 0; i < shuffledPoints.length; i++) {
randomSum += shuffledPoints[i].x;
}
์ ์ฝ๋์์ ์์ฐจ์ ์ ๊ทผ์ ์บ์๋ฅผ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ๋ฏ๋ก ๋น ๋ฅด๊ฒ ์คํ๋๋ค. ๋ฐ๋ฉด, ๋ฌด์์ ์ ๊ทผ์ CPU๊ฐ ๋ฐ์ดํฐ๋ฅผ ์์ธกํ์ง ๋ชปํด ์บ์ ๋๋ฝ์ด ๋ ์์ฃผ ๋ฐ์ํด์ ์๋์ ์ผ๋ก ๋๋ฆฌ๋ค.
L1/L2/L3 ์บ์ฑ
์บ์๋ ์๋์ ํฌ๊ธฐ์ ๋ฐ๋ผ ๊ณ์ธตํ๋์ด ์๋ค. ๋์๊ด์ผ๋ก ๋น์ ํด ๋ณด๋ฉด, ์์ฃผ ์ฐธ๊ณ ํ๋ ์ฑ ์ ์ฑ ์ ์์(L1 ์บ์), ๋ ์ฐธ๊ณ ํ๋ ์ฑ ์ ์๊ฐ์(L2/L3 ์บ์), ๊ทธ๋ฆฌ๊ณ ๋๋จธ์ง๋ ์ฐฝ๊ณ ์(RAM) ๋ณด๊ดํ๋ค.
- L1 ์บ์: ๊ฐ์ฅ ์๊ณ ๋น ๋ฅด๋ฉฐ CPU์ ๊ฐ์ฅ ๊ฐ๊น์ด ์บ์
- L2 ์บ์: L1๋ณด๋ค ํฌ๊ณ ๋๋ฆฌ์ง๋ง ์ฌ์ ํ ๋น ๋ฆ
- L3 ์บ์: L2๋ณด๋ค ํฌ์ง๋ง ์๋์ ์ผ๋ก ๋๋ฆผ
- RAM: ์บ์๋ณด๋ค ํจ์ฌ ํฌ๊ณ ๋๋ฆผ
์บ์๋ ์์ฃผ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ์ฌ CPU๊ฐ ๋น ๋ฅด๊ฒ ์ ๊ทผํ ์ ์๋๋ก ๋๋๋ค. ํ์ง๋ง ๋ฐ์ดํฐ๊ฐ ์บ์ ํฌ๊ธฐ๋ฅผ ์ด๊ณผํ๋ฉด RAM์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ฏ๋ก ์ฑ๋ฅ ์ ํ๊ฐ ๋ฐ์ํ๋ค.
๐ Int8Array๋ ์๋ฐ์คํฌ๋ฆฝํธ์ TypedArray ์ค ํ๋๋ก 8๋นํธ ์ ์ํ ๋ฐฐ์ด์ ๋ํ๋ธ๋ค. TypedArray๋ ์ด์ง ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๋ค๋ฃจ๊ธฐ ์ํด ์ค๊ณ๋ ๋ฐฐ์ด๋ก, ์ผ๋ฐ ๋ฐฐ์ด๊ณผ ๋ฌ๋ฆฌ ๊ณ ์ ๋ ํฌ๊ธฐ์ ๋จ์ผ ๋ฐ์ดํฐ ํ์ ๋ง์ ํ์ฉํ๋ค. ์ด๋ฌํ ํน์ฑ ๋๋ถ์ TypedArray๋ ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ์ฌ์ฉํ์ฌ ์บ์ ํจ์จ์ฑ, ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ์๋, ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ธก๋ฉด์์ ๋ฐ์ด๋ ์ฑ๋ฅ์ ์ ๊ณตํ๋ค.
Int8Array์ ๊ฐ ์์๋ 8๋นํธ ์ ์๊ฐ(-128~127)๋ง ์ ์ฅํ ์ ์๋ค. 8๋นํธ๋ ๋ง ๊ทธ๋๋ก 8๊ฐ์ ๋นํธ๋ก ๊ตฌ์ฑ๋๋ฉฐ, ๊ฐ์ฅ ์ผ์ชฝ ๋นํธ๋ ๋ถํธ ๋นํธ๋ก ์ฌ์ฉ๋๋ค. ๋ถํธ ๋นํธ๋ ์ซ์์ ์์(0) ๋๋ ์์(1) ์ฌ๋ถ๋ฅผ ๋ํ๋ธ๋ค.
- ์์(2์ ๋ณด์ ๋ฐฉ์): 11111111(-1)๋ถํฐ 10000000(-128)
- ์์: 00000000(0)๋ถํฐ 01111111(+127)
์๋๋ ์บ์์ RAM ๊ฐ์ ์ ๊ทผ ์๋ ์ฐจ์ด๋ฅผ ์๋ฎฌ๋ ์ด์ ํ ์ฝ๋. L1 ์บ์ ์ ๊ทผ์ด ๊ฐ์ฅ ๋น ๋ฅด๊ณ , ๊ณ์ธต์ด ๋ด๋ ค๊ฐ์๋ก(L2, L3, RAM) ์ ๊ทผ ์๋๊ฐ ์ ์ ๋๋ ค์ง๋ค.
// ๊ธฐ๋ณธ ๋จ์ ์ ์ (๋ฐ์ดํธ ๋จ์)
const KB = 1024 // 1ํฌ๋ก๋ฐ์ดํธ = 1024๋ฐ์ดํธ
const MB = 1024 * KB // 1๋ฉ๊ฐ๋ฐ์ดํธ = 1024ํฌ๋ก๋ฐ์ดํธ
// ๋๋ต ์ ์ธ ์บ์ ํฌ๊ธฐ์ RAM ํฌ๊ธฐ ์ ์ (๋ฐ์ดํธ ๋จ์)
const L1 = 256 * KB // L1 ์บ์ ํฌ๊ธฐ
const L2 = 5 * MB // L2 ์บ์ ํฌ๊ธฐ
const L3 = 18 * MB // L3 ์บ์ ํฌ๊ธฐ
const RAM = 32 * MB // RAM ํฌ๊ธฐ
// RAM ํฌ๊ธฐ์ ๋ง๋ ๋ฐฐ์ด ์์ฑ ๋ฐ ์ด๊ธฐํ
const buffer = new Int8Array(RAM);
buffer.fill(42);
const random = (max) => Math.floor(Math.random() * max)
// L1 ์บ์ ์ ๊ทผ (๊ฐ์ฅ ๋น ๋ฆ / 100%, ์์ ํฌ๊ธฐ)
let r = 0;
for (let i = 0; i < 100000; i++) { r += buffer[random(L1)] }
// L2 ์บ์ ์ ๊ทผ (L1๋ณด๋ค ๋๋ฆผ / 78%, ๋ ํฐ ํฌ๊ธฐ)
let r = 0;
for (let i = 0; i < 100000; i++) { r += buffer[random(L2)] }
// L3 ์บ์ ์ ๊ทผ (L2๋ณด๋ค ๋๋ฆผ / 65%, ๋ ํฐ ํฌ๊ธฐ)
let r = 0;
for (let i = 0; i < 100000; i++) { r += buffer[random(L3)] }
// RAM ์ ๊ทผ (๊ฐ์ฅ ๋๋ฆผ / 29%, ๊ฐ์ฅ ํฐ ํฌ๊ธฐ)
let r = 0;
for (let i = 0; i < 100000; i++) { r += buffer[random(RAM)] }
๊ฒฐ๊ตญ ์บ์๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๊ณ ์บ์ ๋๋ฝ์ ๋ฐฉ์งํ๋ ค๋ฉด ๋ฐ์ดํฐ๋ฅผ ์์ฐจ์ ์ผ๋ก ์ ๊ทผํ ์ ์๋ ๋ฐฉ์์ผ๋ก ์ค๊ณํ๊ณ , ๋ฐ์ดํฐ ํฌ๊ธฐ๋ฅผ ์บ์ ํฌ๊ธฐ์ ๋ง๊ฒ ์กฐ์ ํ๋ ๊ฒ์ด ์ค์ํ๋ค.
ํฐ ๊ฐ์ฒด ํผํ๊ธฐ Avoid large objects
์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ ๊ฐ์ฒด์ ํํ(Shape)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ต์ ํ๋ฅผ ์ํํ๋ค. ํ์ง๋ง ๊ฐ์ฒด ์์ฑ ๊ฐ์๊ฐ ์ง๋์น๊ฒ ๋ง์์ง๊ฑฐ๋ ๊ตฌ์กฐ๊ฐ ๋ณต์กํด์ง๋ฉด, ์์ง์ ์ด๋ฌํ ์ต์ ํ๋ฅผ ์ ์งํ์ง ๋ชปํ๊ณ ์ผ๋ฐ์ ์ธ ํด์๋งต(Map) ํํ๋ก ์ ํํ๋ค.
์ด ๊ฒฝ์ฐ, ์บ์ ๋๋ฝ(Cache Miss) ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๋ค. ํด์๋งต ๊ธฐ๋ฐ ๋ฐ์ดํฐ ์ ๊ทผ์ ๋ฉ๋ชจ๋ฆฌ ์์์ ๋ฐ์ดํฐ๋ฅผ ๋ฌด์์๋ก ๋ถ์ฐ ์ ์ฅํ๋ฏ๋ก ์บ์ ์ ์ค๋ฅ (Cache Hit)์ด ๋ฎ์์ง๊ณ , ์ด๋ ๊ณง ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง ์ ์๋ค.
๐ ์บ์ ์ ์ค๋ฅ : ํ์ํ ๋ฐ์ดํฐ๊ฐ ์บ์์ ์ด๋ฏธ ์ ์ฅ๋์ด ์์ด ๋น ๋ฅด๊ฒ ์ ๊ทผํ ์ ์๋ ๋น์จ์ ๋ํ๋. ์ ์ค๋ฅ ์ด ๋์์๋ก ๋ฐ์ดํฐ ์ ๊ทผ ์๋๊ฐ ๋น ๋ฅด๋ค๊ณ ๋ณผ ์ ์์.
์๋ ์ฝ๋์์ Object.keys
๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ ํค ๋ฐฐ์ด์ ์์ฑํ ๋ค ๋ค์ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋ฏ๋ก ์ถ๊ฐ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ํ ๋น๊ณผ ๋ฐ๋ณต ์์
์ด ํ์ํ๋ค. ๋ฐ๋ฉด Object.values
๋ ๊ฐ์ฒด์ ๊ฐ ๋ฐฐ์ด์ ๋ฐ๋ก ๋ฐํํ์ฌ ๋ฐฐ์ด ์์ฑ์ ์ค๋ฒํค๋๊ฐ ์ ์ด ์๋์ ์ผ๋ก ๋ ํจ์จ์ ์ด๋ค.
// ์ฌ์ฉ์ ๋ฐ์ดํฐ ์ค์
const USERS_LENGTH = 1_000;
const byId = {};
Array.from({ length: USERS_LENGTH }).forEach((_, id) => {
byId[id] = { id, name: "John" };
});
// ๋ฐ์ดํฐ ์ ๊ทผ
let _ = 0;
// 1. key๋ฅผ ์ฌ์ฉํ ๊ฐ์ ์ ๊ทผ (๋๋ฆผ / 43%)
Object.keys(byId).forEach((id) => {
_ += byId[id].id;
});
// 2. value๋ฅผ ์ฌ์ฉํ ์ง์ ์ ๊ทผ (๋น ๋ฆ / 100%)
Object.values(byId).forEach((user) => {
_ += user.id;
});
๊ฐ์ฒด ํฌ๊ธฐ๋ฅผ 100,000์ผ๋ก ๋๋ฆฌ๋ฉด ์ด๋ฌํ ์ฑ๋ฅ ์ฐจ์ด๋ ๋์ฑ ๋๋๋ฌ์ง๋ค. ๋ฐ๋ผ์ ํฐ ๊ฐ์ฒด๋ฅผ ์์ฃผ ์ธ๋ฑ์ฑํ๋ ๋์ , ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐฐ์ด๋ก ๋ณํํด ๋๋ ๊ฒ์ด ์ฑ๋ฅ ๊ฐ์ ์ ์ ๋ฆฌํ๋ค.
// ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐฐ์ด๋ก ๋ณํ
const users = Array.from({ length: USERS_LENGTH }, (_, id) => ({
id,
name: "John",
}));
// ๋ฐ์ดํฐ ์ ๊ทผ
let _ = 0;
users.forEach((user) => {
_ += user.id;
});
์์ฒ๋ผ ๋ฐ์ดํฐ๋ฅผ ํน์ ID๋ฅผ ๊ธฐ์ค์ผ๋ก ์์ฃผ ์ฐธ์กฐํด์ผ ํ๋ ๊ฒฝ์ฐ, ๋์ผ์ฑ(ID)์ ๊ฐ์ง๋๋ก ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์ฑํ๋ฉด, ๋ณ๋์ ํค๋งต์ ์ฐธ์กฐํ์ง ์๊ณ ๋ Object.values
๋ฅผ ํ์ฉํ ์ ์๋ ์ฅ์ ์ด ์๋ค.
๋์ผ์ฑ์ด๋ ๋ฐ์ดํฐ์์ ๊ฐ ํญ๋ชฉ์ ๊ณ ์ ํ๊ฒ ์๋ณํ ์ ์๋ ID๋ฅผ ํ์ฉํด ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ฑฐ๋ ์กฐ์ํ๋ ๋ฐฉ์์ ์๋ฏธํ๋ค. ๋์ผ์ฑ์ ๋ณด์ฅํ๋ฉด ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ๋ฉด์ ํ์ ๋น์ฉ์ ์ต์ํํ ์ ์๋ค.
// ๋์ผ์ฑ์ ๋ณด์ฅํ๋ ์
const users = [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" },
];
๋ฌธ์์ด ์ ์คํ๊ฒ ์ฌ์ฉํ๊ธฐ Use strings carefully
๋ฌธ์์ด์ ๋จ์ํด ๋ณด์ด์ง๋ง ๋ฐ์ดํฐ๊ฐ ํด์๋ก ๋ฐ์ดํฐ ๋ณต์ฌ๋ก ์ธํด ์ฑ๋ฅ์ ํฐ ์ํฅ์ ์ค ์ ์๋ค. ํ๋์ ์ธ ์์ง์ ์ด๋ฅผ ์ต์ ํํ๊ธฐ ์ํด ์ฐธ์กฐ ๊ธฐ๋ฐ์ ๋ฌธ์์ด ์ฐ๊ฒฐ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
+
์ฐ์ฐ์๋ฅผ ์ด์ฉํด ๋ฌธ์์ด์ ์ฐ๊ฒฐํ ๋ ๋ฐ์ดํฐ ๋ณต์ฌ๊ฐ ์ด๋ฃจ์ด์ง๋ค๊ณ ์๊ฐํ ์ ์์ง๋ง, ์ค์ ๋ก๋ ๋ณต์ฌ ๋์ ์ฐธ์กฐ(ํฌ์ธํฐ)๋ฅผ ํ์ฉํด ๊ฐ ๋ฌธ์์ด ์์๋ฅผ ๊ฐ๋ฆฌํจ๋ค. ์ด๋ ๋ถํ์ํ ๋ฐ์ดํฐ ๋ณต์ฌ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ์ง์ฐ ํ๊ฐ ์ ๋ต์ด๋ค.
์๋๋ ํ์ ์คํฌ๋ฆฝํธ๋ก ์ด ๋์์ ๋ชจ๋ธ๋งํ ์์.
abstract class CustomString {
abstract value(): string[];
}
class BytesString extends CustomString {
constructor(private bytes: string[]) {
super();
}
value() {
return this.bytes;
}
}
class ConcatenatedString extends CustomString {
constructor(
private left: CustomString,
private right: CustomString,
) {
super();
}
value(): string[] {
return [...this.left.value(), ...this.right.value()];
}
}
function concat(left: CustomString, right: CustomString): ConcatenatedString {
return new ConcatenatedString(left, right);
}
const first = new BytesString(['H', 'e', 'l', 'l', 'o', ' ']);
const second = new BytesString(['w', 'o', 'r', 'l', 'd']);
// ๋ ๋ฌธ์์ด์ ์ฐ๊ฒฐํ์ง๋ง, ๋ฐ์ดํฐ ๋ณต์ฌ๋ ์ด๋ฃจ์ด์ง์ง ์์
const message = concat(first, second);
// ์ฐ๊ฒฐ๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ๋๋ง(value() ํธ์ถ) ๋ฌธ์ ๋ฐฐ์ด ์์ฑ
console.log(message.value()); // ['H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
์์ฒ๋ผ ์ฐธ์กฐ ๊ธฐ๋ฐ ์ค๊ณ๋ ๋ฐ์ดํฐ๊ฐ ์ค์ ๋ก ํ์ํด์ง๋ ์์ ์๋ง ํ๊ฐ๋ฅผ ์ํํ๋ฏ๋ก CPU์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ๋ค. ์ฌ๋ผ์ด์ค ์ฐ์ฐ ์ญ์ ์๋ณธ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ์ง ์๊ณ ํน์ ๋ฒ์๋ฅผ ์ฐธ์กฐ๋ฅผ ์์ฑํ๋ค. ์ค์ ๋ฐ์ดํฐ๋ ํ์ํด์ง๋ ์๊ฐ์๋ง ํ๊ฐ๋๋ค.
class SlicedString extends CustomString {
constructor(
private source: CustomString,
private start: number,
private end: number,
) {
super();
}
value(): string[] {
return this.source.value().slice(this.start, this.end);
}
}
function substring(source: CustomString, start: number, end: number): SlicedString {
return new SlicedString(source, start, end);
}
// 0~2 ์ธ๋ฑ์ค ๋ฒ์๋ฅผ ๊ฐ๋ฆฌํค๋ ์ฐธ์กฐ ์์ฑ
const firstTwoLetters = substring(message, 0, 2);
// ์ฐธ์กฐ๋ ๋ฒ์๋ฅผ ํ๊ฐํ์ฌ ์ค์ ๋ฌธ์ ๋ฐฐ์ด ๋ฐํ
console.log(firstTwoLetters.value()); // ['H', 'e']
์ด๋ฌํ ์ฐธ์กฐ ๊ธฐ๋ฐ ์ค๊ณ๋ ๋ฐ์ดํฐ๊ฐ ๋ถ๋ณ(immutable)์ผ ๋ ํจ๊ณผ์ ์ด๋ค. ๋ง์ฝ ๋ฌธ์์ด ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ค๋ฉด ๋ณต์ฌ ๋น์ฉ์ด ๋ฐ์ํ๊ธฐ ์์ํ์ฌ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์๋ค. ์๋ ์์ ์์ trimEnd()
๋ฉ์๋๋ ์๋ณธ ๋ฐ์ดํฐ๋ฅผ ๋ณดํธํ๊ธฐ ์ํด ๋ณต์ฌ๋ณธ์ ๋จผ์ ์์ฑํ ํ, ๊ณต๋ฐฑ ์ ๊ฑฐ ์์
์ ์ํํ๊ณ ์๋ค.
abstract class CustomString {
abstract value(): string[];
trimEnd() {
const bytes = this.value();
// ์๋ณธ ๋ฐฐ์ด์ ์ง์ ์์ ํ์ง ์๊ธฐ ์ํด ๋ณต์ฌ๋ณธ ์์ฑ
const result = bytes.slice();
while (result.at(-1) === ' ') result.pop();
return new BytesString(result);
}
}
mutation๊ณผ concatenation์ ์ฑ๋ฅ ์ฐจ์ด๋ฅผ ๋น๊ตํด ๋ณด๋ฉด, ์๋ณธ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ mutation ๋ฐฉ์์ด ํจ์ฌ ๋๋ฆฌ๊ฒ ๋์ํ๋ค.
const classNames = ["primary", "selected", "active", "medium"];
// mutation ๋ฐฉ์ (๋๋ฆผ / 37%)
const result = classNames
.map((c) => `button--${c}`)
.join(" ");
// concatenation ๋ฐฉ์ (๋น ๋ฆ / 100%)
const result = classNames
.map((c) => "button--" + c)
.reduce((acc, c) => acc + " " + c, "");
์๋ฐ์คํฌ๋ฆฝํธ์์ trim()
, replace()
๊ฐ์ ๋ฉ์๋๊ฐ ๋ด๋ถ์ ์ผ๋ก mutation ์์
์ ์ํํ๋ค. ๋ง์ฝ ๋๋์ ๋ฌธ์์ด ์ฒ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ, ์ด๋ฌํ ๋ฉ์๋ ์ฌ์ฉ์ ํผํ๋ ๊ฒ ์ฑ๋ฅ์ ์ ๋ฆฌํ ์ ์๋ค.
์ ๋ฌธํ ์ฌ์ฉํ๊ธฐ Use specialization
ํน์ ์ฌ์ฉ ์ฌ๋ก์ ์ ์ฝ ์กฐ๊ฑด์ ๋ง๊ฒ ๋ก์ง์ ์กฐ์ ํ๋ ๊ฒ์ ์ ๋ฌธํ๋ผ๊ณ ํ๋ค. ํน์ ์กฐ๊ฑด์ด ํด๋น ์ฌ๋ก์์ ๋ ์์ฃผ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ ํ์ ํ๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํด๋น ์กฐ๊ฑด์ ๋ง๊ฒ ์ฝ๋๋ฅผ ์ต์ ํํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
์๋ฅผ ๋ค์ด, ํ๋งค์๊ฐ ์ ํ ๋ชฉ๋ก์ ํ๊ทธ๋ฅผ ์ถ๊ฐํด์ผ ํ๋ ์ํฉ์ ์๊ฐํด ๋ณด์. ํ๊ทธ๊ฐ ๋๋ถ๋ถ ๋น์ด์๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค๋ ์ฌ์ค์ ์๊ณ ์๋ค๋ฉด, ์ด ์ ๋ณด๋ฅผ ํ์ฉํด ์ฝ๋๋ฅผ ์ต์ ํํ ์ ์๋ค.
// ์์ ๋ฐ์ดํฐ
const descriptions = ["apples", "oranges", "bananas", "seven"]; // ์ ํ ์ค๋ช
๋ชฉ๋ก
// ํน์ ์ ํ์ ๋ํ ํ๊ทธ
const someTags = { apples: "::promotion::" };
// ํ๊ทธ๊ฐ ์๋ ๊ฒฝ์ฐ
const noTags = {};
// ์ ํ ์ค๋ช
๊ณผ ํ๊ทธ๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํ๋ ๊ธฐ๋ณธ ํจ์
function productsToString(description, tags) {
let result = "";
description.forEach((product) => {
result += product; // ์ ํ ์ด๋ฆ ์ถ๊ฐ
if (tags[product]) result += tags[product]; // ํ๊ทธ ์์ผ๋ฉด ์ถ๊ฐ
result += ", "; // ๊ตฌ๋ถ์ ์ถ๊ฐ
});
return result;
}
// ํ๊ทธ๊ฐ ์๋ ๊ฒฝ์ฐ๋ฅผ ์ต์ ํํ ์ ๋ฌธํ๋ ํจ์
function productsToStringSpecialized(description, tags) {
// ํ๊ทธ๊ฐ ์์ ๋ ์ต์ ํ๋ ๋ก์ง ์คํ
if (Object.keys(tags).length === 0) {
let result = "";
description.forEach((product) => {
result += product + ", "; // ํ๊ทธ ์์ด ์ ํ ์ด๋ฆ๋ง ์ถ๊ฐ
});
return result;
}
// ํ๊ทธ๊ฐ ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ ํจ์ ์ฌ์ฉ
return productsToString(description, tags);
}
// ์ ๋ฌธํ๋์ง ์์ ํจ์ ์ฑ๋ฅ ํ
์คํธ (๋๋ฆผ / 85.71%)
for (let i = 0; i < 100; i++) {
productsToString(descriptions, someTags); // ํ๊ทธ๊ฐ ์์ ๋
productsToString(descriptions, noTags); // ํ๊ทธ๊ฐ ์์ ๋
productsToString(descriptions, noTags);
productsToString(descriptions, noTags);
productsToString(descriptions, noTags);
}
// ์ ๋ฌธํ๋ ํจ์ ์ฑ๋ฅ ํ
์คํธ (๋น ๋ฆ / 100%)
for (let i = 0; i < 100; i++) {
productsToStringSpecialized(descriptions, someTags); // ํ๊ทธ๊ฐ ์์ ๋
productsToStringSpecialized(descriptions, noTags); // ํ๊ทธ๊ฐ ์์ ๋
productsToStringSpecialized(descriptions, noTags);
productsToStringSpecialized(descriptions, noTags);
productsToStringSpecialized(descriptions, noTags);
}
์ด๋ฌํ ์ต์ ํ๋ ์ผํ ๋ณด๋ฉด ํจ๊ณผ๊ฐ ๋ฏธ๋ฏธํด ๋ณด์ผ ์๋ ์์ง๋ง, ๋ฐ๋ณต์ ์ผ๋ก ์คํ๋ ์๋ก ํจ๊ณผ๊ฐ ์ ์ฐจ ๋์ ๋๋ค.
๋ฐ์ดํฐ ๊ตฌ์กฐ Data structures
์ ์ ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์ ํ์ ํ๋ก๊ทธ๋จ ์ฑ๋ฅ์ ์ง์ ์ ์ธ ์ํฅ์ ๋ฏธ์น๋ค. ์๋ฅผ ๋ค์ด, ๋ฐฐ์ด์ ์์ฐจ์ ์ธ ๋ฐ์ดํฐ ์ ๊ทผ์ด๋ ํฌ๊ธฐ๊ฐ ์์ ๋ฐ์ดํฐ ์งํฉ์์ ํจ์จ์ ์ด์ง๋ง, ๋ฐ์ดํฐ ํฌ๊ธฐ๊ฐ ์ปค์ง์๋ก ๊ฒ์์ด๋ ํฌํจ ์ฌ๋ถ ํ์ธ ์์ ์ ์ฑ๋ฅ์ด ๊ธ๊ฒฉํ ์ ํ๋๋ค. ๋ฐ๋ฉด, Set์ ๋ด๋ถ์ ์ผ๋ก ํด์ ํ ์ด๋ธ์ ์ฌ์ฉํ์ฌ ๋น ๋ฅธ ์กฐํ ์๋๋ฅผ ์ ๊ณตํ๋ฏ๋ก ๊ฒ์ ์์ ์ ๋งค์ฐ ์ ๋ฆฌํ๋ค.
์ฌ๋ฐ๋ฅธ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ํํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ์์๋ฅผ ๊ณ ๋ คํ ์ ์๋ค:
- ๋ฐ์ดํฐ ํฌ๊ธฐ์ ๊ตฌ์กฐ
- ์์ ์ ๋น๋์ ์ข ๋ฅ (๊ฒ์, ์ฝ์ , ์ญ์ ๋ฑ)
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋๊ณผ ์ฑ๋ฅ ๊ฐ์ ํธ๋ ์ด๋์คํ
์๋ชป๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ํํ๋ฉด ํน์ ์์ ์์ ๋ถํ์ํ ์ฐ์ฐ์ด ๋ฐ๋ณต๋ ์ ์์ผ๋ฉฐ, ํนํ ๋๊ท๋ชจ ์งํฉ์์ ์ฌ๊ฐํ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง ์ ์๋ค. ๋ฐ๋ผ์ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๊ธฐ๋ณธ ํน์ฑ์ ๋ช ํํ ์ดํดํ๊ณ , ์๊ตฌ ์ฌํญ์ ๋ง๋ ๊ตฌ์กฐ๋ฅผ ์ ์คํ ์ ํํ๋ ๊ฒ์ด ์ค์ํ๋ค.
์๋๋ Array.includes
์ Set.has
์ ์ฑ๋ฅ์ ๋น๊ตํ๋ ์ฝ๋. ๋ฐ์ดํฐ์
ํฌ๊ธฐ๊ฐ ์ฆ๊ฐํจ์ ๋ฐ๋ผ ์ฑ๋ฅ ๊ฒฉ์ฐจ๊ฐ ๋์ฑ ์ปค์ง๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
const measurePerformance = (label, callback, iterations = 10) => {
const timings = [];
for (let i = 0; i < iterations; i++) {
const start = process.hrtime.bigint();
callback();
const end = process.hrtime.bigint();
timings.push(end - start);
}
const total = timings.reduce((sum, t) => sum + t);
const average = total / BigInt(iterations);
const timingNumbers = timings.map(Number);
const min = Math.min(...timingNumbers);
const max = Math.max(...timingNumbers);
console.log(`${label}: ํ๊ท ${average}ns, ์ต์๊ฐ ${min}ns, ์ต๋๊ฐ ${max}ns`);
};
const sizes = [1_000, 10_000, 100_000, 1_000_000, 10_000_000];
sizes.forEach((size) => {
const userIds = Array.from({ length: size }, (_, i) => i);
const adminIdsArray = userIds.slice(0, size / 10);
const adminIdsSet = new Set(adminIdsArray);
const targetValues = [0, Math.floor(size / 2), Math.floor(Math.random() * size), size - 1];
console.group(`๋ฐ์ดํฐ ํฌ๊ธฐ: ${size}`);
measurePerformance('Array.includes', () => {
for (const value of targetValues) {
adminIdsArray.includes(value);
}
});
measurePerformance('Set.has', () => {
for (const value of targetValues) {
adminIdsSet.has(value);
}
});
console.groupEnd();
});
process.hrtime.bigint()
๋ Node.js ํ๊ฒฝ์์ ์ ๊ณตํ๋ ํจ์๋ก ํ๋ก์ธ์ค๊ฐ ์์๋ ์ดํ์ ๊ฒฝ๊ณผ ์๊ฐ์ ๋๋
ธ์ด ๋จ์์ BigInt๋ฅผ ๋ฐํํ๋ค. ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ฉํ๋ performance.now()
์ ์ ์ฌํ์ง๋ง ๋ ๋์ ์ ๋ฐ๋ ์ ๊ณตํ๋ค. ์ฐธ๊ณ ๋ก ์๊ฐ ๋จ์๋ ์ด, ๋ฐ๋ฆฌ์ด ์ธ์๋ ๋ง์ดํฌ๋ก์ด, ๋๋
ธ์ด, ํผ์ฝ์ด, ํจํ ์ด, ์ํ ์ด, ์ ญํ ์ด ๋ฑ์ด ์๋ค. ๊ฐ ๋จ์๋ ์ด์ ๋จ์์ 1/1000์ด๋ค.
- 1์ด (s) = 1000 ๋ฐ๋ฆฌ์ด (ms)
- 1๋ฐ๋ฆฌ์ด (ms) = 1000 ๋ง์ดํฌ๋ก์ด (µs)
- 1๋ง์ดํฌ๋ก์ด (µs) = 1000 ๋๋ ธ์ด (ns)
- 1๋๋ ธ์ด (ns) = 1000 ํผ์ฝ์ด (ps)
- …
๋ ํผ๋ฐ์ค
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Algorithm] ์ฌ๋ผ์ด๋ฉ ์๋์ฐ Sliding Window ์๊ณ ๋ฆฌ์ฆ ํบ์๋ณด๊ธฐ (2) | 2024.11.11 |
---|---|
[React] ๋ฆฌ์กํธ ์ฝ๋๋ฅผ ๊ฐ์ ํ ์ ์๋ 4๊ฐ์ง ํ (0) | 2024.10.28 |
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 2 (1) | 2024.10.13 |
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1 (0) | 2024.10.05 |
[TS] ํ์ ์คํฌ๋ฆฝํธ ๋ธ๋๋๋ ํ์ (0) | 2024.09.26 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[Algorithm] ์ฌ๋ผ์ด๋ฉ ์๋์ฐ Sliding Window ์๊ณ ๋ฆฌ์ฆ ํบ์๋ณด๊ธฐ
[Algorithm] ์ฌ๋ผ์ด๋ฉ ์๋์ฐ Sliding Window ์๊ณ ๋ฆฌ์ฆ ํบ์๋ณด๊ธฐ
2024.11.11 -
[React] ๋ฆฌ์กํธ ์ฝ๋๋ฅผ ๊ฐ์ ํ ์ ์๋ 4๊ฐ์ง ํ
[React] ๋ฆฌ์กํธ ์ฝ๋๋ฅผ ๊ฐ์ ํ ์ ์๋ 4๊ฐ์ง ํ
2024.10.28 -
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 2
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 2
2024.10.13 -
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1
2024.10.05