JavaScript | 大きな配列を扱うときのパフォーマンス面

JavaScript JavaScript
スポンサーリンク

では、最終ステップとして 複数 Web Worker を使った完全並列処理 に拡張し、巨大多次元配列を複数 CPU コアで分散処理する例を作ります。UI は進捗バー付きでフリーズなしです。


例:複数 Worker で巨大多次元配列を並列処理

1️⃣ メイン HTML(UI + Worker 管理)

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>複数 Worker 並列処理デモ</title>
<style>
  #progress {
    width: 100%;
    background-color: #eee;
    border-radius: 5px;
    overflow: hidden;
    margin: 10px 0;
  }
  #bar {
    height: 20px;
    background-color: #E91E63;
    width: 0%;
  }
  #status { font-family: monospace; }
</style>
</head>
<body>
<h1>複数 Worker 並列処理デモ</h1>
<div id="progress"><div id="bar"></div></div>
<p id="status">準備中...</p>

<script>
const numWorkers = 4; // 使用する Worker 数
const rows = 1000, cols = 1000;
let workers = [];
let remainingWorkers = numWorkers;
let sumTotal = 0;

function updateProgress() {
  let percent = Math.min(100, Math.floor((sumTotal / (rows * cols * 1.0)) * 100));
  document.getElementById('bar').style.width = percent + '%';
  document.getElementById('status').textContent = `処理中... ${percent}% 合計=${sumTotal}`;
}

for (let i = 0; i < numWorkers; i++) {
  const worker = new Worker('worker-multi.js');
  workers.push(worker);

  worker.onmessage = function(e) {
    const data = e.data;
    if (data.progress !== undefined) {
      updateProgress();
    }
    if (data.done) {
      sumTotal += data.sum;
      remainingWorkers--;
      if (remainingWorkers === 0) {
        document.getElementById('status').textContent =
          `完了! 合計 = ${sumTotal}`;
        document.getElementById('bar').style.width = '100%';
      }
    }
  };

  // データ範囲を Worker に渡す
  worker.postMessage({
    startRow: Math.floor((rows / numWorkers) * i),
    endRow: Math.floor((rows / numWorkers) * (i + 1)),
    cols
  });
}
</script>
</body>
</html>
HTML

2️⃣ Worker ファイル(worker-multi.js)

self.onmessage = function(e) {
  const { startRow, endRow, cols } = e.data;

  class FastQueue {
    constructor() { this.data = []; this.head = 0; }
    enqueue(v) { this.data.push(v); }
    dequeue() { if (this.head >= this.data.length) return undefined; return this.data[this.head++]; }
    size() { return this.data.length - this.head; }
    compact() { if (this.head > 10000) { this.data = this.data.slice(this.head); this.head = 0; } }
  }

  let q = new FastQueue();
  for (let i = startRow; i < endRow; i++) {
    let row = Array.from({length: cols}, (_, j) => j);
    q.enqueue(row);
  }

  const chunk = 10;
  let processed = 0;
  let sum = 0;

  function processChunk() {
    let chunkCount = 0;
    while (chunkCount < chunk && q.size() > 0) {
      let row = q.dequeue();
      let newRow = [];
      for (let val of row) {
        if (val % 2 === 0) {
          val = val * 2;
          sum += val;
          newRow.push(val);
        }
      }
      if (newRow.length > 0) q.enqueue(newRow);
      chunkCount++;
    }

    processed += chunk;
    self.postMessage({ progress: Math.min(100, Math.floor((processed / (endRow - startRow)) * 100)), sum });

    if (processed < (endRow - startRow)) {
      setTimeout(processChunk, 0);
    } else {
      self.postMessage({ done: true, sum, remaining: q.size() });
    }
  }

  processChunk();
};
JavaScript

ポイント解説

  1. Worker を複数使い CPU を分散
    • 1つの Worker に処理を分割 → CPU コアを有効活用
    • 巨大多次元配列でもフリーズなし
  2. 各 Worker 内でチャンク処理
    • 一度に全データを処理せず、UI や通信に影響を与えない
  3. 進捗集計
    • メインスレッドで各 Worker の進捗を統合
    • 合計をリアルタイム表示
  4. メモリ効率
    • Worker 内は行単位で操作
    • shift/unshift せず FastQueue を活用
  5. 条件付き削除+変換+集計を同時処理
    • filter/map/sum をまとめて処理
    • 中間配列を最小化して高速化

💡 応用例

  • 巨大データをブラウザ上でリアルタイムに安全に処理
  • 並列処理で大規模統計やシミュレーションを実行
  • データ解析やゲームエンジンなど、CPU 集約タスクに最適

この仕組みを応用すると、数千万件レベルの多次元データもブラウザで効率的に処理可能 になります。

タイトルとURLをコピーしました