๋ฐ˜์‘ํ˜•

"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์€ ๋‚ด๋ถ€์ ์œผ๋กœ ํ•ด์‹œ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น ๋ฅธ ์กฐํšŒ ์†๋„๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ๊ฒ€์ƒ‰ ์ž‘์—…์— ๋งค์šฐ ์œ ๋ฆฌํ•˜๋‹ค.

 

์˜ฌ๋ฐ”๋ฅธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์š”์†Œ๋ฅผ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ๋‹ค:

  1. ๋ฐ์ดํ„ฐ ํฌ๊ธฐ์™€ ๊ตฌ์กฐ
  2. ์ž‘์—…์˜ ๋นˆ๋„์™€ ์ข…๋ฅ˜ (๊ฒ€์ƒ‰, ์‚ฝ์ž…, ์‚ญ์ œ ๋“ฑ)
  3. ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๊ณผ ์„ฑ๋Šฅ ๊ฐ„์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„

 

์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜๋ฉด ํŠน์ • ์ž‘์—…์—์„œ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ์ด ๋ฐ˜๋ณต๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์ง‘ํ•ฉ์—์„  ์‹ฌ๊ฐํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ๊ธฐ๋ณธ ํŠน์„ฑ์„ ๋ช…ํ™•ํžˆ ์ดํ•ดํ•˜๊ณ , ์š”๊ตฌ ์‚ฌํ•ญ์— ๋งž๋Š” ๊ตฌ์กฐ๋ฅผ ์‹ ์ค‘ํžˆ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

 

์•„๋ž˜๋Š” 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)

 

 

๋ ˆํผ๋Ÿฐ์Šค


 

Optimizing Javascript for fun and for profit

--> I often feel like javascript code in general runs much slower than it could, simply because it’s not optimized properly. Here is a summary of common optimization techniques I’ve found useful. Note that the tradeoff for performance is often readabil

romgrk.com

 

 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
 
๋ฐ˜์‘ํ˜•