view 7.tex @ 0:a9fda18657b3 default tip

add
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Wed, 16 Dec 2009 10:05:04 +0900
parents
children
line wrap: on
line source

\section{ Cerium Engine}

Cerium Engine は、PS3 上のScene Graphの描画と変更によって
記述されたゲームを実行する Engine である。Task の生成を
管理する TaskManager, Task をScheduleする Scheduler,
ソフト的なキャッシュを管理する MemeorySegment そして、
SceneGraph ライブラリ、 複数の Task で実行される
Rendering Engine からなる。

C++ で記述されており、Mac OS X, Linux, PS3 上で同一の
ソースで動作する。

SceneGraph は、Blender (Open Source な3D modeling Tool)
から、Python Script を作って生成された XML である。
内部でも生成変更可能である。

SceneGraphのパラメータを変更することによりゲームが
進行し、それと同時に Pipeline 的に SceneGraphの描画を
行う。

Cerium Engine は、
Open CL とは独立に設計しているが、結果的には似たものになっている。

\subsection{ Task Manager}

Cerium の Task は、型のない入力と出力を持つシンプルな関数である。
入力と出力は、Cell のDMAによって用意される。List DMAがあるので、
断片化されたデータを読み書きすることも可能になっている。

create\_task() で、HTask という構造を作成し、spawn()することで
active queue に登録する。

Task は、番号で登録される。SPE と PPE では独立な番号を用いている。
手動による Overlay で、SPE 上のコードの入れ換えを行っているので、
ポインタで指定することは出来ない。Open/CL では、文字列で指定した
プログラムを llvm に引き渡す形式だが、その点は異なる。

Cerium は、PPEのTaskと、SPEのTaskの二種類を持っており、それぞれ
別なキューで管理されている。さらに、Task の終了時に呼ばれる
Task がある。

PPE Task は相互のDependencyを持ち、Dependency が満たされた時点で
PPEまたはSPE上で実行される。

普通のOSと異なり、高度なスケジューリング
は行われない。Task は十分小さく、基本的に preempt されずに
実行されるので、Round Robin などの工夫は不要である。通常の
スレッドやプロセスとは異なる。

SPE上には最小限のSchedulerが存在している。SPE上では基本的にTask
は生成しない。

SPE Task からはメインメモリは参照できないが、MemHash というLRU
キャッシュが実装されている。しかし、特に構文的な制限があるわけではな
く、PPE Task からはメインメモリ全域をアクセスすることが出来る。

一つのPPE Taskから生成されたSPE/PPE Taskは、まとめて投入される。
投入した Task は Cell の Mail を使って、SPE/PPE スケジューラに
伝えられる。終了した Task は、Dependency を解消し、Active に
なった Task を Active Queue に移動する。

DMAは、Taskの中から起動することも可能になっている。PPE や、Mac OS X
上でも、DMAのEmulationを行っており、同じコードで、PS3、Mac OS X
で動作させることが出来る。

HTask は、実行時は SchedTask と言う型のオブジェクトになる。
Cerium Engine に対する処理は、SchedTask へのメソッドで行われる。

\subsection{ Task 間の同期}

Task はメインメモリ上にQueueを持っており、

{\small
\begin{verbatim}
   task->wait_for(another_task)

\end{verbatim}
}

という形で待ち合わせを行う。これは、task queue にwaiting queue
を持つこととで実現されている。実際の実行は、Core 上で行われる。
Core から見たメインメモリはかなり遠いので注意が必要である。

{\small
\begin{verbatim}
   task->set_post(post_task,in, out)

\end{verbatim}
}

という形で、Taskの継続を指定することが出来る。継続のTaskでは、
DMA は行われない。主に、終ったタスクが書き出したデータの整理、
次のタスクの起動などに使われる。スケジューラの前に起動される
ので、継続で接続された Task のチェーンは、見掛け上、シングル
スレッドで実行される。

何もしないDummy taskを作り、それをwait\_for することにより、
同期を取るような手法が多用されることになる。これは、一種の
バリアである。

taskの終了は Mail による通知であり、Singe Thread なTask Manager
がMail 待ちループを持っていて処理する。

{\small
\begin{verbatim}
    do {
        ppeManager->schedule(ppeTaskList);
        ppeTaskList = mail_check(waitTaskQueue);
    } while (ppeTaskList || spe_running >0);


\end{verbatim}
}

つまり、wait\_for/set\_post はメインメモリ上の
シングルスレッド Task として実行される。Task Manager を
複数のスレッド、あるいは分散したプロセスとして実装することも
可能だと思われるが、それが、wait\_for/set\_post の意味に
どう係わるかは、かなり難しいと思われる。現状では、シングル
スレッドが使いやすい。

\subsection{ Task}

一つの Task は、read/exec/write の三つに分解されて、
それぞれが、さらにパイプライン的に実行される。TaskManager
は、それが可能なように、Core に複数のTaskの集合(TaskList)
を投入する。

{\small
\begin{verbatim}
    void
    Scheduler::run()
    {
        task1 = new SchedNop();
        task2 = new SchedNop();
        task3 = new SchedNop();
        // main loop
        do {
            task3->write();
            task2->exec();
            task1->read();
            delete task3; 

            task3 = task2;
            task2 = task1;
            task1 = task1->next(this, 0);
        } while (task1);
        delete task3;
        delete task2;
    }

\end{verbatim}
}

ここで、read/exec/write は Task の状態によって変わる
ステートパターン用の仮想関数である。何もしないもの、
DMAの開始/待ち合わせ、次のタスクの取得、次の TaskList
の取得などを行う。つまり、read/write は非同期で、
ほとんど待ち時間はないと想定されている。exec が
実際のユーザが定義したTaskの関数を呼び出す。

TaskList は、Core 側がEmptyになったのをMailでMain側に
知らせて、Main側から Mail によりTaskListへのポインタ
を取得して、DMAにより転送を行う。これは一つのTask
として実装されていて、上のパイプラインループの中で
実行される。


\subsection{ Memory Segment Manager }

PPE上/SPE上のメモリは、MemorySegment によって管理されている。
これは、Hash access 可能な double linked されたメモリ領域
である。

これは、set\_global, get\_global によって、Core(SPE)上に
常駐する領域である。これは明示的に作成削除する必要がある。

Cerium Engine の性質上、ほとんどの処理は Pipeline Buffer 上で
行われる。つまり、 入力 MemorySegment から出力MemorySegment
に書き出される Task の集合である。

MemorySegment は読むだけ、あるいは書くだけとなる。そうでないと、
Pipeline 実行時に途中でデータを変更されてしまうことになる。

Taskの実行時には、必ずメモリのコピーを伴うし、DMAのオーバヘッド
のほとんどは隠されてしまうので、積極的にコピーする。メインメモリ
は、SPEのLSに比べれば広大なので、倍量のメモリを取っても問題ない。
Texture や Polygon のデータ等、変更されない場合はコピーの必要はない。

必ず Copy を伴うので、細かく解放する必要がない。したがって、
常に Copying GC を行っているようなことになる。これは、Pool を
多用する Apache Web Server と同じような実装となる。