記事「 エピソード8:Node.jsの作成者であるRyan Dahlのインタビューと 翻訳に関するコメント」を読んだ後、Node.jsのブロックおよびノンブロッキングファイル読み取り操作の有効性を、テーブルとグラフィック猫の下でテストすることにしました。
UPD:アンダーカットは誤ったベンチマークです。 コメントが正しく指摘したように、基本的にfs.readFileSyncとfs.readFileを使用して同じファイルのキャッシュを比較します。
UPD2:記事が編集され、ベンチマークが修正され、結果が追加されました。
ブロッキング操作(fs.readFileSyncはこれらの1つです)は、JSに直接関連する操作が完了するまで、アプリケーション全体の実行が中断されることを前提としています。
非ブロッキングオプションにより、非JS操作を並列スレッドで非同期に実行できます(例:fs.readFile)。
ブロックと非ブロックの詳細については、 こちらをご覧ください 。
Node.jsは、child_processまたはclusterを使用して単一のスレッドで実行されますが、コードの実行を複数のスレッドに分散できます。
テストは、キャッシュファイル(大規模および小規模)の並列および順次読み取り、および非キャッシュファイルの読み取りで実行されました。
すべてのテストは、1台のコンピューターと1台のHDD、およびimmenoで行われました。
OS:Ubuntu 16.04
Node.jsバージョン:8.4.0
プロセッサー:AMD Phenom(tm)9750 Quad-Core Processor
物理コア:4
HDD:2TB 7200rpm 64MB
ファイルシステムタイプ:ext4
file.txtサイズ:3.3 kB
bigFile.txtサイズ:6.5 MB
キャッシュファイルの結果。
3.3 kBファイルを10,000回読み取る場合
記号 | お名前 | ops /秒 | パーセント |
---|---|---|---|
A | ループreadFileSync | 7.4 | 100% |
B | 約束チェーンreadFileSync | 4.47 | 60% |
C | 約束チェーンreadFile | 1.09 | 15% |
D | Promise.all readFileSync | 4.58 | 62% |
E | Promise.all readFile | 1.69 | 23% |
F | マルチスレッドループreadFileSync | 20.05 | 271% |
G | マルチスレッドpromise.all readFile | 4.98 | 67% |
3.3 kBファイルを100回読み取る場合
記号 | お名前 | ops /秒 | パーセント |
---|---|---|---|
A | ループreadFileSync | 747 | 100% |
B | 約束チェーンreadFileSync | 641 | 86% |
C | 約束チェーンreadFile | 120 | 16% |
D | Promise.all readFileSync | 664 | 89% |
E | Promise.all readFile | 238 | 32% |
F | マルチスレッドループreadFileSync | 1050 | 140% |
G | マルチスレッドpromise.all readFile | 372 | 50% |
6.5 MBファイルを100回読み取る場合
記号 | お名前 | ops /秒 | パーセント |
---|---|---|---|
A | ループreadFileSync | 0.63 | 83% |
B | 約束チェーンreadFileSync | 0.66 | 87% |
C | 約束チェーンreadFile | 0.61 | 80% |
D | Promise.all readFileSync | 0.66 | 87% |
E | Promise.all readFile | 0.76 | 100% |
F | マルチスレッドループreadFileSync | 0.83 | 109% |
G | マルチスレッドpromise.all readFile | 0.81 | 107% |
3.3 kBファイルを10,000回読み取り中のCPU使用率
ご覧のとおり、fs.readFileSyncは常に1つのコアの1つのスレッドで実行されます。 fs.readFileはその作業でいくつかのスレッドを使用しますが、カーネルは最大容量でロードされません。 小さなファイルの場合、fs.readFileSyncはfs.readFileよりも高速に実行され、同じスレッドで実行されているノードで大きなファイルを読み取る場合のみ、fs.readFileはfs.readFileSyncよりも高速に実行されます。
したがって、fs.readFileSyncを使用して小さなファイルを読み取り、fs.readFileを使用して大きなファイルを読み取ることをお勧めします(ファイルの大きさはコンピューターとソフトウェアによって異なります)。
一部のタスクでは、fs.readFileSyncは大きなファイルの読み取りにも適している場合があります。 たとえば、多くのファイルを長時間読み取って処理する場合。 この場合、カーネル間の負荷はchild_processを使用して分散する必要があります。 大まかに言って、複数のスレッドでの操作ではなく、ノード自体を実行します。
UPD2
以下は、同じサイズ(3.3kB)の多くのキャッシュされていないファイルを読み取るために取得されたデータです。
1000ファイルを読むとき
記号 | お名前 | ops /秒 | パーセント |
---|---|---|---|
A | ループreadFileSync | 8.47 | 74% |
B | 約束チェーンreadFileSync | 6.28 | 55% |
C | 約束チェーンreadFile | 5.49 | 48% |
D | Promise.all readFileSync | 8.06 | 70% |
E | Promise.all readFile | 11.05 | 100% |
F | マルチスレッドループreadFileSync | 3.71 | 32% |
G | マルチスレッドpromise.all readFile | 5.11 | 44% |
100ファイルを読むとき
記号 | お名前 | ops /秒 | パーセント |
---|---|---|---|
A | ループreadFileSync | 79.19 | 85% |
B | 約束チェーンreadFileSync | 50.17 | 54% |
C | 約束チェーンreadFile | 48.46 | 52% |
D | Promise.all readFileSync | 54.7 | 58% |
E | Promise.all readFile | 92.87 | 100% |
F | マルチスレッドループreadFileSync | 80.46 | 86% |
G | マルチスレッドpromise.all readFile | 92.19 | 99% |
キャッシュされていないファイルを読み取るときのプロセッサの負荷は小さく、約20%です。 結果は±30%異なります。
結果は、非ブロッキングfs.readFileを使用する方がより有益であることを示しています。
ファイル読み取り状況の例。
1つのスレッドT1のノードでWebサーバーが回転しているとします。 2つの要求(P1およびP2)が同時に小さなファイル(要求ごとに1つ)を読み取り、処理します。 fs.readFileSyncを使用する場合、T1ストリームでのコード実行のシーケンスは次のようになります。
P1-> P2
fs.readFileを使用する場合、T1ストリームでのコード実行のシーケンスは次のようになります。
P1-1-> P2-1-> P1-2-> P2-2
P1-1、P2-1-別のストリームに読み取りを委任する場合、P1-2、P2-2-読み取り結果を受け取り、データを処理します。