view paper/cerium_gpu.tex @ 33:9dd4a1aa4475 default tip

final
author Nozomi Teruya <e125769@ie.u-ryukyu.ac.jp>
date Mon, 30 May 2016 23:16:41 +0900
parents 0127effb8fcd
children
line wrap: on
line source

\section{Cerium の GPGPU への対応}
本章では、まずはじめに GPU プログラミングの特徴および問題について述べ、Cerium への実装でどのように対応したかについて説明する。

\subsection{GPU プログラミングの特徴および問題}
まず Multi Core CPU に対するプログラミングと同様に性能を向上させるためには、プログラム全体を対象とした並列度を高くしなければならない。
明示的な並列化部分はループ部分である。
GPU は数百個のコアを有しており、ループ部分に対してデータ並列で処理を行うことで CPU より高速で演算を行うことができる。
プログラムの大部分がループであれば、データ並列による実行だけでプログラムの性能は向上する。
しかし、多くのプログラムはその限りではない。
GPGPU においてネックになる部分はデータ転送である。
GPU の Memory 空間(図:\ref{fig:gpuarch})は CPU(図:\ref{fig:cpuarch}) とは異なり、Shared Memory ではないため host と device 間でデータの共有ができない。
データにアクセスするためには Memory 空間ごとコピーするしかない。
これが大きなオーバーヘッドになるので、データ転送をオーバーラップする必要がある。
今回新たに、データ転送を自動でオーバーラップするように OpenCL および CUDA を用い Scheduler を実装した。

\begin{figure}[htpd]
  \begin{center}
    \includegraphics[scale=0.35]{./images/gpu_arch.pdf}
  \end{center}
  \caption{Gpu Architecture}
  \label{fig:gpuarch}
\end{figure}

\begin{figure}[htpd]
  \begin{center}
    \includegraphics[scale=0.7]{./images/cpu_arch.pdf}
  \end{center}
  \caption{Cpu Architecture}
  \label{fig:cpuarch}
\end{figure}

\subsection{OpenCL および CUDA を用いた Scheduler の実装}
Scheduler と CpuThreads に対応させる形で OpenCL を用いた GpuScheduler, GpuThreads、CUDA を用いた CudaScheduler, CudaThreads を実装した。
TaskManager から転送された TaskList の情報をもとに device 上のメモリ領域を確保する。
その後、OpenCL ならば CommandQueue、CUDA ならば Stream に Operation を発行していく。
Operation は発行された順序で実行されるので、host から device へのデータ転送、kernel の実行、device から host へのデータ転送の順に発行する。
非同期 API を用いることでデータ転送や kernel の実行を並列に行うことができる。
通常、非同期 API を用いる場合は依存関係を考慮した同期が必要になるが転送されてくる Task の依存関係は TaskManager ですべて解消されているので Scheduler 側では順番を考えず Task を実行して問題ない。
host から device へのデータ転送は、OpenCL では clEnqueueWriteBuffer、CUDA では cuMempcyHtoDAsync を用いて行われる。
clEnqueueWriteBuffer は第三引数に CL\_FALSE を指定することで非同期なデータ転送を行う。
転送されてきた TaskList からデータ並列またはタスク並列で実行するか決定する。
データ並列で実行する場合は、OpenCL では clEnqueueTaskNDRangeKernel、CUDA では cuLaunchKernel を用いる。
タスク並列で実行する場合は、OpenCL では clEnqueueTask、CUDA では cuLaunckKernel の引数を1に設定することで実行することができる。
device から host へのデータ転送は、OpenCL では clEnqueuReadBuffer、CUDA では cuMemcpyDtoHAsync を用いて行われる。
clEnqueueReadBuffer も clEnqueueWriteBuffer と同様に第三引数に CL\_FALSE を指定することで非同期実行となる。
転送されてきた Task がすべて終了すると Synchronized Queue である mail を通して TaskManager に Task の終了を通知する。
終了が通知されると TaskManager で依存関係が解消し、再び TaskList を転送する。
GpuScheduler および CudaScheduler は複数の CommandQueue および Stream を持っており、パイプラインで実行される。

kernel の記述は以下のようになる。
\lstinputlisting[caption=multiply(OpenCL),label=test]{./source/Multi.cl}
\lstinputlisting[caption=multiply(CUDA),label=test]{./source/Multiply.cu}

修飾子など若干の違いはあるが、ほぼ同じ記述で書くことができるが CPU, OpenCL, CUDA のどれか1つの記述から残りのコードも生成できるようにすることが望ましい。