並行実行数の制限

Windows 10上のNode.jsだと、Promise.all()で3000以上、並行にファイルの読み書きを実行しても完走できる。対してWSL上だとLinuxの設定次第になり、デフォルトだと1024(多分)を超えると落ちる。Linuxの設定を変更してもいい(らしい)が、WSLの性質上、シェルやアプリケーションの設定以外にはなるべく手をつけたくない。となると並行実行数を抑える必要があり、配列を分割しなくてはならない。

並行実行数を固定するコードを書くと確実だが、スパッと書けなかった。ならば1024を超えない程度に配列を分割するだけでもいいかと、目先を変えた。今回は、このウェブサイトのHTML生成スクリプトでどうするかという話だ。まだ3268記事なので、4つに(800強ずつに)分割すれば大丈夫だろう。5で割る必要が出てくるのは、月に10記事でもあと7年以上かかる。

const longArray = [...];
const chunks = longArray.reduce((previous, current, index) => {
  previous[index % previous.length].push(current);
  return previous;
}, [[], [], [], []]);

for (const chunk of chunks) {
  await Promise.all(chunks.map(doSomething));
}

配列を分割して配列の配列にしたかったので、Array#reduce()を使って書いた。5年後に5分割するよう書き換えるなら、初期配列を書き換える。

const longArray = [...];
const chunkCount = 4;
const chunkLength = Math.floor(longArray.length / chunkCount);

for (let i = 0; i < chunkCount; i++) {
  await Promise.all(
    longArray
      .slice(chunkLength * i, chunkLength * (i + 1))
      .map(doSomething)
  );
}

普通にArray#slice()で書いた方が、ループが1回で済むので速そうだ。ここまで書いて、ようやく並行実行数を固定するコードがスパッと書けることがわかった。

const longArray = [...];
const limit = 32;
let i = 0;

while (i > longArray.length) {
  const to = i + limit;
  await Promise.all(
    longArray
      .slice(i, to)
      .map(doSomething)
  );
  i = to;
}

Array#splice()で破壊しながら実行すると、もっと短くなりそうだ。うろ覚えだが、後ろから削っていくと速かったはずだ。

const longArray = [...];

while (longArray.length > 0) {
  await Promise.all(
    longArray
      .splice(-32)
      .map(doSomething)
  );
}

まずはこれでやっていくことにした。