💭

Day2:Ray Core の前に... ~ N日間Rayを学ぶ / Python で分散コンピューティング~

2023/08/31に公開

毎日やると言いましたが、和訳も元の英語のブログも更新できていません、、というのも体調を大きく崩しており...継続が大事ということで投げ出さず細々と続けていけたらと思います。
Day2はRay Core(分散コンピューティングを支える基礎となるAPIを提供している部分)概要に入る前の前提知識の整理がメインになります。
アプリケーションアーキテクチャの本などで基本のきとして載ってる内容なのかなとか思います。

Ray Core の前に...

1. Rayクラスターとは

Rayクラスターは、アプリケーションのプロセスを分散させたいノードのセットを指します(ベアメタルサーバー、仮想マシン、コンテナなど)。

Rayクラスターは、ヘッダーノードワーカーノードから成り立っています。

ヘッダーノードはクラスターの中に1つだけ存在し、このノードだけにドライバーと呼ばれるプロセスが存在します。ドライバーは、オートスケーラーやGCSなどのクラスター管理、およびRayのジョブを実行するプロセスを担当します。

ワーカーノードにはこのような管理プロセスは存在せず、Rayのタスクとアクター(後述)のユーザーコードを実行するだけです。ワーカーノードは分散スケジューリングに参加し、クラスターメモリ内のRayオブジェクトの格納と配布も行います。

クラスターは、rayをインポートしてたったこれだけを実行するだけで形成され、初期化されます。

import ray
ray.init()

ローカルPCを使っている場合、デフォルトでは8625ポートにダッシュボードが起動します。
公式ページにもありますが、ダッシュボードを使うにはpip install rayではなく以下のコマンドを打つ必要があります。

pip install -U "ray[default]"

# If you don't want Ray Dashboard or Cluster Launcher, install Ray with minimal dependencies instead.
# pip install -U "ray"

2. Rayの実行の基本単位

2-1. 処理の並列性と並行性

・ 並行処理(Concurrent Processing):
並行処理は、複数のタスクやプロセスを並行に実行することですが、実際に同時に実行されている状態ではありません。効率的にタスクをスケジューリングすることで、リソースの効率的な利用と処理の時間短縮を図ります。非同期通信といったときに関わりがあるのはこちらです。非同期通信は、待ち時間が発生するタスクがあった時、その時間で他のタスクを実行する、など、タスクの実行順序が保証されない場合に必要となります。

・ 並列処理(Parallel Processing):
並列処理は、複数のタスクや命令を複数のプロセッサ、コア、または計算リソースを使用して同時に実行することです。タスクを複数の小さな部分に分割して同時に処理し、性能と速度を大幅に向上させることができます。並列処理とは異なり、並行処理は真の並列実行を達成することを目指し、複数のリソースを利用してタスクをより速く完了させます。タスクを実際に並列に実行できる計算集中型のタスクによく使われます。

2-2. 関数型プログラミングとオブジェクト指向プログラミング

メインのトピックを説明する前に、関数型プログラミングとオブジェクト指向プログラミングの2つの異なるプログラミングタイプについて見直してみましょう。

並行処理を使うアプリケーションでは、可変変数に起因する競合状態、デッドロック、および同時更新の問題が発生します。関数型プログラミングでは、変数は変更されないため、これらの問題が軽減されます。

したがって、アプリケーションアーキテクチャの観点からは、関数型プログラミングが並行処理に適しています。

・ 関数型プログラミング:
関数の組み合わせとしてプログラムを見るパラダイムです。状態を持たないので、同じ入力に対して常に同じ結果を返します。まさに関数。
状態変更を避け、不変性を重視するため、副作用がなく、並列処理を扱う際にスレッドセーフな操作を実行できます。

・ オブジェクト指向プログラミング:
クラスのインスタンスであるオブジェクト同士の相互作用としてプログラムを見るパラダイムです。オブジェクトにはデータと関連するメソッド(関数)が含まれ、カプセル化や抽象化などの概念を重視します。並列処理を扱う際に、オブジェクトは内部状態を維持できるため、オブジェクト間で共有される状態を処理するために一部の同期メカニズムが必要な場合があります。

2-3. Rayにおけるタスクとアクターモデル

Rayは、タスクベースアクターベースのプログラミングの両方をサポートする統合されたプログラミングモデルを提供します。

・ タスクベースのモデル
タスクベースのモデルでは、プログラムは実行する必要のあるタスク(またはプロセス)のコレクションとして構築されます。

Rayでは、任意の関数を非同期に実行することができます。この非同期なRay関数はタスクと呼ばれます。タスクは独立しており、ステートレスであり、並列に実行できるため、計算集中型のアプリケーションやサービスのスケーリングに適しています。

・ アクターモデル
アクターモデルでは、処理の単位をアクターと呼び、それぞれのアクターはメッセージを送受信し、非同期に処理することで並行性を実現します。各アクターは独自の状態を保持し、メッセージを受け取ることで内部状態を変更でき、他のアクターとの相互作用も可能です。

簡潔に言うと、PythonとRayのコンポーネントの対応関係は以下の通りです。

Python Ray
function task
class actor

2-4. 関数をタスクに、クラスをアクターに変換する方法は?

Rayの強みの一つは、Pythonコードとの親和性です。既存のコードに@ray.remote()というデコレータを追加するだけで、関数からタスクに、またクラスからアクターに簡単に移行できます。

@ray.remote()
def FUNCTION():
    return ~

@ray.remote()
def class Class:
    ~

上のようにして宣言したアクターをインスタンス化するには、.remote() メソッドを使用します。
こうするとバックグラウンドで初期化の処理が走ります。

my_actor = MyClass.remote()

実際に何が行われるのかは、後日見ていきたいと思います。
後日触れますが、.remote()が返すのは具体的な計算結果等ではなく、未来オブジェクトとかオブジェクトリファレンスと言われるポインタみたいなもので、将来そこに結果が格納される箱の場所みたいな理解をしています。

恥ずかしながら、PythonコードのfuncclassがRayでいうtaskとactorになるよって言われただけでスッと入ってこなかったので整理してみた感じのDay2でした。

Discussion