view cerium-manager.tex @ 13:a91e95964045

*** empty log message ***
author gongo
date Thu, 27 Mar 2008 00:48:54 +0900
parents fea1ac32de27
children 091d888f7729
line wrap: on
line source

\subsection{Task Manager}
Task Manager は、Task と呼ばれる分割された各プログラムを
管理するライブラリである。Task 同士の依存関係を考慮しながら
メモリ上にマッピングし、SPE 上ではそのプログラムを
DMA 転送によりロードする (\figref{fig-manager-load}) 。
SPE は 256KB という小さなデータ量しか持てず、
大量のプログラムを全て SPE 上に置いておくことはできない可能性がある。
そのため、必要な時に必要な実行プログラムだけが SPE 上にロード
されていることが望ましい。

\begin{figure}[tb]
  \begin{center}
    \includegraphics[scale=0.45]{figure/manager-load.pdf}
    \caption{Task Manager}
    \label{fig-manager-load}
  \end{center}
\end{figure}

現在実装されている Task Manager の API を \tabref{tab:manager-api} に示す。

\begin{table}[htbp]
  \caption{Task Manager API} \label{tab:manager-api}
  \hbox to\hsize{\hfil
  \begin{tabular}{r|l} \hline \hline
    create\_task & Task を生成する \\ \hline
    spawn        & Task を実行 Task Queue に登録する \\ \hline
    set\_depend  & Task の依存関係の考慮 \\\hline
    set\_cpu     & Task を実行する CPU の設定 \\ \hline
    run          & 実行 Task Queue の実行 \\ \hline
  \end{tabular}\hfil}
\end{table}


以下に Task Manager を使った記述例を示す。

{\small
\begin{verbatim}

int
main(void)
{
    TaskManager *manager = new TaskManager;
    Task *task1, *task2;

    /**
     * cmd  : 実行するタスクID
     * size : in_addr で取得するデータのバイト数
     * in_addr  : 入力データ元アドレス
     * out_addr : 出力データ先アドレス
     */
    task1 = create_task(CMD_RUN1, size1,
                        in_addr1, out_addr2);
    task2 = create_task(CMD_RUN2, size2,
                        in_addr2, out_addr2);

    // task2 は task1 が終了してから開始する
    task2->set_depend(task1);

    task1->spawn();
    task2->spawn();

    manager->run();

    return 0;
}

\end{verbatim}
}


\subsubsection{Task の定義}
タスクの定義を以下に示す。

{\small
\begin{verbatim}

    class Task {
    public:
        int command;
        int size;
        unsigned int in_addr;
        unsigned int out_addr;
        struct task *self;
    };

    class HTask : public Task {
    public:
        TaskQueue wait_me;
        TaskQueue wait_i;
        CPU_TYPE  cpu_type;
    };

\end{verbatim}
}

Task クラスは SPE が実行するタスクの単位オブジェクトである。
SPE はメインメモリの \verb|in_addr| から DMA で入力データを取得し、
command に対応するコードを実行し、
結果を メインメモリの \verb|out_addr| に DMA で送信する。
これらの処理はパイプラインに沿って動作する (\ref{sec:pipeline}) 。

HTask クラスは TaskManager で管理する実行前のタスクオブジェクトである。
\verb|wait_me, wait_i| はタスク依存の条件 (\ref{sec:task}) に、
\verb|cpu_type| は PPE と SPE のタスクの切り替え (\ref{sec:change}) に用いる。


\subsubsection{スケジューラ} \label{sec:pipeline}
Cell ではそれぞれのコアがメインメモリを
直接参照することは出来ず、DMA 転送によりデータをやりとりする。
DMA は CPU を介さず直接データ転送を行う方式である。
SPE は DMA 完了を待たずに他の処理を行うことが出来るので、
DMA のレイテンシを隠すことが出来る。

また、ダブルバッファリングを行うことで
パイプライン処理が可能となる (\figref{fig-pipeline}) 。

\begin{figure}[tb]
  \begin{center}
    \includegraphics[scale=0.43]{figure/pipeline.pdf}
    \caption{Pipeline}
    \label{fig-pipeline}
  \end{center}
\end{figure}

パイプライン処理を入れた Task Manager の Scheduler を以下に示す。

\begin{verbatim}

    do {
        task3->write();
        task2->exec();
        task1->read();

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

\end{verbatim}

Task Manager を \figref{fig-cerium} に適用させると、
\figref{fig-manager-pipeline} のようにパイプライン的に動作する。

\begin{figure}[tb]
  \begin{center}
    \includegraphics[scale=0.36]{figure/manager-pipeline.pdf}
    \caption{Task Manager が行う Pipeline}
    \label{fig-manager-pipeline}
  \end{center}
\end{figure}

\subsubsection{Dependency}  \label{sec:task}
Task Manager はタスク依存を解決する機能を持っている。
以下は記述例である。

\begin{verbatim}

    // task2 は task1 が終了してから開始する
    task2->set_depend(task1);

\end{verbatim}

タスク依存が満たされたものをアクティブキューにいれ、SPE を起動する。
SPE はアクティブキューから、処理するコードとデータを取得し、
自律的に実行を行う。
終了したタスクは PPE に対して終了のコマンドを発行し、PPE はそれを見て
ウェイトキューのタスクを調べ、タスク依存を満たしたものが出てきたら
アクティブキューに入れ、SPE を再び起動する。

\subsubsection{PPE コードと SPE コードの互換性} \label{sec:change}
Cerium では、 SPE を用いる Cell 上でのみ動作するプログラムの他に、
Linux や Mac OS X などで動く、アーキテクチャに依存しないプログラムを
記述することが可能である。
依存しないプログラムの場合、スケジューラはもちろんメインメモリ上で
動作する。パイプライン処理は SPE と

{\small
\begin{verbatim}

  task1->set_cpu(CPU_PPE); // PPE 上で実行される
  task2->set_cpu(CPU_SPE); // SPE 上で実行される

\end{verbatim}
}

\subsubsection{PPE と SPE 間の同期}
\ref{sec:task} で述べたように、PPE から SPE へはタスクの実行命令を、
SPE から PPE へはタスクの終了などを伝える必要がある。
その際、待ち合わせを行うと処理が止まってしまい、並列度が下がってしまう。

Cell では、PPE と SPE 間のメッセージのやりとりには
Mailbox という FIFO メッセージキューを用いることができる。
メッセージ交換なので待ち合わせを避けることが可能である。

%%PPE が SPE からメールを受け取る場合、
%%\verb|spe_out_mbox_read()| という、
%%SPE Runtime Management Library \cite{libspe2} を用いる。
%%しかし、\verb|spe_out_mbox_read()| は Non-blocking function のため、
%%busy-wait なメールの待ち方は避けるべきである。
%%今回は、PPE 側でメールチェックを行う際に、SPE からのメールをチェックする。
%%
%%PPE と SPE のメールチェックを分離させたい場合、
%%メールをチェックする Blocking Function を作る必要がある。
%%
%%もしくは SPE Event Handling を用いる手法もある。
%%これは、メールが来たら event を起こすことを Event Handler に登録する。
%%この場合、通常の Mailbox ではない、割り込み用の interrupting Mailbox を
%%使用しなくてはならない。
%%interrupting Mailbox を使用する spe\_out\_intr\_mbox\_read() は、
%%Event が起きたときに呼ばれるため Blocking Function である。
%%SPE のどれかからメッセージが来たらポーリングによってメールチェックを行う。
%%
%%今回は、PPE と SPE のメールチェックは分離しない実装をした。
%%