Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】How many tasks at once? Count the cores you are given

2026年10月13日に「Dev.to」が公開したITニュース「How many tasks at once? Count the cores you are given」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

コンテナ環境では、プログラムが認識するCPU数やメモリはホストの値で、コンテナの実際の制限とは異なる場合がある。タスクを多すぎると性能が落ちるため、cgroupで設定された正しい制限値を読み取り、適切なタスク数を設定することが重要だ。これにより効率的なタスク実行が可能になる。

ITニュース解説

システム開発において、プログラムが同時にいくつの処理(タスク)を実行するかは、システムの性能や効率に大きく影響する重要な設定だ。多くの処理をまとめて実行する「タスクランナー」のようなツールでは、この同時実行数を決める場面が頻繁に訪れる。もしこの数が少なすぎると、コンピューターの頭脳であるCPUの能力を十分に使いこなせず、せっかくのリソースが無駄になってしまう。逆に多すぎると、タスク同士が限られたCPUを取り合ってしまい、一つ一つのタスクの処理速度が落ち、結果として全ての処理が終わるまでに余計な時間がかかってしまうのだ。

一般的に、同時実行数の最適な値は「CPUのコア数と同じ」だと考えられがちだ。CPUコアとは、CPUが同時に独立した処理を行える単位のことで、例えば4コアのCPUなら同時に4つの処理を効率良く実行できる。しかし、最近のシステム開発で広く使われている「コンテナ」という技術の環境では、この常識が通用しない場合があり、注意が必要となる。

コンテナは、アプリケーションとその実行に必要なものを一つにまとめた軽量な仮想環境だ。DockerやKubernetesといった技術が代表的で、CI/CD(継続的インテグレーション・継続的デリバリー)と呼ばれる開発プロセスでは、テストやビルドのジョブがコンテナ内で実行されることが非常に多い。このコンテナ環境では、CPUの使用量に意図的に制限がかけられることがある。例えば、Dockerコマンドの--cpus=2というオプションや、Kubernetesの設定ファイルにあるlimits.cpuといった項目で、「このコンテナは最大で2コア分のCPUしか使えない」といった具体的な制限を設定できるのだ。

問題は、プログラムがこのCPU制限を正しく認識できない点にある。例えば、JavaScriptの実行環境であるBunではnavigator.hardwareConcurrency、Node.jsではos.cpus()といった関数を使うと、プログラムは現在のシステムが持つCPUのコア数を取得できる。しかし、コンテナ内でこれらの関数を実行しても、取得できるのはコンテナ自身にかけられた制限値ではなく、コンテナが動作している「ホスト」と呼ばれる物理マシンや仮想マシンのCPUコア数なのだ。

具体的なシナリオを想像してみよう。例えば、32コアの強力なホストマシン上で、CPU利用を2コアに制限されたコンテナが動いているとする。このコンテナ内でタスクランナーが起動され、プログラムがホストのCPU数である「32コア」を読み取ってしまった場合、タスクランナーは「よし、32個のタスクを同時に動かそう」と判断するかもしれない。しかし、実際にコンテナに与えられているのは2コア分の能力だけだ。結果として、32個のタスクのうち、実際に処理が進むのは2個だけで、残りの30個のタスクは、カーネル(OSの核となる部分)のスケジューラーに順番待ちをさせられることになる。これは、タスクランナー自身が効率的なスケジューリングをする機会を失い、無駄な待ち時間が発生するため、全体の処理が遅くなってしまう原因となる。

CPUだけでなく、メモリについても同様の落とし穴がある。os.totalmem()のような関数は、コンテナに割り当てられたメモリ制限ではなく、ホストマシンの総RAM容量を報告してしまう。これもまた、プログラムが利用可能なリソースを誤認識し、予期せぬメモリ不足やパフォーマンス低下を引き起こす可能性がある。

では、プログラムがコンテナの正確なCPU制限を把握するためにはどうすればよいだろうか。Linuxシステムには「cgroup(コントロールグループ)」という仕組みがあり、カーネルがプロセスやコンテナのリソース使用量を管理・制限するために使われている。このcgroupのファイルシステムから、プログラムは実際にカーネルが適用しているCPUやメモリの制限値を直接読み取ることが可能だ。

cgroupにはバージョンがあり、主にv1とv2が使われている。cgroup v2では、cpu.maxというファイルにCPUの制限情報が書かれている。このファイルには通常、「<割り当て量> <期間>」という形式の数値が記載されており、例えば200000 100000であれば、100,000マイクロ秒(0.1秒)の期間中に200,000マイクロ秒(0.2秒)分のCPU時間を使用できる、つまり2コア分のCPUが割り当てられていることを意味する。もしmaxと記載されていれば、CPUに制限がない状態だ。cgroup v1の場合も同様に、cpu.cfs_quota_usとcpu.cfs_period_usという二つのファイルから制限値を読み取ることができる。こちらのクォータ値が-1であれば、制限なしとなる。

さらに複雑な点として、cgroupは階層構造になっている。あるコンテナに適用される制限は、そのコンテナ自身のcgroupだけでなく、その親となるcgroupによっても決定されることがある。そのため、最も厳しい制限を正確に把握するには、プログラムが自身のcgroupからシステムルートまで階層を遡り、途中で見つかる最も厳しい制限値を探し出す必要がある。利用可能なワーカー数(同時に実行できるタスク数)は、このようにして見つけた最も厳しいCPUクォータによって上限が設定される。例えば、1.5コア分のクォータが設定されていた場合でも、半分のコアでも作業はできるため、切り上げて2ワーカーとして計算される。メモリの制限も同様で、cgroup v2ではmemory.max、cgroup v1ではmemory.limit_in_bytesから正しい情報を取得する。ちなみに、macOSにはcgroupのようなリソース管理の仕組みがないため、単純にマシンの物理的なCPU数やメモリ容量がそのまま利用可能な値となる。

MITライセンスで提供されているJavaScript向けタスクランナーvxは、まさにこのcgroupの仕組みを利用して、コンテナ環境におけるCPUの制限を正確に把握する。vx infoというコマンドを実行すると、vxが決定したワーカー数と、その情報がどこから取得されたのかを表示してくれる。例えば、「workers: 2 — cgroup CPU quota 2 of 32 cores」という出力は、ホストマシンが32コアであるにもかかわらず、cgroupによって2コアに制限されていることを正しく認識し、その制限に基づいて2ワーカーで実行することを決定したことを示している。一方、制限がないラップトップPCで実行すれば、「8 — the CPU count」のように、物理的なCPUコア数をそのまま利用することを示す。

vxはデフォルトで最適なワーカー数を自動で決定してくれるが、開発者はこの設定を柔軟に調整することもできる。例えば、--concurrency 4というオプションを使えば、強制的に4タスクを並行して実行させることができる。また、--concurrency 50%のように、利用可能なコア数の割合で指定することも可能で、この場合、最低でも1タスクは実行されるようになっている。ネットワーク通信やディスクI/Oが主な処理内容で、CPUの計算能力をあまり使わないタスクの場合は、たとえCPUコア数を超えていても、--concurrency 150%のように並行処理数を増やすことで、全体の処理時間を短縮できる場合もある。これは、CPUが待機している間に他のネットワークやディスクの処理を進められるためだ。

さらに、全てのタスクがCPUのワーカー数を消費するわけではない。例えば、キャッシュからファイルを復元する作業は、主にディスクへの読み書きが中心であり、CPUの計算処理をほとんど必要としない。そのため、vxではこのようなキャッシュ復元処理を通常のタスクとは別の「レーン」で実行し、CPUワーカーの数を消費しないようにしている。キャッシュ復元は、通常のワーカー数の最大2倍まで並行して実行でき、CPUを必要とする他のタスクの邪魔をしない。また、タスクが別のマシン上で実行される「リモートタスク」の場合も、そのリモートマシンのリソースを消費するため、ローカルマシンのCPUワーカー数を消費しない。これにより、例えば10コアのラップトップPCから、64ワーカー分の容量を持つリモートプールを最大限に活用するといった柔軟な運用が可能になる。

このように、特にコンテナ環境下では、プログラムが利用可能なCPUやメモリのリソースを正確に認識することが、システムのパフォーマンスを最適化し、リソースの無駄をなくす上で非常に重要となる。cgroupのようなOSの機能を使って正しい制限を読み取り、タスクの性質に応じて並行処理数を適切に調整する。これは、システムエンジニアとして効率的で高性能なシステムを構築するための基本的な知識であり、実践的なスキルだと言えるだろう。

関連コンテンツ

関連IT用語

関連ITニュース