view paper/chapter4.tex @ 77:f9b73e12a52f

fix
author Yuhi TOMARI <yuhi@cr.ie.u-ryukyu.ac.jp>
date Mon, 23 Feb 2015 19:12:19 +0900
parents 7956856211c5
children
line wrap: on
line source

\chapter{マルチコアへの対応}
Cerium は Cell 上で並列に動作するフレームワークであった\cite{yutaka:2012a}が、
 Mac OS X 、 Linux 上でも並列に実行させることを可能にした。\cite{daichi:2012a}

\section{マルチコア上での実行の機構}
Cell には MailBox という機能がある。
Cell は Shared Memory でないので、 Memory に直接アクセスできない。
そこで MailBox を用いて双方向のデータの受け渡しを可能にしている。
 MailBox は FIFO キュー構造になっており、Device と Host で
この MailBox に対応させる形で Synchronized Queue を用いて MacOSX 、
Linux 用の TaskManager へ MailBox を移植した。

Syncronized Queue はバイナリセマフォで管理されており、Queue を操作しているスレッドは常に一つになる。
各スレッドは Input 用と Output 用として Synchronized Queue を2つ持っており、管理スレッドから Task を受けて
並列に実行する。

\section{DMA}
Cell ではデータの受け渡しとして MailBox 以外に DMA 転送を使用する方法がある。
CPU を介さずに周辺装置とメモリ間でデータ転送を行う方式である。

Cerium も DMA 転送を用いている箇所がある。
しかし現在主流のアーキテクチャでは DMA をサポートしていない。
マルチコア CPU 上で実行する場合は各 CPU で同じメモリ空間を利用できるため、
 DMA 転送を用いていた部分をポインタ渡しを行うように修正した。
メモリに直接アクセスさせることで速度の向上が見込める。

更に、Cerium には prefetch 機能がある。
データの転送効率を向上させるため、転送する送信データを予め取り込むことができる。
Cerium ではprefetch、prefetch を用いないポインタ渡し、更に明示的なコピーによるデータ転送をサポートする。

\section{データ並列}
\label{sec:multicore_dataparallel}
並列プログラミングを行う際、並列化の方式としてタスク並列とデータ並列の2つがある。
Cerium における並列処理は、タスク並列により実現されている。

タスク並列は1つのデータに対して異なる処理方法を適用し、それぞれ独立して実行させるものである。
一方で1つの Task に対して多くのデータを与え、データごとに独立した処理を行わせる事をデータ並列という。
プログラムを並列化する際はどちらの並列化手法が適しているか考える必要がある。
独立した Task が充分にある場合はタスク並列が有効となる。
処理対象となるデータが充分な数のサブデータへ分割することが可能で、
サブデータへの処理を行うことが元のデータへの処理と同じになる場合、データ並列が有効となる。

本研究で Cerium でデータ並列による実行を可能にした\cite{yuhi:2013a}。
Cerium でデータ並列実行を行う場合、Task を spwan API でなく iterate API で生成すればよい。
iterate API は複数の length を引数とし、
length の値がデータ分割後に各 Task が担当するサイズ、length の個数がデータの次元数となる。
これを元にScheduler が各 Task が担当する index を計算し、Task に set\_param する。

index の割り当ての例を表:\ref{table:dataparallel_index}に示す。
データ数10個の入力を持つ Task に対して CPU 数4、
一次元における分割でデータ並列実行した場合の index の割り当ては表:\ref{table:dataparallel_index}になる。

この例だと各 CPU に対する index の割り当ては CPU0 は index 0、4、8、 CPU1 は index 1、5、9、
CPU2 は index 2、6、CPU3 は index 3、7となる。

\begin{tiny}
  \begin{table}[htpb]
    \begin{center}
      \small
      \begin{tabular}[htpb]{c||c|c|c|c}
        \hline
        stage & CPU0 & CPU1 & CPU2 & CPU3 \\
        \hline
        \hline
        1 & 0 & 1 & 2 & 3 \\
        \hline
        2 & 4 & 5 & 6 & 7 \\
        \hline
        3 & 8 & 9 &   &   \\
        \hline
      \end{tabular}
      \caption{データ並列実行時の index の割り当て}
      \label{table:dataparallel_index}
    \end{center}
  \end{table}
\end{tiny}

並列プログラミングだと、並列化部分が全て同一の Task であるということは少なくない。
その際、 Task 生成部分をループで回すことなく、iterate API により簡単な Syntax で記述することができる。

マルチコア CPU 上でデータ並列実行する場合、ソースコード:\ref{src:multiply_cpu}のように Task を記述する。
なお、2つの input データの積を output データに格納する例題、 multiply を用いた。

\begin{lstlisting}[frame=lrbt,label=src:multiply_cpu, caption=Multiply(CPU),numbers=left]
static int 
run(SchedTask *s, void *rbuf, void *wbuf) {
    float *indata1, *indata2, *outdata;

    indata1 = (float*)s->get_input(rbuf, 0);
    indata2 = (float*)s->get_input(rbuf, 0);
    outdata = (float*)s->get_output(wbuf, 0);

    long id = (long)s->get_param(0);
    outdata[id] = indata1[id] * indata2[id];
    return 0;
}
\end{lstlisting}

Task 間で共有する Input/Output データと自分が計算を行う index は Scheduler により送られてきている。
get\_input 、get\_output API を用いて Input/Output データを取得し、get\_param API で担当する index を取得する。
後は各自担当範囲に対して計算を行うだけでよい。

データ並列で実行する場合、1つの Input と Output を各 Task 間で共有し、
各 Task は自分が担当する index に対してのみ計算を行うため、少ないコピーに抑えられる。