プログラミングにおける並行処理と並列処理の違いとは?真に同時な並列処理はかけるのか?
今回はプログラミングにおける並行処理と並列処理についてみていきたいと思います。
「プログラミングにおける平行処理と並列処理の違いは何でしょう?」
これらは「同時に処理する」というイメージで、似たような概念のように思えますが、実際には異なるものです。
並行処理は、複数のタスクが同時に一つのCPUコアで実行される(ように見える)ことを指します。
CPUのコアが1つしかないのに、複数のタスクを同時に実行するのは不可能のようにおもいますが、 タスクをコンテキストスイッチ(一つのタスクから別のタスクへの切り替え)によって高速に切り替えることで実現しています。
一方、並列処理は、複数のタスクが異なるCPUコアで同時に実行されることを指します。
それでは、低レイヤーなCPUから順番にみていきたいと思います。
CPUのコアについて
最近のCPUは、多くの場合、一つ以上の「コア」を持っていて、同時に複数の処理を行うことを可能にします。
コアには「物理コア」と「論理コア」の二つの概念が存在し、それぞれが異なる目的で使用されます。
物理コアとは
物理コアは、CPU内部に実際に存在するプロセッサーのコアです。
それぞれの物理コアは、独立して命令を実行する能力を持っています。
複数の物理コアを持つCPU(マルチコアCPU)は、それぞれのコアが独立して作動するため、一度に複数の処理を行うことが可能です。
論理コアとは
論理コアは、物理コアの数を超えて存在する(ように見える)コアです。
論理コアは、ハイパースレッディング(Intelの技術)や同等のテクノロジーを使用して、物理コアを2つ以上の論理コアに分割します。 これにより、単一の物理コアが複数のスレッドを同時に処理することが可能になり、結果的にシステムのパフォーマンスが向上します。
物理コアと論理コアの違い
物理コアはCPUの実際の物理的なコンポーネントで、それぞれが独立して命令を実行する能力を持っています。
一方、論理コアは物理コアを「仮想的に」分割して作られ、複数のタスクを同時に処理するように見せることでシステムのパフォーマンスを向上させます。 (論理コアでも物理的に重複して持っているコンポーネントはあるが、実行ユニットは1つらしいです。)
物理コアは実際に複数のタスクを「真に同時」に実行できるのに対して、 論理コアの「同時」処理は、タスクを非常に高速に切り替えること(コンテキストスイッチ)で実現されます。 これはタスクが同時に実行されているかのように見えるだけです。(物理コアは一度に一つのタスクしか処理できないので)
ネイティブスレッドについて
ネイティブスレッドはオペレーティングシステム(OS)によって直接管理されるスレッドを指します。
これらのスレッドは、OSのスケジューラによって、システムが利用可能な「論理コア」間でスケジューリングされます。 どのネイティブスレッドをどの論理コアにスケジュールするのかはOS任せなので、「真に同時」になる場合とならない場合がでてきます。
2つのスレッドがある場合、スケジューラーが同じ物理コアに割り当てた場合は、「真に同時」にはなりません。
一方、別の物理コアに割り当てた場合は、「真に同時」になることがあります。
ですが、一度、特定の論理コアに割り当てたスレッドは、最適化のために別の論理コアに割り当てられる可能性があります。
ネイティブスレッド利点は、マルチコアをフルに活用することができます。 各スレッドが独立してスケジュールされるため、同時に複数のスレッドが実行されることが可能になり、 一つのスレッドがブロック状態になっても(例えば、I/O待ちなど)、他のスレッドがその実行を続けることが可能です。
欠点としては、ネイティブスレッドの作成とスケジューリングは高コストであり、リソースを大量に消費することです。 スレッドのコンテキストスイッチにはオーバーヘッドが伴い、パフォーマンスに影響を及ぼす場合もあります。
グリーンスレッドについて
グリーンスレッドは、OSではなく、プログラムやランタイムシステムが管理するスレッドのことを指します。
これは、OSのスケジューラによるスレッドの管理ではなく、ユーザースペース(ユーザーレベルのランタイム環境)で管理されるスレッドです。
グリーンスレッドの利点は、そのオーバーヘッドが小さく、プログラムが自身のスレッドを直接管理できることから、 より効率的なスケジューリングやより高度な制御が可能であるという点です。
一方で、グリーンスレッドの欠点は、これらのスレッドが同じプロセス内で実行されるため、 通常、オペレーティングシステムのスケジューリングやマルチコアの利用が制限されるという点です。
これは、一つのグリーンスレッドがブロック(例えばI/O待ち)すると、 同じプロセス内の全てのスレッドがブロックする可能性があることを意味します。
一部のランタイムシステム(たとえば、Go言語のランタイム)では、これらの制約を克服するために、 グリーンスレッドを複数のネイティブスレッドにマッピングしているようです。
これにより、マルチコアの恩恵を受けつつも、グリーンスレッドの軽量さと効率性を保つことができます。
プログラミングで真に同時な並列処理はかけるのか
「真に同時」な並列処理を書くには、タスクがどの物理コアで実行されるかを指定する必要があります。
しかし、特定のタスクが必ず特定の物理コアで実行され、コンテキストスイッチが発生しないように保証することは、一般的な高レベル言語ではできません。
高レベル言語では、物理的なハードウェアの詳細を開発者から隠蔽します。 これにより、開発者はアプリケーションの開発に集中できますが、 その裏で、ハードウェアやオペレーティングシステム(OS)は各レイヤーで最適化するように詳細なスケジューリングやリソース管理を行います。
したがって、高レベル言語から特定の物理コアにタスクを割り当て、コンテキストスイッチを避けることは通常できません。
これを実現するには、OSのカーネル開発やデバイスドライバーの開発など、 特定の高度なシステムプログラミングで用いられる、より低レベルのプログラミングが必要になります。
プログラミングの世界は抽象化と具体化のバランスです。 高レベル言語は抽象化のレベルを高め、開発者がコードの具体的な実行について細部まで考慮する必要がありません。
一方、低レベル言語では、細部まで制御可能な具体性を提供します。
一般的な実行効率や生産性を高めるのであれば、平行か並列かはあまり意識しなくても良いのかもしれません。
しかし、CPUやOSやランタイムが並行、並列を用いて、どのように最適化しているのかを理解することは、シビアなパフォーマンスが求められる 状況下では必要な知識になってきます。
また、特殊な用途においては、この一般的な最適化が逆に高コストになることもあるので、低レベル言語で高度な最適化を 手動で行う必要や価値が出てくるのではないかと思います。
この記事を書いた人
-
2008年にアーティスへ入社。
システムエンジニアとして、SI案件のシステム開発に携わる。
その後、事業開発部の立ち上げから自社サービスの開発、保守をメインに従事。
ドメイン駆動設計(DDD)を中心にドメインを重視しながら、保守可能なソフトウェア開発を探求している。
この執筆者の最新記事
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー