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

JavaScript JavaScript
スポンサーリンク

では次のステップとして、Worker ごとの個別統計やエラー検知もリアルタイムグラフ化するプロ仕様拡張版 を作ります。
これにより、各 Worker の進捗、処理合計、エラー発生状況などを個別に可視化できます。


例:Worker 個別統計+エラー検知+リアルタイムグラフ

1️⃣ HTML(UI + Chart.js)

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Worker 個別統計+エラー検知デモ</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
  #progress { width: 100%; background-color: #eee; border-radius: 5px; overflow: hidden; margin: 10px 0; }
  #bar { height: 20px; background-color: #FF5722; width: 0%; }
  #status { font-family: monospace; }
  #chart-container { width: 100%; max-width: 800px; }
</style>
</head>
<body>
<h1>Worker 個別統計+エラー検知デモ</h1>
<div id="progress"><div id="bar"></div></div>
<p id="status">準備中...</p>
<div id="chart-container">
  <canvas id="progressChart"></canvas>
</div>

<script>
const maxWorkers = navigator.hardwareConcurrency || 4;
let workers = [];
let taskQueue = [];
let activeWorkers = 0;
let sumTotal = 0;

const rows = 1000, cols = 1000;

// Chart.js 初期化(Worker 個別データを色分け)
const ctx = document.getElementById('progressChart').getContext('2d');
const datasets = [];
for (let i = 0; i < maxWorkers; i++) {
  datasets.push({
    label: `Worker ${i+1}`,
    data: [],
    borderColor: `hsl(${i*360/maxWorkers},70%,50%)`,
    backgroundColor: `hsla(${i*360/maxWorkers},70%,50%,0.2)`,
    fill: true
  });
}
const chart = new Chart(ctx, {
  type: 'line',
  data: { labels: [], datasets },
  options: { animation: false, scales: { x: { title: { display: true, text: 'ステップ' } }, y: { title: { display: true, text: '合計' } } } }
});

// タスク分割
for (let i = 0; i < rows; i += 50) {
  taskQueue.push({ startRow: i, endRow: Math.min(i + 50, rows), cols });
}

function startWorker(task, workerIndex) {
  const worker = new Worker('worker-multi-error.js');
  activeWorkers++;
  let step = 0;

  worker.onmessage = function(e) {
    const data = e.data;
    if (data.progress !== undefined) {
      // グラフ更新
      step++;
      chart.data.labels.push(step);
      chart.data.datasets[workerIndex].data.push(data.sum);
      chart.update('none');

      const percent = Math.min(100, Math.floor((sumTotal + data.sum) / (rows * cols * 1.0) * 100));
      document.getElementById('bar').style.width = percent + '%';
      document.getElementById('status').textContent =
        `処理中... ${percent}% 合計=${sumTotal + data.sum}${data.error ? ' ⚠エラー発生!' : ''}`;
    }

    if (data.done) {
      sumTotal += data.sum;
      activeWorkers--;
      if (taskQueue.length > 0) {
        startWorker(taskQueue.shift(), workerIndex); // 次タスク割当
      } else if (activeWorkers === 0) {
        document.getElementById('status').textContent =
          `完了! 合計 = ${sumTotal}`;
        document.getElementById('bar').style.width = '100%';
      }
    }
  };

  worker.postMessage(task);
  workers.push(worker);
}

// 初期 Worker を最大数まで起動
for (let i = 0; i < Math.min(maxWorkers, taskQueue.length); i++) {
  startWorker(taskQueue.shift(), i);
}
</script>
</body>
</html>
HTML

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

// worker-multi-error.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(){return this.head>=this.data.length?undefined: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 = [];
      let error = false;

      for (let val of row) {
        // 任意のエラー条件(例: 999 は異常値と仮定)
        if (val === 999) { error = true; continue; }
        if (val % 2 === 0) { 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, error });

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

  processChunk();
};
JavaScript

ポイント解説

  1. Worker 個別グラフ
    • Chart.js で各 Worker の合計推移を色分け表示
    • タスクごとの処理状況をリアルタイムで確認
  2. エラー検知
    • Worker 内で任意の異常値を検知
    • メインスレッドに error フラグ付きで通知
    • UI に ⚠マークで表示
  3. 自動スケーリング + リアルタイム統計
    • タスク完了後に次タスクを自動割当
    • Worker 数や処理量に応じて柔軟に負荷分散
  4. ブラウザ負荷軽減
    • チャンク処理 + 非同期 setTimeout
    • グラフ更新は高速化設定(animation:false + update('none')

💡 応用例

  • 巨大スプレッドシートの個別 Worker ごとの進捗監視
  • Web アプリでの大規模データ解析+リアルタイムエラー検知
  • 大規模シミュレーションやゲーム処理の状況監視

この構成を応用すると、ブラウザ上で CPU 複数コアをフル活用しつつ、個別 Worker の統計とエラーまでリアルタイム可視化できる本格プロ仕様のデータ処理プラットフォーム が実現可能です。


さらなる拡張案

  1. Worker 個別統計の高度可視化
    • 処理時間、タスク完了率、エラー率を複合グラフで表示
    • グラフを折れ線+棒グラフ+色分けで Worker ごとに可視化
  2. ダイナミック Worker スケーリング
    • 負荷に応じて Worker 数を増減
    • 低負荷時は Worker を停止、高負荷時に自動追加
  3. リアルタイムフィルタ&集計
    • Worker から送られる集計結果をブラウザでフィルタ
    • 条件付き集計(例:特定値以上の合計だけ集計)を即時表示
  4. UI 操作によるタスク制御
    • タスクの一時停止、再開、キャンセル
    • 特定 Worker の処理優先度変更
  5. エラー解析・通知
    • エラー発生 Worker をハイライト
    • 詳細ログをリアルタイム表示、メールや通知に連携可能

まとめ

  • 巨大多次元データをブラウザ上で安全に処理
  • 自動スケーリング Worker で CPU コアを最大活用
  • Worker 個別統計・エラー検知をリアルタイム可視化
  • UI は常に応答可能で、進捗・統計・エラーを即座に把握
タイトルとURLをコピーしました