高柳亘 脇田建

1　はじめに
近年GPUを汎用計算に利用するGeneral Purpose computing on GPU (GPGPU)という並列処理技術は，いくつか事例において優れた計算性能を示している．GPUはプロセッサの性能がそのダイサイズの平方根に比例するというポラックの法則に対し、画像データ処理のためのあまり高い性能を持たない小さな演算装置を大量に使用した並列処理を行うことで,並列性と処理性能を近似的に比例させている.しかしアムダールの法則より並列処理においては,シングルスレッドによる逐次処理のようにプロセッサの進化につれて同じプログラム処理が高速になるとは限らないことが示されている。そのため並列処理においては，結果の正しさを保証するための同期排他制御の適切な実行や，優れた処理性能のために負荷分散やデータの配置を考慮し，計算プロセッサの特徴に応じてアルゴリズムの拡張や移植を行うなど，複雑なプログラミングを行うことが求められる[15].  GPGPUプログラミングにおいても同様であり，SIMD処理形式を持つGPUアーキテクチャに対応した並列処理の実装を行わなければならない。 CUDA[13]やOpenCL [11]のようなGPGPUのための並列処理言語を利用すれば簡単にGPGPUプログラミングを行うことができるが、正しさや処理性能のためにプログラマによる調整が必要であるため，GPGPUは歌居の高い技術であるといえる.本研究の主題は，GPGPU上でアクターモデルに基づいた計算を実行することである。アクターモデル[9] [1]は，並列分散環境でのプログラミングにおいて広く用いられるメッセージパッシング方式に基づく理論的，実践的で単純なモデルである．競合状態を回避する仕組みを備えており，アクターモデルに従うことでプログラマは並列プログラミングで必要となる同期などを考慮しないプログラミングが可能となり，またモデル導入により加算される実質的なオーバーヘッドは，メッセージパッシング機構によるもののみであるため，性能面での損失は小さい。アクターと呼ばれる動的オプジェクトと，その間でのメッセージ送受信のみで構成される単純なモデルであるため，拡張性,移植性も高く，プログラマからもプログラムの構造を把握しやすい。GPUアーキテクチャ上ヘアクターモデルを移植し，簡単な記述でアクター計算を実現できれば，アクターモデルの有益な性質とGPUの優れた計算性能を結合させた，容易で高性能な結果をもたらすプログラミングが可能になると考えている。またGPUが得意とするデータ並列処理以外のアプリケーションの実装を簡単にし，GPGPUプログラミングの汎用性を高めることも狙っている.我々は以前よりGPGPUプログラミングの汎用性や記述性の向上を目的とした上で，処理系の実装を行なってきた[19][18].しかし仕様に未熟な部分が多く,満足な結果を得ることはできなかった．本稿では以前のシステムに新たな発想を加えた新システムに関して，アクターを用いたGPGPUプログラミングを可能とするActGPU言語の紹介とそれを実現するために行ったGPUアーキテクチャとアクターモデルの対応付け，ActGPUコードからCUDAコードへの変換について論じる。本騎執筆時において新システムは安定した動作をしていないため本稿では正確な評価ができていないが，以前のシステムと比較してよりC++互換な言語として実装の自由度を上げたことで記述性が向上し，並列処理部分を増やしたことでより高速な処理が可能になると考えている.本稿の構成は以下のとおりである。第２節では前提知識となるアクターモデルやCUDA (GPU)についての簡単な解説を行う。第３節では，ActGPUの仕様について説明を行い，プログラマから見たAct- GPUプログラム処理について述べる．第４節ではアクターモデルとGPUアーキテクチャの対応付けと, GPU上でのアクター計算の実行方法について，そしてActGPUコードからCUDAコードへの変換について説明する。第５節ではプログラムの記述性，処理性能の観点から新システムについて考察を行う．第6節では本研究の関連研究を紹介する。第７節ではまとめと今後の課題について述べる.
2アクターモデルとGPGPU
この節では，アクターモデル[1][10][2]，CUDA[13]についての概要をまとめる．CUDAについては， NVIDIA GPUに関しても述べる．なお本節では実装を意識した簡単な説明を行う。より詳細な説明は付録と参考文献を参照していただきたい。
2.1アクターモデル
アクターモデルは動的オプジェクトのアクターを用いた計算モデルであり，非同期メッセージパッシングにより相互に情報交換をしながら，並列に計算を実行する。この項ではアクターモデルを構成するアーキテクチャについて簡単に述べた後，メッセージパッシングを用いたプログラムの処理手順について説明する.アクターモデルにはアクターとメッセージのみが存在する.アクターやメッセージはそれぞれに数値などの情報を持つ.アクターは他のアクターのアドレスを保存しており，知っているアクターに対してのみメッセージを送信する。アクターの持つ情報（データや知り合いアクターの場所）については，メッセージで伝達する手段のみで他のアクターへ知らせることができる。以上よりアクターモデルを用いたプログラムでは，個々に情報隠蔽されたアクターが知り合い関係によって辺で繋がれている網を構成しているように表現できる.アクターはメッセージ交換によって，個々の情報を交換しながら計算を行う。受け取ったメッセージに応じて，そのメッセージを処理する方法も決まっており，これをアクターの振舞いという．振舞いは単純に，新たなアクターの生成，新たなメッセージの送信，そのアクターが次に実行する振舞いを定義することの三つのみを実行する。簡単に表現すると，アクターはメッセージによって受け取ったデータにより,自身の持つ情報を更新し，さらに他のアクターヘメッセージを送信したり，新しいアクターを作って情報を伝撒することによりプログラム全体の計算を実行する。なおアクター間のメッセージ送信は非同期に行われ，メッセージに対応した振舞いを実行するタイミングはアクターごとに不定である．メッセージは一般的にキューによって保存され，順番は問わないものの，すべてのメッセージは必ず処理される.計算の中で守らなければならないこととして，アクターの原子性が存在する。これはアクター内での競合状態を発生させないように，アクターの振舞いを逐次的に実行する特徴である．状態（データ）の更新が逐次的に実行されることにより，ここに競合は発生しなくなる。アクター計算はアクターの情報隠蔽性と,このアクターの原子性によって競合状態の発生しない正確な計算結果をもたらすことができる.
2.2 CUDA
GPUと，GPUを汎用計算に用いるGPGPUという技術を実現する言語環境CUDAについて簡単に説明する．特にここでは実装を含めた機能面を中心的に述べる。なお本研究ではCUDAを用いるため，扱うGPUはNVIDIA社製のGPUということに注意していただきたい. GPUにはStreaming Processor (SP)という命令実行単位が多数存在する．その上位にSPをまとめたStreaming Multiprocessor (SM)というものがあるが，基本的にはこのSPが並列に処理を実行することによって並列な処理を実行する。これに対し，CUDAにはスレッドという計算単位がある。上位にブロック，グリッドという概念があるが，スレッドごとに一意なIDを利用した同一配列の異なる位置へのアクセスによって並列な処理を実行するのが基本である.これらの間の対応関係は，スレッドの塊であるプロックをSMに割り当て、SMの中のSP一つが複数のスレッドに対応する。特に本質的な並列処理はSM単位で発生するため，なるべく多くのプロックを利用するプログラムを実装した方がより処理の並列性は向上する.ただし，実際の処理性能には並列性のみが関連するわけではない．基本的なこととして，GPU上のメモリにはグローバルメモリと呼ばれるGPU上どこからでも見えるがメモリアクセスが低速なメモリ，シェアードメモリと呼ばれる同一プロックからしか見えないが高速に読み書きできるメモリ，コンスタントメモリと呼ばれる読み込み専用だがグローバルメモリと同様のスレッドから見える高速なメモリなど極類があり，利用するメモリによって処理速度は変化する．またCPUとGPUのメモリは物理的に離れており，かつGPUメモリを直接触ることはできないため， CPU-GPU間のデータ転送が発生し，このデータ転送にも時間がかかる。さらにGPUメモリと処理を実行するSPとの間のメモリ転送にも時間を要する．その他GPUならではの特性が存在するため，プログラム実装において注意を必要とする点は多い。並列性も含めて総合的にGPUの特性に合った実装を行わなければ，GPUを用いた高速な処理は実現できない．性能劣化を引き起こす可能性のある特徴として, branch divergenceやcoalesced memory accessがある．branch divergenceとは，実際のCUDAスレッドの処理は連続したIDを持つ32スレッドを1グループ（ワープという）とし，ワープ単位で同一命令を実行するGPUの特徴により発生するオーバーヘッドである。これは同一ワープのスレッドが条件分岐などでそれぞれに異なる命令を実行する場合，他方向へ分岐するスレッドは待機するという形で，分岐先のすべての処理を実行しなければならなくなる．coalesced memory accessとは，メモリアクセスの結合のことである．これに反し，スレッドIDとアクセスするメモリアドレスが不連続な場合に負荷が発生する．特に連続したIDを持つスレッドが連続したメモリ番地ヘアクセスすることで，高速なデータ転送が実行である.その他並列処理では競合状態が発生することによりデータが破壊される恐れがあり、それらを避けるための同期や排他制御のための機能がCUDAではAPIとして与えられている。これらを適切に利用することにより，正確な解を導き出す必要もある． GPUによる処理は，__global__指示詞をつけたカーネル関数として定義する．前述したGPUの特徴に従ったコードは，このカーネル関数内に書く．GPUを用いたプログラムの実装は，グローバルメモリやコンスタントメモリの領域確保と初期化，使用するプロック数とスレッド数を指定を行い，その後でカーネル関数を呼び出す形で行う。
2.3アクターモデルとCUDAの相違点
アクターモデルとGPU計算モデルの間には相違点が存在する。第一にアクターモデルではアクターが計算主体であることに対し，CUDAではスレッドが計算主体である。これらの計算方法の違いにより，以下のような相違点がある。
並列制御における非同期と同期の違いは大きなものである。アクターモデルはメッセージパッシングと原子性により，非同期処理でも競合を回避することができる.対してCUDAの処理は共有メモリ上で処理を行い、正確な解を出力するために同期を処理の中に挿入する。これに応じて計算の性質も異なっており，アクター計算ではタスク並列な処理を，GPUではデータ並列な処理を得意としている.それぞれ基本的なデータ構造が構造体と配列で異なっているのも,この並列制御の相違より表されている.またアクターモデルが理論であることに対し、 CUDAによる計算はGPUを対象としているため,想定するデータ領域の大きさが異なっている．アクターモデルではアクターやメッセージの数に制限はなく，またそれぞれの持つ情報に関しても同様である．GPUは物理的にメモリの量が決まっているため， CUDAでは計算の前にGPUメモリ領域の確保を行う必要があり，そのため計算できる問題の大きさに制限がある。
3　ActGPUシステム
本節では我々が制作したActGPU言語について論説する.  ActGPU言語とは，アクターモデルに基づいたオプジェクト指向言語であり，記述されたプログラムはCPU-GPU上で動作する。ここではActGPU言語の概要と，実際の実行の様子を示す
3.1    ActGPU言語
まず第一に，ActGPU言語はアクターモデルに基づいた実装によって，非同期並列なアクター計算をGPUの特徴を考慮せずに実装することを可能とする言語である．文法としてはC++を基本構造としており，その中にアクターを用いるための独自の記述を加える事によってGPUを用いたアクター計算が可能となる。アクターによる計算方法に従うことのみを要求し，利用者は一般的なCUDAプログラミングに従っ
図１　ActGPU言語でのフィポナッチ数の計算.
た記述をする必要が無い。この項ではActGPU言語において、アクタープログラムを実装する方法について述べる.前節で述べたように，アクターモデルにはアーキテクチャとしてアクターとメッセージのみが存在する。計算はメッセージに応じたアクターごとの振舞いによって，情報を更新しながら新たなメッセージを他のアクターへ送信することで実行される。 ActGPU言語はアクターモデルに基づいた文法を持つため，アクターやメッセージを示すもの，アクターの振舞いを示すものをコードの中で定義する必要がある.図1にActGPU言語を用いてフィポナッチ数の計算を実行するコードを示す。この例を参考にしながら，ActGPU言語によるアクター計算の構成方法について述べる.アクターはC++のクラスと類似した定義を行うことで，利用可能となる．図１の１行目にあるようにactor指示詞をつけた構造体としてアクターを定義する.アクター構造体の固有変数はアクターの持つデータを表現する。これらのデータは本来アクターとして定義され，中心となるアクターの隣接アクターとして表現されることもあるが，ActGPU言語ではGPUでの処理を考慮し，アクターが個々に持つデータとして表現した．この中で扱える固有変数現在のところint,  float,  actorの変数,配列, ポインタのみとなっている.アクター構造体の固有関数は，アクターの振舞いを表現する。そして固有関数への引数の集合が，メッセージとして扱われる。メッセージも本来アクターとして扱われるが，ActGPUでは明確なオプジェクトとしての定義はせず，さらに固有関数を呼び出すことをメッセージの送信として扱う．図1の26行目における，アクター rのvalueメソッドを実行することが，ヶに向かつてvalueに記述された振舞いを実行させるメッセージを送信することに相当する．固有関数内で扱える変数はアクターの固有変数と同じでint, float,  actorの変数,配列,ポインタである.また実際に実行できる計算は四則演算やシフト演算，さらにif文などの条件分岐，for文などのループ処理などを用いることができる。さらにnewを用いて新たなアクターの生成，deleteを用いてアクターの削除が実行できる．詳しくは図2を参照していただきたい．ただし例外として，コンストラクタはアクターの振舞いとしては扱われない。コンストラクタの中ではアクターの固有変数の初期化のみを許し，その他の処理を実行させる場合は新たに他の関数を呼び出し、メッセージ送信を発生させなければならない．理由については4.2項で述べる．図1のFibonacciコンストラクタとnext関数は44〜45行目を参照すると,本来一つのコードとしてまとめられるが，この仕様のため分離させている。なお，このアクター構造体における固有変数はpri- vate,固有関数はpublicアクセスである．前者はアクターの情報隠蔽性を，後者はメッセージ送信を表現しているためである.余談であるが，ActGPU言語ではactor指示詞を用いずに実装を行うこともできる．この場合はアクター計算を実行しないことを表し，一般的なC++のプログラムとしてコンパイル，実行されることとなる.
3.2   CPU-GPUでの処理
ActGPU言語で記述したプログラムがCPU-GPU環境でどのように動作するかについて簡潔に述べる. GPU上でのより詳細な寒行については，次節で述べる.プログラムで生成したアクターは基本的にはGPU上に配置される。そして，CUDAスレッド一つが一つのメッセージに対応したアクターの振舞いを実行する。これによりアクターの処理は並列に実行される. CPUは初期アクターの設定と，そのアクターへのメッセージ送信を実行することでアクター計算の実行を開始させる.図１の５3行目にあるようにアクターの固有関数外でアクターを生成し，メッセージ送信を実行する。なおアクター計算が終了した時点で，すべてのデータをCPU側にまとめる．GPU上に存在するアクターと同等のアクターをCPU上に配置し,計算の終了時にはCPU側のアクターにすべてのデータをまとめる処理を実行する.これはユーザからアクターの情報と計算結果を見えるようにするためである.現在実装中であるが，GPUだけでなくCPUでもアクター計算を実行させることも考えている．上記のようにCPUが初期アクターの生成のみを行う形では，CPUの仕事が少なく無駄になってしまい，また
園２　ActGPUの簡易文法. 演算子の優先度は考慮していない.
GPU側のみで計算を実行すると最終的な計算結果をユーザが取得することが困難であるため、それらを解決できると考えている.さらにActGPU言語としてはユーザへの負担は極力小さくする予定である．例えば，CPUでアクター計算を実行するとなると，アクターをCPU上にも配置する必要がある。どのアクターがCPUとGPUどちらに配置されるかなどは，基本的にはユーザは指定すること無くプログラムを書くことができるようにする.
3.3プログラムへの介入
いくつか，外部からプログラムへ介入するための手段をActGPU処理系では備えている.まずは，初期アクターの生成と初期化方法の提供である。図１のような例の場合、初期アクターとその初期化はわずか数行で実行できるが，巨大なネットワークをアクターで表現する場合，その初期化をコード中に記述することは困難であるといえる．そこで，初期に配置するアクターとその初期データを記述したデータをプログラムに与えることで，簡単に初期化できるようにする。与えるデータは，アクターの固有変数に対応したJSON型の形式に，貨を入れたものとする．図3に例を示す．ここでアクターの固有変数はユーザがプログラム中で指定するため，入力データ形式についてはユーザが把握できているとする.次に計算中のアクターネットワークに対し，外部からデータアクセスを可能とする手段を提供する。例えば，ActGPUプログラムの実行に対し，コンソールを表示し，アクターのデータをgetコマンドによって得られるようにする。これにより，ユーザは計算結果を動的に待ることも可能となる．ここでもCPU側のアクターを利用する.
4実装
ActGPU言語ではGPUアーキテクチャを意識しない実装によって、アクターを用いたGPUによる並列処理を可能にしている．ただし2節よりわかるように，アクターモデルとGPUの計算モデルの間には相違点が多くある。アクター計算を実行するためには，GPUアーキテクチャに従った形に変換を行わなければならない。その変換を踏まえ，我々はActGPUコードからCUDAコードへの変換を行う処理系を実装した．本節ではActGPU言語で定義したアクター,メッセージのGPUアーキテクチャへの変換手法と, GPU上での詳細なアクター計算の実行手順について論説する.
4.1  GPUアーキテクチャへの変換
前節で述べたように，アクター計算を実行するにあたってActGPUコード中でユーザが定義すべきことは，アクターの持つ固有変数と，固有関数である．固(a)アクターの固有変数の例. (b)入力データ例.図３アクター宣言と入力データ形式の対応関係.
図４　図１におけるアクターとメッセージの擬似コード.
有関数の引数がメッセージのデータとなるため，固有関数の定義はアクターの振舞いとメッセージの定義に相当する、この項ではこれらのGPUアーキテクチャへの変換について述べる.現した．実行時にはこの構造体を作成し，配列の同位置のデータはそれぞれ同一アクターやメッセージのデータとして扱う形で，複数のアクターやメッセージを表現する．例えばあるスレッドのID ("tid"と置く)を用いて，図４のDFibonacci構造体のインスタンス”dfib”に対し，dfib.fibonacci_fibonacci_n[tid]とdfib.fibonacci_fibonacci_r[tid]はそれぞれ同一アクターのデータとして表現される.構造体の中で配列の大きさは使用できるアクターやメッセージの最大数に相当するが、アクターモデルでは本来無限個のアクターを扱うことができるため,ここは無限に定義されるべきである。しかし，物理的に有限で確保しなければならないため，配列はリングバッファとして扱い，キューを実装することとした.そのため，キューの先頭と末尾を表す変数を構造体2 3 4まずアクターやメッセージなどのデータ集合は、図4のようにSoA (Structure of Arrays)型の構造体として表現した．図4はGPU上のアクターやメッセージを表すが，前節の説明にあったようにこの構造体はGPUとCPU双方に同じ形式のものを生成する.つまりアクターやメッセージのデータそれぞれに対し,十分大きな配列として領域を確保し，それら配列を要素として持つ構造体をアクターやメッセージの集合として定義する。配列のタイプはアクターに関連する指示詞以外は単純変換し，actor指示詞の場合は整数型に変換し，アクターのGPU上でのアドレスとして表の中で宣言する．図4の3行目と17行目に定義される。 headとtailがこれに当たる。ただしバッファの大きさは適当な値（図４のsize変数）を構造体作成時に与えるものとする．またアクターには4.3項で述べる，ロックによる排他処理のための変数を用意しておく．図4の3行目のisbusyがこれに当たり，この値を各スレッドが排他的に操作し，アクターの原子性をする。この変換はCUDAスレッドによる連続メモリ領域アクセスを意識している．詳しいスレッドへの仕事割り当てについては後述するが，SoA型によってアクターごとに共通するデータを一つの配列として表現することで，アクターごとに共通な処理をスレッド並列に実行する際に連続メモリ領域アクセスによるデータ処理ができることになる．これらの構造体は読み書きが必要であるため，GPUのグローバルメモリに置いた．連続メモリ領域アクセスによってメモリアクセスの結合を達成することもできている.なおアクターシステムの一例として，アクターごとにメッセージキューを保持する方法がある[1].しかし，我々の実装ではメッセージキューとアクターを分離して表現することとする。代わりにメッセージのデータとしてそのメッセージの送信先アクターのアドレスを覚えておくことにした。これはアクターごとにメッセージキューをもたせる形にすると、確保するメモリ領域が大きくなることと，スレッドによるメモリアクセスが不連続になることを避ける事を目的としている.次にアクターの振舞いについてである。アクターの振舞いは固有関数の実行にあたるが，GPU上ではこれをswitch文によって，与えられたメッセージに応じて実行する処理を分岐させる形をとった。メッセージの種類を条件に分岐させ，分岐先の処理には該当する固有関数の処理を割り当てる。ただしコンストラク夕関数は3.1項で述べたように，分岐条件として記述しない．結果として図５のように，ActGPUコードはGPUで扱う形に変換される。GPUで実行させるにあたってはより詳細な変換が必要となるが，それについては4.3項で述べる.
✉ 6   CUDAでの実行概要.
4.2 CUDAによる処理
この項では，CUDAスレッドによる並列処理の詳しい順序、スレッドへの仕事割り当てについて説明を行う.前項で述べたように，CUDAスレッド一つが一つのメッセージの処理を実行する。これは図6に表現されるように，CUDAスレッドがメッセージを取得し、対応したメッセージの送信先アクターを特定、該当するアクターのデータを用いてメッセージ処理を実行するという形で実行される。この一連の処理を繰り返すことによって，アクター計算を進めていく．最初のスレッドによるメッセージキューへのアクセスは，メッセージキューを配列としてスレッドのIDを用いて連続になるようにする。これはメッセージキューとアクターを分離した利点である。さらにプロックごとにメッセージキューの違う領域にアクセスすることで，処理の並列性も向上させる．ここでプロックごとに同種類のメッセージ処理を実行することによってワープごとの処理が共通化され，branch divergenceを回避することができる。ただし，固有関数内で発生した条件分岐に関しては対応していない.次にメッセージを取得したスレッドはメッセージに対応するアクターを探す。これはメッセージの持つアクターの場所を読み込むだけでよい。そして対応するアクターによる振舞いを実行するが，ここでロックによる排他制御を行う。これはアクターの原子性を表現するためである．複数のスレッドにより参照されたアクターにおいて，処理できるメッセージを高々一つにする。実際に処理することのできなかったメッセージはメッセージキューの末尾に送り，元の位置のメッセージは破棄する。これによってアクター内の競合を防ぐことができる。メッセージの処理の中で新たにアクターやメッセージが生成されたら，それぞれキューの末尾に追加する.この処理の中で，競合状態は発生しない．アクター内のデータに関しては，アクターモデルに従って処理を進めるため競合は発生しない．それ以外に競合が発生する部分は，我々が定義したキューへの新たな要素の追加である．これに関しては，CUDAの排他制御関数を用いることによって，排他的にキューの末尾を更新していくことによって，キューへの書き込みで衝笑しないようにしている.なお前項で述べたようにコンストラクタ関数をメッセージとして扱わなかった理由は，不利益の発生を抑えるためである。キューが増えることでキューあたりのプロックとプロックの数が減少すること，排他制御によるスレッドの待機状態が発生しやすくなること,そしてアクターの初期化が遅れることにより他のメッセージ処理の結果に間違いが発生してしまうことなどが起きうる。コンストラクタ処理の実行方法については次項で述べる.
4.3 CUDAコード生成
CUDAコードへの変換について，より具体的に説明をする。コード変換はActGPUコードに対して構文解析を行い，アクター部分について適切な形に構造を変換することで実行される。構文解析はPEG.js [12]を用いて，ActGPU言語の文法に従ってJSON形式で表現される木構造を生成するようにした．CUDAコードは得られた木構造よりカーネル関数を作り、カーネル関数実行の前処理と後処理を加えることで生成する.カーネル関数では，スレッドはIDに対応したメッセージの処理を行う．IDに対応したメッセージキューの要素が空でない場合，最初にメッセージの送信先アクターのアドレスを用いて，アクターにロックをかける。ここではatomicCASによってロックをかけるのと同時に条件分岐を発生させ，一つの大きなswitch文を内部に記述する。このswitch文は図5を例とする，メッセージ処理の分岐である．固有関数の処理部分は大部分をActGPUコードのまま扱い，アクターやメッセージの変数部分は前述したアクターやメッセージ構造体のデータに変換する．さらにActGPUコードより固有関数内で新規に生成するアクター,メッセージの個数は把握できるため，キューの領域確保をatomicAddを用いて必要な分だけ固有関数処理の先頭で実行させる。なお前節で述べたが，アクターのコンストラクタに関してはメッセージとして扱われず，アクターを生成した部分にコンストラクタ処理を挿入することでアクターの初期化を実行させる. switch文を抜けた所でatomicSubによってアクターのロックを外し，該当アクターにおける次のメッセージ処理を可能にする。最後にロックによって処理できなかったメッセージをキューに再送信する処理を，分岐処理として加え，カーネル関数を閉じる．例として，図１より図７のようなコードが生成される．次にカーネル関数実行の前処理と後処理部分のコード生成である。前処理はGPUメモリ領域の確保からその初期化，カーネル関数を呼び出すところまでである。初期化についてはActGPUコードよりアクターの固有関数外で，アクター生成やメッセージ送信を実行している部分を探し，コード変換をする．GPUメモリ領域の確保は，アクター構造体やメッセージ構造体を生成した際にcudaMallocによる領域確保が実行される（図4参考）、またCPU側でもGPU側と同様の構造体を生成し，こちらはmallocを用いて領域確保を行う。アクターやメッセージキューの大きさは構造体生成時に設定できるが，この部分は現在
図7   カーネル関数の擬似コード.
適当な値で決定している．後処理はActGPUコードにおいて，アクターの固有関数外でアクターの情報を扱う処理に相当する。3.2項で述べたように，最初にCPU上のアクターへGPU上のデータをすべて書き写す。このCPU上のアクターのため、基本的にはActGPUコードを変換せずに，アクターの情報をメッセージ処理以外で利用することができる。以上の変換の様子を簡単に表したのが図8となる.なお本節ではアクターに関わる部分のみについて説明したが，3.１項末部で述べたようにactor指示詞を使わない場合は一般的なC++のプログラムとして扱われる。またアクター部分以外に関する変換については述べなかったが，基本的にはactor指示詞を使わない場合と同様にC++コードとして扱われ，CUDAコード上でもActGPUコードそのままの形で記述される. 2節では説明していないものがある．詳細は付録Bを参照していただきたい. ●シェアードメモリの利用● 実行に使用するスレッド, プロック数の自動決定● CPUでのアクター計算の実行● 複数GPUの利用●アクターの原子性の限定的解除
4.4将来的な機能
以下に列挙する事項に関しては具体的な実装方法に関しては定まっていない，もしくは未だ実装が完了していないが，将来的にシステムに迫加するアイデアである．なおここで扱うCUDAの機能については●ガーベジコレクションの実装まずシェアードメモリの利用について説明する。現在アクターやメッセージはグローバルメモリに置いていることは前述した．これはアクターの隣接関係はプログラムによって異なるため，GPUのすべてのスレッドからアクターが見えなければならなく，また生成するメッセージも任意のアクターの振舞いによって実行されるため，すべてのスレッドからキューにアクセスできなければならないためである。グローバルメモリは任意のスレッドからアクセス可能だが，メモリアクセスはGPUメモリ上で最も遅い．そのため，より高速なシェアードメモリを利用することを考える。ただしシェアードメモリはプロック内のみで共有できるメモリであり，今述べたようにアクターやメッセージはどこからでもアクセスされるため，これらをすべてシェアードメモリに置くことは難しい。ただし，一部を置くことは可能である．例えばメッセージ処理におけるアクターのデータの一部をシェアードメモリに置く，そのデータを使用する回数や，レジスタの数も考慮に入れて実装を行いたい.次にスレッド,プロック数の自動決定について説明する．現在の実装ではスレッド，プロックの数はユーザが定義できず，夏で指定した値で実行を行なっている．ただし，この数は適当に設定されている．CUDAプログラムを実行するにあたって，GPUの物理的制限によりスレッド数やプロック数によって使用できるレジスタの数や，物理的に並列に実行できるスレッドの数などに制限が設けられる。そのため，プログラムによって最適なスレッド,プロックの数が決まっているといえる．我々は一般的なプログラムを実行する実行系を作成しているため，必ずしも最適な数を選択できるとは限らないが，計算によって適切な数を待ることはできると考えている。アクターやメッセージの数，そしてGPUの物理的特徴よりスレッドとプロック数の適切なペアを算出するアルゴリズムを研究中である.三つ目はCPUでのアクター計算の実行である. 3.2項で述べたように，CPUという計算資源を現在は有効活用できていない。これを達成するためには, CPUとGPU間でのデータの同期と，アクターの原子性の保持という問題を解決する必要がある．前者に関してはゼロコピーで、後者に関してはCPU-GPU間の同期を増やすという方法で対応できると考えている。ただしアクターの移動性を保証した実装を行わなければならないため，現在は分離しているアクターとメッセージの関連性を高めるなど，データ構造に関して工夫が必要であると予測する。複数GPUを使うことも考えている．こちらに関してはPeerアクセスを用いることによってGPU問のメモリの共有ができるため，実装は簡単であると見積もっている.また，並列性を向上させることに関して，アクターの原子性を限定的に解除することが着想としてある.現在ロックを用いて排他的にアクターごとの処理を実行しているが，メッセージの処理ごとに読み書きするアクターのデータは異なる場合が多い。ここで互いに競合を起こさないメッセージ処理ならば，排他的に処理する必要はない。実装上この手法が利用できるのは，ActGPUコードより静的に解析できる部分に関してのみであるが，排他処理が緩和されるため並列性は向上する.最後にガーベジコレクションの実装について説明する。メッセージキューに関しては隙間無く連続領域にデータが置かれることを前述した．しかしアクターに関しては、deleteによる処理で配列要素の初期化を実行するが，空耍素をつめる機能はなく，メモリ領域が無駄になる．これを解消するために，ある程度アクターキューが詰まった時点でゴミ収集を実行し,キューを再構築することを考えている．現時点ではアクター計算のステップを一時中断して，GPU上でガーベジコレクションを実行し，中断した部分からアクター計算を再開することを考えている.
5システムの考察
本稿冒頭で述べたように，本システムは安定して動作しておらず，正しい評価はできない状況にある．本節ではシステムに関して，我々の目標とするGPGPUプログラミングの記述性の高さ，実装プログラムの処理性能について考察という形で展望する.
5.1 ActGPU言語の効率
図1はActGPU言語で書いたプログラム例の一つである．最初にアルゴリズムについて簡単に説明する.  fib(n) =  fib(n − 1) +  fib(n − 2),  fib(1) = 1, fib (0) = 0の計算式に従い，Fibonacciアクターに定数nを与えるとn ≥ 2のとき新たなFibonacciアクターを二つ生成し，それぞれにn − 1とn − 2をメッセージ送信，n ＜２のときはAddアクターにnを送信する。 Addアクターは数値を二つ受け取り,その和を次のAddアクターにメッセージ送信する. Addアクターの計算を繰り返し，最終的にResultアクターへ計算結果が返る仕組みである.コード全体を眺めて，GPGPUプログラミングに必要な記述がない点に注目できる。GPUに合った実装方法に従う必要がないという点で，簡単にGPUを用いた実装ができるようになったと言える。ただしActGPU言語がプログラマに，アクターモデルに従った非同期オプジェクト指向プログラミング技術を要求することもわかる．特にフィポナッチ数の計算はより簡単な逐次実装方法が明確であるため，コード例の理解は多少難しいと思われる。しかしアクターがメッセージ送受信によって連結されていると考えれば,プログラムを構造的に理解しやすい点で一反一短であると言え，GPUアーキテクチャにとらわれない点で記述性は総合的に向上していると判断できる。他の例として，重みなし無向グラフの一対全最短距離を測るプログラム例を図9あげる．こちらはアクターをノードとして置くことで，ネットワーク構造とプログラムの構造が対応させ，フィポナッチ数計算の例よりも理解しやすい。エッジで結ばれたノードをお互いにアドレスを知るNodeアクターとして定義し, Nodeアクターはあるノードからの最短距離を保持させ，定数nを受け取った際にしが最短距離より小さい場合に隣接ノードへl + 1を送ることで，あるノードからの最短距離を伝搬させるアルゴリズムである.この例において要点となるのは，隣接ノード情報の保持の仕方である．4. 1項で述べたようにGPUメモリは有限であるため，アクターごとに保持できる情報の数も有限である．例の16行目でSUIT_Nと置かれている部分は，本来はプログラマが定めたネット
図9   重みなし無向グラフの一対全最短距離
ワークに応じた適切な値であるべきである．この点に関しては，GPUを意識した実装が必要であると言える.なおノードの隣接状況などの初期化は，32行目のinitActorsで実装できる．これは3.3項で説明したスキーマに従った情報をプログラムの引数として与え，それによってアクターを初期化するAPIである.よってノードの初期化も簡潔に実装できる.
5.2処理性能について
前項で述べたように，大部分でGPUに依存しない実装ができている。そのためプログラムの実行性能は，ActGPUコードから自動変換されたCUDAコードに従うと言える．現在のシステムでは時間計測ができていないため，過去のシステム[19] [18]での処理性能を参考に，現在のシステムの評価を行う.図10は[18]より参照した,フィポナッチ数計算プログラムの性能を表す図である。GPUはNVIDIA Tesla C2075一つを用い，ActGPUと実行時システムはどちらもCUDAプロック数42, プロックあたりのCUDAスレッド数128の環境で処理を行っている. CPUはIntel Xeon E5645を単一コアで，C言語で実装している．図でActGPUと実行時システムで分かれているのは，ActGPUコードからCUDAコードを生成したか，アクターを利用したCUDAコードを直接記述したかの違いである。以前のActGPUシステムがメッセージの処理を一種類ごと実行しており,かつコンストラクタの処理もメッセージ処理として扱っていた．対して直接CUDAコードを記述した方は同様にメッセージの処理を一種類ごと実行しつつもコンストラクタの処理は他のメッセージの処理の中で実行しており，よってランタイムシステムの方が図6の繰り返し回数が少なかったことがこの差を表していると言える．現在のシステムでは，後者と同様にコンストラクタ処理はメッセージ処理として独立させていない上，すべてのメッセージを同時に処理させるため，この点だけで最高で[num_of_msg_types]の処理時間になると見積もっている．なおCPUでの処理の方がはるかに高速であるが，これは我々のシステムが未熟であることも要因の一つであるが，フィボナッチ数計算がアクター計算にあまり適さないという点がもっとも大きな原因であると考えている.図11は[19]より参照した，グラフの一対全最短距離導出プログラムの性能を表す図である. GPUとCPUはフィボナッチ数の計算と同じであり，ただしGPU実行におけるCUDAプロック数が64,プロロックあたりのCUDAスレッド数が64の環境で処理を行っている．計測に利用した重みなし無向グラフは，Duncan J. Wattsのβ-モデルによって生成したスモールワールドネットワーク[16]である．図よりCPUの実装は最適化をしていないものの，ActGPUの方が高速に実行できていることがわかる。ただしここでのGPUはActGPUコードより自動変換により生成したCUDAコードではなく，CUDAコードとして直接実装している．しかし現在のActGPU処理系ならば，より並列性が高く高速な実装ができると考えている。特にマルチGPUやCPUを利用したアクター計算が可能になると，倍々的に処理性能が上がると見積もっている.
6  関連研究
我々の研究の要点は，アクターモデルを用いている点，それによってGPUコンピューティングの汎用性や記述性を向上させている点である。それぞれにおいて関連研究を挙げる.
6. 1アクターモデルに基づくシステム
アクターモデルの有用性よりアクターモデルに基づいた言語，枠組みは数多く存在する．Actor-Based Concurrent Language (ABCL)[17]はMultiple In- struction Multiple Data (MIMD)形式のアクターモデルに基づいた分散言語である。非同期メッセージパッシングを並行オプジェクト間で実行するための手段が与えられている.現在も更新中のものとしては，Akka[7]というツールキットが存在する。これはJVM上で動作するScala [8]とJavaで使える汎用ライプラリであり，アクターモデルを機能の中で採用しており，耐故障性や負荷分散にも対応した高度な並行分散環境を定義している.またErlang[3]もアクターモデルに基づいた言語として有名である．これらの関連研究では，どれもアクターの定義において，メッセージに対応した振舞いを定義するのみで並行分散処理を記述することが可能であり，アクターモデルの利点に準じた記述性を持っている.しかしGPU上でアクター計算を実行させる研究はあまり進んでいない。我々の研究は，アクターモデルに基づいたスレッドなどの低レベルな要素を考慮に入れずプログラミング環境を定義した上，GPUで動作させるという点で異なっている。
6.2   GPUコンピューティングの汎用性向上
GPUプログラミングの複雑さを解消する記述性の向上についてもいくつか挙げられる。近年注目を集めているOpenACC APIは一例である．これはループ処理の前に指示文を加える事で，コンパイラが自動的に並列化するAPIである.  OpenMPをGPUに対応させたものと捉えることができる.多言語からGPU環境に対応したコードへ変換するシステムもある.  X10コードからCUDAコードへのコンパイルするシステムが存在する[5].  X10のAPIとしてGPUで動作するコードをX10コード中に実装できるようにしたものであり，二重ループを用いてX10の並列スレッドを定義し，外側のループでCUDAプロックを，内側のループでCUDAスレッドを表現し，コード変換を行う．Lime[4]はJavaとの互換性を持った並列分散システムを構成するための言語である.  GPUにも対応しており,  LimeコードよりOpenCLコードを生成するコンパイラも実装されている[6].特にオプジェクトを用いた実装が可能であり，同期や並列実行のためのスケジューリングはプログラマから隠蔽されている。本質的にはループ文をデータ並列な処理に変換し，OpenCLコードを生成する.これらに共通して言えるのは，基本的にGPUの特異なデータ処理の記述性を向上させており，GPUに不得意な計算は依然として実装しにくいという点があるということである．対して我々の研究はGPUで動作するオプジェクト指向プログラミングを実装でき,アクターモデルの特徴を考慮した実装を必要とするものの，CUDAの複雑な実装を必要としないプログラミング環境を提供している.
7　まとめと今後の課題
本稿では我々が作成してる，GPU上でアクター計算を実行するActGPUシステムについて解説を行った．現在のシステムは未完成であるものの，GPGPUプログラミングの記述性の向上や計算性能における将来性など、興味深い結果をもたらす期待を示した。今後の展望としては，4.4項で述べた内容について実装することにより、本システムの有効性、処理性能を高めていくことが第一にある．また最近発表された，Keplerアーキテクチャに対応したNVIDIA GPUを用いることも検討してる。 Hyper-Qの機能により本質的に並列性が向上しているため，これを利用しActGPU言語によるプログラムの計算速度を上げることができると考えている.謝辞本研究はJST CREST「ポストペタスケールシステムにおける超大規模グラフ最適化基盤」プロジェクトの支援により進めています.同プロジェクトチームの方とは有意義な議論をさせていただき，大変参考となりました．この場を借りて御礼申し上げす. [1] Agha, G.:  ACTORS: A  Model of Concurrent Computation in Distributed Systems, The MIT Press, Cambridge, Massachusetts, London, Eng- land, 1986. [ 2 ] Agha, G., Mason, I., Smith, S. F., and Talcott, C. L.: A foundation for actor computation, Journal of Functional Programming 7.1, 1997, pp.  1–72. [ 3 ] Armstrong, J., Virding, R., Wikstr, C., and Williams, M.:  Concurrent programming in ER- LANG, PRENTICE HALL, 1996. [ 4 ] Auerbach, J., Bacon, D. F., Cheng, P., and Rab- bah, R.:  Lime:  a Java-compatible and synthesizable language for heterogeneous architectures, OOPSLA '10, Proceedings of the ACM SIGPLAN interna- tional conference on Object Oriented Programming Systems Languages and Applications , 2010, pp. 89– 108. [ 5 ] Cunningham, D., Bordawekar, R., and Saraswat, V.: GPU Programming in a High Level Language, Proceedings of the ACM SIGPLAN X10'11 Work- shop, 2011. [ 6 ] Dubach, C., Cheng, P., Rabbah, R., Bacon, D. F., and Fink, S. J.: Compiling a high-level lan- guage for GPUs:(via language support for architec- tures and compilers), PLDI '12, Proceedings of the 33rd ACM SIGPLAN conference on Programming Language Design and Implementation , 2012, pp.  1– 12. [ 7 ]  Gupta, M.:  Akka Essentials, Packt Publishing Ltd, 2012. [ 8 ]  Haller, P. and Sommers, F.:  Actors in Scala, Artima Incorporation, 2012. [ 9 ] Hewitt, C., Bishop, P., and Striger, R.: A uni- versal modular actor formalism for artificial intel- ligence, IJCAI '3, Proceedings of the 3rd Interna- tional Joint Conference on Artificial Intelligence, 1973, pp.  235–245. [10] Karmani, R. K., Shali, A., and Agha, G.: Actor frameworks for the JVM platform: a comparative analysis, PPPJ '09, Proceedings of the Ith inter- national conference on Principles and Practice of Programming in Java , 2009, pp.  11–20. [11] Khronos OpenCL Working Group: The OpenCL Specification. PEG.js, Parser Generator for [12] Majda, D.: JavaScript. http://pegjs.majda.cz/, 2013/07/17アクセス. [13]  NVIDIA Inc.:  NVIDIA  CUDA  C  Programming Guide, version 4.2, 2012. [14] NVIDIA Inc.: TUNING CUDA APPLICA- TIONS FOR KEPLER, 2012. [15] Sutter, H.: The free lunch is over: A fundamen- tal turn toward concurrency in software, Dr. Dobb's Journal 30.3 , 2005, pp.  202–210. [16] Watts, D. J. and Strogatz, S. H.: Collective dy- namics of 'small-world' networks, Nature 393 , 1998, pp. 440–442. [17] Yonezawa, A., Briot, J., and Shibayama, E.: Object-oriented concurrent programming ABCL/1, OOPSLA '86, Proceedings of the ACM SIGPLAN international conference on Object Oriented Pro- gramming Systems Languages and Applications, 1986, pp.  258–268. [18]  高柳亘,徐駿剣,脇田雄:  Actorモデルにもとづいた非同期並列プログラミング言語ActGPUのコンパイラの実装とその評価, IPSJ SIG Notes, 2012-HPC-137, 2012, pp.  1–10. [19]  高柳亘,脇田建:  CUDAを用いたメッセージ送信型並行計算Actorモデルの束装, IPSJ SIG Notes, 2012-HPC-135 , 2012, pp.  1–10. 2.1項でアクターモデルについて説明を行った.付録として，アクターシステムのためのアクターモデルの構成について説明を行う．アクターモデルそのものではないが，モデルとしての特徴は理解できると考える.アクターモデルとは，一般的に分散環境のための並行計算モデルである[1].アクターシステムは,アクターモデルに従った並行で独立した動的オプジェクトであるアクターによって構成される。アクターはそれぞれ固有の命令処理系と状態を持っており，自身の持つ状態を更新するアクター計算を実行する。アクターの状態の更新は，アクター問のメッセージ交換によって待られた情報を使って行われる.アクターシステムには，アクターとメッセージのみが存在する。メッセージはID（タグ）と，送信先アクター，処理を決定するキーワードの組として表現される[1].アクターは他のアクターのアドレスと，受信したメッセージに対応する処理動作である振舞いを持つ.この振舞いとは受信したメッセージに対応する処理動作を表し，メッセージと振舞いは一対一対応している。メッセージは論理的に無限サイズの集合として表現される．アクターは受信したメッセージの集合より無作為に一つ選択し，メッセージごとに逐次的に処理を実行する（アクターの原子性）[2].アクターの振舞いとして以下が実行される[1]. ●アクターの生成●非同期メッセージの送信●状態の更新特に注意が必要なのは，メッセージ処理の結果として生成されるアクターやメッセージは，そのメッセージが処理される前に生成されないことである．アクターが動的であるのは，メッセージの処理に対応して状態が逐次的に更新されていくこの性質を表している.アクター生成において，新たなアクターのアドレスはその時点では生成したアクターのみが知ることになり，他のアクターから新規アクターへのメッセージ送信を可能とするには，生成したアクター又は新規アクターからメッセージパッシングによって他のアクターのアドレスを知るしかない.非同期メッセージの送信は，メッセージを送信したアクターは送信先アクターの状態にかかわらずメッセージを送信し，受信の確認も行わないということである。これは前述したアクターごとの受信したメッセージに対する順番を考慮しない処理の実行に関連する.アクターの状態について詳しく説明する．簡単に説明すると，アクターの持つ数値などのデータや，他のアクターのアドレスがアクターの状態である．アクターは他のアクターのアドレスと振舞いを持つと前述したが，数値などもアクターとして表現できるため,他のアクターのアドレスはアクターの持つデータも表現している．状態の更新は具体的なシステムによってことなるが，変数やその加減乗除式，プール値とその評価式を用いた条件分岐などを組み合わせて実行される.最後にアクターシステムの持つ，重要な性質について述べる[10]. ●カプセル化
●移動性カプセル化とはアクターの内部状態は他のアクターから直接見えないことと，メッセージは値渡しで表現されることであり，主にアクター計算において競合状態が発生しないことを保証する性質である。スケジューリングの公平性とは，メッセージは必ず送信先へ到着して処理されるという性質であり，例えばキューによってメッセージを管理することで，公平性を実現できる．位置透過性とは，アクターはシステムで統一定義されたアドレスで管理されることで，並列分散環境下で物理的にアクターが置かれている場所への意識を不要としてくれる性質である。移動性とは，並列分散環境下でのノード間でのアクター（データ）の移動が可能であることを保証する性質であり，位置透過性を保証する性質でもある。なお移動性には強さがあり，強い移動性としてはコードと実行時の状態を移動できることを表し，アクターモデルではメッセージ処理中のアクターをノード間で移動できることを表す.弱い移動性はコードの移動と初期状態のみを移動させることを表し，アクターモデルでは処理すべきメッセージが存在しないアクターならばノード間移動ができることを表す. 2.2項でCUDAについて説明したが，ここでは補足としてより詳細な仕様について説明する．NVIDIA社製のGPUについても同時に説明を行う.まずCUDAのマルチスレッドについて説明する. CUDAは巨大な数のスレッドを用いて並行処理を実行する。そのスレッドをまとめる上位レベルとしてプロックというものがあり，さらにその上位にグリッドがある.  KeplerアーキテクチャのGPUの登場によって状況が変化しつつあるが[14]，最上位のグリッドがCUDAのカーネル関数に対応し，つまり１グリッド内のプロックとスレッドによってGPUで並列計算が行われる。なおスレッドやプロックには固有のIDが与えられており，プログラマはこのIDを使ってスレッドごとに別の処理を定義できる.ここでGPUアーキテクチャについて説明を加える. GPUには複数個のStreaming Multiprocessor (SM)があり，その中に複数のStreaming Processor (SP)を持っている．物理的には最小のSPによって並列な処理を行うため，CUDAスレッドとSPは対応関係を持つ、ただしスレッドの数は非常に多く，物理的なSPの数をはるかに上回っている．そのためCUDAではスレッドスケジューリングにより，SPに計算させるスレッドを適切に割り振ることを行なっている.同様にそれぞれの上位存在であるCUDAプロックとSMに関しても対応関係があるが，こちらもプロック数の方が現実のSMの数より一般的に多いため，SMあたり複数のプロックを割り当てて，その中でスレッドスケジューリングを実行している. CUDAスレッドのスケジューリングについて述べる前に，SPの特徴について説明をする．NVIDIA GPU l社Single Instruction Multiple Thread (SIMT)という処理形式で動作する。これは従来のSingle Instruc- tion Multiple Data (SIMD)と同じ概念であり，一つの命令を多数のスレッドによって処理を行うことを意味する.  GPUの最小計算単位はSPであるが,最小命令発行ユニットはSMであるということである。これに対応させ，スレッドの動作においても定義がある。スレッドは32スレッドを1ワープとして扱い，スレッドの動作はすべてワープ単位で制御される。つまり32スレッドが同じ命令を実行する動作形態（SIMT）となっている。なおワープ内では命令列をIn-Orderで処理する．よって実際に行われているのはスレッドスケジューリングではなく，ワープのスケジューリングである.ワープのスケジューリングについて述べたいが,その前にGPUメモリについて説明をしておく.  GPUにはグローバルメモリ,コンスタントメモリ,テクスチャメモリ,シェアードメモリ,ローカルメモリ,レジスタと多種類のメモリが存在する。メモリには種類に応じて特徴がある。例えばグローバルメモリはすべてのプロックから参照可能で巨大な容量を持つが，オフチップメモリであるためアクセスに時間がかかる．対してシェアードメモリはオンチップメモリで高速なアクセスが可能だが，プロック内でのみ共有され他のプロックからは参照できない。加えてオンチップメモリであるシェアードメモリとレジスタに関してはプロックごとに最大容量が決まっており，プロック内のスレッドで共有するため，スレッドごとに扱える容量にも制限が存在する。この最大容量はGPUによって変化し，最新のGPUほど容量は大きくなっている．一般的にプログラマは大容量低速なオフチップメモリと小容量高速なオンチップメモリを上手に利用したプログラムを実装することになる.これらのメモリアクセスは処理のオーバーヘッドになりうる。メモリコピーが発生している間に他の処理を実行することはできないため，待機状態が発生するからである。ワープのスケジューリングは，この待機状態によるレイテンシを隠蔽するように実行される．ワープの処理がIn-Orderで実行される中で，そのワープにおいてメモリコピーが発生するとそのワープの命令処理を中断し，他のワープの命令処理を実行する。これを繰り返すことにより，メモリコピーの待ち時間を他の処理によって隠蔽することが可能となる.ただしSIMT処理形式によって発生するオーバーヘッドにより，これらの隠蔽機能を超えてメモリコビーの待機時間が発生したり，命令処理の時間が長くなることで，全体のパフォーマンスが損なわれる場合がある。例えばメモリアクセスにおいて，グローバルメモリへのアクセスを結合できない場合や，シェアードメモリへのアクセスでバンクコンフリクトが発生する場合，メモリコピーに時間がかかる．グローバルメモリへのコアレスアクセスとは，連続するメモリ箇所への複数スレッドによる統合的なアクセスのことを示す。またバンクコンフリクトとは，メモリを連続したアドレスに対応させて複数個のバンクに分割するシェアードメモリにおいて，スレッドごとに同一バンクヘアクセスした際の衝突を表す。どちらも基本としてはスレッドIDとメモリアドレスを連続に対応させることで高速化させることができる.命令処理の時間が長くなるのは，branch divergenceによって引き起こされる。branch divergenceはSIMT処理形式によって，ワープ内のスレッドが同時に一つの命令のみを実行できるという特徴に対し，ワープ内のスレッドの処理に分岐が発生した場合に起きる.プログラムはスレッドIDに従った並列処理を実行するため，ワープ内で分岐が発生するプログラムも実装することができる。しかし分岐が発生してもワープ内では並列に処理をすることはできないため，分岐先の処理は全て実行しなければならない．このとき本来の分岐先とは違うスレッドは，待機状態となって実際には処理を実行しない．待機状態はオーバーヘッドとなるため，ワープごとの処理は分岐しないように実装することが高速化には必要である.しかし実際の並列プログラムは，同期や排他制御を行わなければ競合状態が発生する可能性がある．__syncthreads関数などの同期処理やatomicAdd, atomicSubなどの排他処理をプログラム中に適切に入れることで正しい処理を実現する。ただしこれらは必ず待機するスレッドを作るため，なるべく使わないようにするべきである.以上がGPU上での処理となる。プログラマは上記したことに注意を払いながら実装をすることで，高速なプログラムを実装できる。なおGPU上の処理だけでなく，GPU外での処理においても注意が必要な特徴がある．例えばCPUとGPUメモリの関係についてである.  CPUとGPUは物理的に別物であるため，メモリ空間も物理的に分断されている．そのためGPUカーネル関数を実行する前に，cudaMallocなどを用いてGPUメモリ領域にデータに応じた領域確保を行い，CPUメモリからGPUメモリヘメモリコピーをする形で，GPUメモリに初期値を与える必要がある。このメモリコピーは時間がかかり，パフォーマンスの低下に関わってくる．そのため，なるべくデータを一括にコピーしたり，CUDAストリームを用いてカーネル関数の実行と並列にメモリコピーを行うなどすることで，オーバーヘッドを減らす必要がある.  CUDAストリームとは，GPUで実行されるタスクのキューのようなもので，ストリームを複数用意し，適切にキューヘタスクを入れることで，カーネル関数実行とメモリコピーを並列処理できる.なおGPUから直接メモリアクセスする手段として,ゼロコピーアクセスというものがある．cudaMemcpyなどのCPU-GPU間のメモリコピーを行う必要がないためゼロコピーと呼ばれており，コピーによるオーバーヘッドを完全になくすことができる。ただしGPUとCPUは物理的に離れているためGPUメモリにアクセスするよりは時間がかかるので，何度もCPUメモリヘアクセスする場合は，一度のメモリコビーによってGPUメモリヘデータを置いたほうが高速な場合もある.