日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

プログラムメモリの小さな Processing-in-Memory 向
け Java to C コンパイラ
一野瀬 知輝鵜川 始陽

Processing-in-Memory(PIM) とは DRAM チップ上にプロセッサ（以下メモリプロセッサ）が配置されたアーキテクチャであり，そこに計算の一部をオフロードすることで CPU と DRAM との間のデータ移動による遅延や消費電力を低減できることが期待されている．PIM アプリケーションの開発効率向上のため，我々は 1 つの Java のプログラムで CPU とメモリプロセッサの計算を記述できるシステムを開発している．本研究ではオフロードするメ
ソッドを事前にメモリプロセッサの機械語にコンパイルするシステムを提案する．メモリプロセッサのプログラムメ
モリは小さいため，複数のバイナリにメソッド単位で分割してコンパイルし，それらをプログラムオーバーレイにより実行する．このとき，呼出し関係にあるメソッドは同じバイナリにコンパイルする．提案システムをインタプリタ
を用いた先行研究と比較した．先行研究で用いられていたベンチマークではバイナリの入れ替えが起きず，実行は31% 高速だった．また，プログラムオーバーレイのオーバーヘッドの内訳を調べ，バイナリ入れ替えの割合が大き
いことがわかった．

セッサで動作させて逐次実行していた．

1 はじめにしかしこの方式には，次の 2 つの問題がある．

Processing-in-Memory (PIM) は DRAM チップ上

• インタプリタにはバイトコードのフェッチやディにプロセッサ（以下メモリプロセッサ）を配置した
アーキテクチャである．DRAM 内のデータベース検索などのデータインテンシブな計算をメモリプロセッ

スパッチなどのオーバーヘッドがある．

• 完全なインタプリタはメモリプロセッサのプログ
ラムメモリに収まらない．

サにオフロードすることで，CPU と DRAM の間のそこで本研究では，オフロードする Java プログラム

データ転送に起因する遅延や消費電力のボトルネッ

を事前にメモリプロセッサの機械語にコンパイルする

クが低減できることが報告されている [2]．PIM アプ

システムを提案する．コンパイル後のプログラムのサ

リケーションの開発効率を向上させるため，我々は

イズは，もとの Java プログラムの規模によって変化

Java 言語の 1 つのプログラム内に CPU とメモリプ

する．プログラムメモリに収まらない大規模なプログ

ロセッサの計算を記述できるフレームワークを開発しラムの実行も可能にするために，メソッド単位でプロている．

グラムを分割してから，静的コールグラフをもとに呼先行研究 [5] で開発したプロトタイプシステムで出し関係にあるメソッド同士を同じバイナリにまとめ

は，オフロードする Java プログラムのバイトコード

る．こうしてできた複数のバイナリをプログラムオー

を，機能を制限した Java インタプリタをメモリプロ

バーレイにより必要に応じてバイナリを入れ替えな∗ Java to C Compiler for Processing-in-Memory with

Small Program Memory.
This is an unrefereed paper. Copyrights belong to
the Author(s).
Kazuki Ichinose, Tomoharu Ugawa, 東京大学情報理工学系研究科, Graduate School of Information Scienceand Technology, The University of Tokyo.

がら実行する．
提案システムを評価するために，我々は 2 つの実験を行った．先行研究 [5] と同じベンチマークを用いて提案のコンパイル方式による実行時間を比較した
結果，31%の短縮が見られた．また，プログラムオー
バーレイのオーバーヘッドには，バイナリ入れ替えそのもののオーバーヘッドと，入れ替えが起きても実行の継続を可能にする仕組みにより普段の実行で発生

プログラムするオーバーヘッドがある．マイクロベンチマークを

データ

CPU

データ

用いてこれらの割合を調べた結果，バイナリ入れ替え
そのもののオーバーヘッドが支配的であることがわ

IRAM(24KB)かった．

MRAM(64MB)DPU
WRAM(64KB)

2 問題
2. 1 UPMEM システム
UPMEM システム [8] は，PIM デバイスを搭載

DMA コントローラ

PIM チップ

した，現在一般に利用可能なシステムの 1 つである．

UPMEM システムは CPU，従来の DRAM，および

図1

PIM チップ構成

メモリプロセッサを搭載した DRAM である UPMEM

PIM モジュールによって構成される．UPMEM PIM
モジュールの各 DRAM チップを以下では PIM チッ
プと呼ぶ．

できない．
我々のフレームワークは CPU から指定の DPUの MRAM に指定のクラスのインスタンスを作成す

PIM チップの詳細を図 1 に示す．PIM チップにはメる API(createObject)，MRAM 内のオブジェクト

モリプロセッサとして DRAM Processing Unit(DPU)のメソッドを DPU で実行する API(invokeMethod)，が積層されている．各 DPU は専用のプログラムメモ

invokeMethod で実行したメソッドの戻り値を取得

リ (IRAM)，スクラッチパッドメモリ (WRAM)，メ

す る API(getIntReturnValue な ど) を 提 供 す る ．

インメモリ (MRAM) を備えていて，いずれのメモ

createObject は，作成したインスタンスに対応する

リも CPU とデータのやりとりができる．

ハンドルを返す．invokeMethod はこのハンドルを介各メモリは異なる特徴を持つ．IRAM には CPU か

してメソッドを呼出す．

らプログラムを書き込み，実行することができるが，我々のフレームワークを利用した PIM アプリケー

24KB と小さいためプログラムの大きさに制限がある．

ションの例を図 2 に示す．このプログラムは，図 3

WRAM は 64KB の高速なメモリである．MRAM はに示す二分探索木クラスで PIM チップ中に木を構

64MB と容量が大きいが，DPU は DMA コントロー

築し，その木を探索する．まず，createObject にラを通してアクセスする必要があり，アクセスのオー

より MRAM 内にキーを 123， 値を 456 として

バーヘッドが大きい．それぞれのメモリの特性を考慮

ノードを作成する．そのノードのハンドルに対して，しながらプログラミングをする必要がある．

invokeMethod により insert メソッドを繰り返し呼び出すことで，MRAM 内に木を構築する．続いて

2. 2 PIM プログラミングフレームワーク

search メソッドを呼出して，キーが 100 のノードを

我々は，CPU と DPU で実行されるプログラムのDPU で探索する．最後に getIntReturnValue によ

両方を Java で 1 つのプログラム内に記述すること

り探索結果を取得すると，はじめに挿入したノードのができるフレームワークを開発している．このフレー値である 200 が返される．

ムワークは CPU を中心とした分散オブジェクトモ

先行研究 [5] ではさらに，ハンドルをラップするプ

デルに基づいていて，CPU から PIM チップ内にオ

ロキシオブジェクトを作ることで，透過的に DPU に

ブジェクトを作成し，そのオブジェクトのメソッドをあるオブジェクトのメソッドを呼出せるようにしてDPU で実行することができる．DPU から CPU 上いる．これは本研究の範囲を超えるので説明は省略のオブジェクトに対するメソッド呼出しをすることはする．

Handle handle = UPMEM.createObject("TreeNode",123, 456);

12

令のみを実装しているが，プログラムサイズは 9KB
に達する．

3

UPMEM.invokeMethod(handle, "insert",
100, 200);
...

4
5
6

3 提案システム
本研究ではオフロードするメソッドを事前に DPU

7UPMEM.invokeMethod(handle, "search", 100);
int result = UPMEM.getIntReturnValue(); // 200

89の機械語にコンパイルするシステムを提案する．メ
ソッドの各バイトコード命令を，それと等価な機械

図2

PIM チップの中に木を構築して探索するプログラ
ムの例

語に置き換えたバイナリを生成することで，命令のフェッチとディスパッチのオーバーヘッドを削減する．
また，バイナリが IRAM に収まらないサイズになっ1

public class TreeNode {ても実行できるように，プログラムをメソッド単位で分割してコンパイルし，プログラムオーバーレイによ2public void insert(int k, int v) {/ * Insert a new node with key=k, val=v. */
}

3
4
5

りバイナリを入れ替えながら実行する．

6

public int search(int k) {if (k == getKey())
return getVal();if (k < getKey() && getLeft() != null) {return getLeft().search(k);} else if (k >= getKey() &&getRight() != null) {return getRight().search(k);}return -1;
}

7
8
9
10
11
12
13
14
15
16
17
18

}

図3

PIM チップの中で扱う二分探索木クラス

3. 1 DPU の機械語へのコンパイル方法
我々のシステムでは Java プログラムのバイトコー
ドを，一度 C 言語のプログラムにコンパイルし，得られた C 言語のプログラムをコンパイルすることでDPU 用のバイナリを生成する．バイトコードから C
言語へのコンパイルでは各バイトコード命令をその命令と等価な C 言語のプログラム片に置き換える．C
言語からバイナリへのコンパイルは UPMEM のソフ
トウェア開発環境 †1 が提供する DPU 用の C コン
パイラを使用する．

2. 3 PIM アプリケーションのインタプリタ実行

1 つの Java のメソッドは 1 つの C 言語の関数に

invokeMethod の際には，DPU で Java プログラ

コンパイルする．関数の中には，各バイトコード命令

ムを実行する仕組みが必要である．先行研究のシステと等価な C 言語のプログラム片が並ぶ．バイトコー

ムでは PIM チップ中で扱うクラスのバイトコードを

ドから C 言語へのコンパイルを，図 4 に示す search

MRAM にロードし，機能を制限した Java インタプ

メソッドのバイトコードを例に説明する．iload_1 とリタを DPU で動作させてバイトコードを逐次解釈，

aload_0 はそれぞれ 1 番目のローカル変数を整数と実行していた．

して，0 番目のローカル変数を参照として Java スタッ

このシステムでは，インタプリタの実行時オーバー

クにプッシュする命令である．また invokevirtual

ヘッドが大きいという問題がある．具体的には，バイは仮想メソッドを解決して呼び出す命令である．こトコード命令の実行のたびに DMA コントローラをれをコンパイルした C 言語プログラム片を図 5 に通して MRAM から命令をフェッチするコストがあ示す．iload_1 はローカル変数の 1 番目を読み出し，

る．また，命令をディスパッチするコストもある．

Java スタックの一番上に格納してから スタックポイ

これに加えて，完全なインタプリタは IRAM に収ンタ (SP) をインクリメントする命令にコンパイルす

まらないという問題もある．先行研究では二分探索木

る．ここで，local は Java スタック中のローカル変クラスを扱うために最低限必要になるバイトコードであるメソッド呼出しや整数・オブジェクトに対する命†1 https://sdk.upmem.com/

public int search(int);
0: iload_1
3
1: aload_0
4
2: invokevirtual "getKey"
5
...
1
2

から Java のメソッドをコンパイルした関数を呼び出す．そのうえで， Java のメソッド呼出しもリターン
も，トランポリン関数にリターンする C 言語のプロ
グラム片にコンパイルする．トランポリン関数は呼

図4

1
2

search メソッドのバイトコード/* iload_1 */stack[SP++] = local[1];

出し先やリターン先のメソッドに対応する C 言語の関数を呼出す．これにより，WRAM 中の実行スタッ
クは伸びず，保存する必要がなくなる．
図 6 に示す例をもとに具体的に説明する．この図

345/* aload_0 */stack[SP++] = local[0];は， search メソッドが再帰的に呼ばれ，その後バイ/* invokevirtual "getKey" */stack[SP++] = BP;
stack[SP++] = 0; // Return label
stack[SP++] = ID_search;

きの C 言語の実行スタックと Java のスタックの様子

6
7
8
9
10

13
14
15
16
17

を示している．まず，search メソッドが search メ
ソッドを呼出すと，バイナリの入れ替えは発生せず，

11
12

ナリ入れ替えを伴って getKey メソッドが呼ばれたと

OBJ_ADDR reveiver = stack[SP - 3 CALLEE_ARGUMENT_SIZE];
trampoline_info->method_id =resolve_virtual(receiver, INDEX_getKey);
trampoline_info->is_call = true;
return;18

RETURN_LABEL0:
20
...
19

トランポリン関数にリターンしてから再度 search の関数を呼出す．次に getKey メソッドが呼出される．

getKey の関数は実行中のバイナリに入っていなかったとする．このときは CPU に制御を戻して getKeyの関数が入ったバイナリに入れ替え，それを実行す
る．このとき，Java のスタックは MRAM にあるた

図5

バイトコードのコンパイル例

め入れ替えの前後で維持される．また，CPU へ制御を戻すのはトランポリン関数からリターンすることで数の領域を指すポインタである．aload_0 も同様に行うため，WRAM 中の実行スタックには何も残らなコンパイルする．続く invokevirtual をコンパイルい．getKey メソッドから search メソッドにリター

した部分は 3. 2 節で説明する．

ンするときは再びバイナリを入れ替えて実行する．

3. 2. 1 メソッド呼出し
3. 2 トランポリンによるプログラムオーバーレイ

invokevirtual は以下の情報を Java スタックに積メソッドを呼び出す invokevirtual 命令はプログ

んで，仮想メソッドの解決をした後，トランポリン関数

ラムオーバーレイを伴う場合がある．つまり，実行中にリターンする．ここで積む情報は，invokevirtualのバイナリに呼出し先のメソッドが存在しなければ，で呼出されたメソッドからリターンした後，現在のメ呼出し先のメソッドが存在するバイナリに入れ替えてソッドの実行を継続するために必要になる．

から呼び出す．PIM チップではバイナリを入れ替え• 現在のベースポインタ (BP)る際に WRAM の内容は初期化されるため，WRAM• リターン後の再開位置を表す IDに配置される C 言語の実行スタックが消去されても問題ないように対策が必要である．

• 現在のメソッド ID
リターン後に実行を再開する位置，つまりメソッド呼

そこで，我々のシステムでは実行に必要な情報は出しの直後にはラベルを付けておき，リターン後に全て MRAM に保存する．まず Java のスタックはその位置にジャンプできるようにする．ラベルはそ

MRAM に配置する．さらに， Java のメソッド呼出のまま Java スタックに積めないので一意に ID をふ

しは，いわゆる「トランポリン方式」の関数呼出しにり，それを積む．図 5 では現在の BP，リターン後のコンパイルする．つまり，最初の Java のメソッド実

実行再開位置のラベル RETURN_LABEL0 の ID である

行前に，特別な「トランポリン関数」を実行し，そこ

0，現在実行している search メソッドの ID を Java

CPU

DPU
searchsearch メソッドのトランポリン
トランポリン
バイナリをロードして
search関数
関数
実行
WRAM 中の MRAM 中の実行スタック Java スタック

searchトランポリン関数

searchトランポリン関数

searchgetKeysearchトランポリン関数

searchsearchsearchsearchトランポリン関数

searchsearch メソッドのバイナリを再びロードして トランポリン search関数
実行searchsearchトランポリン関数

searchsearchsearchsearch①getKeygetKey メソッドのバイナリをロードして実行

トランポリン関数

searchトランポリン関数③

searchsearchsearchsearchsearchトランポリン関数

トランポリン関数

search④
searchトランポリン関数②

図6

プログラムオーバーレイ時のスタックの様子

スタックに積んでいる．
トランポリン関数にリターンするときには，以下123に示す情報を trampoline_info という構造体に書き込む．

• 次に実行すべきメソッドの ID (method_id)• そ の 実 行 が メ ソッド 呼 出 し か リ タ ー ン か(is_call)

4
5
6
7
8
9
10
11

/* ireturn */int return_value = stack[SP - 1];
SP = BP;BP = stack[SP + ARGUMENT_SIZE];
trampoline_info->label_id =stack[SP + ARGUMENT_SIZE + 1];
trampoline_info->method_id =stack[SP + ARGUMENT_SIZE + 2];
trampoline_info->is_call = false;
stack[SP++] = return_value;
return;• 実行再開位置の ID (label_id)
method_id には仮想メソッドを解決して得られたメ
ソッド ID を書き込む．仮想メソッドを解決する方法

図7

ireturn 命令をコンパイルした C 言語のプログラ
ム片は 3. 2. 5 節で詳しく説明する．図 5 では getKey メ
ソッドの解決をして得られた ID を method_id 書き

具体例として図 5 で呼出された getKey メソッド

込んでいる．is_call には呼出しであることを示すからリターンする場合を説明する．getKey メソッド

true を書き込む．label_id はメソッド呼出しではは int 型を返す ireturn 命令によりリターンする．

書き込まない．

コンパイルしたプログラム片を図 7 に示す．まず，

トランポリン関数は trampoline_info に書かれたJava スタックの一番上に積まれている返り値を取り次に実行すべきメソッドが同じバイナリにコンパイル

出す．その後，Java スタックを巻き戻すために SP と1 の矢印がされていれば，それを呼出す．図 6 の ○BP を設定し，呼出し元の BP，実行再開位置の ID，それを示している．

そしてメソッド ID を Java スタックから取り出し3. 2. 2 リターン

trampoline_info と BP を設定する．また，リター

リターンの場合は，Java スタックを巻き戻した後，ンであることを示すために is_call に false を設定

invokevirtual で Java スタックに積んだ情報を取する．最後に返り値を Java スタックに積んでトランり出して，trampoline_info に書き込み，戻り値をポリン関数にリターンする．

Java スタックに積んでトランポリン関数にリターン

3. 2. 3 関数プロローグ

する．トランポリン関数は，trampoline_info に書Java のメソッドをコンパイルした関数の冒頭にかれた情報をもとにリターン先メソッドをコンパイルは図 8 に示すプロローグを挿入し，メソッド呼出

2 の矢印がその様子を示した関数を呼出す．図 6 の○しとリターンのそれぞれに応じた処理を行う．まずしている．

trampoline_info の is_call をもとにメソッド呼void trampoline() {trampoline_info_t trampoline_info;2
3
read_from_mram(&trampoline_info);
3
4
while (1) {4
5
if (is_in_same_binary(
5
6
trampoline_info.method_id)) {67switch (trampoline_info.method_id) {7
8case ID_search:
89search(&trampoline_info); break;
910
case ID_getKey:
11
getKey(&trampoline_info); break;
12
...
13
}
図 8 メソッドをコンパイルした関数のプロローグ
14} else {15
write_to_mram(&trampoline_info);change_binary(trampoline_info->method_id);
出しかリターンかを判定する．呼出しの場合は Java 1617 } } }1

if (trampoline_info->is_call) {BP = SP - 3 - ARGUMENT_SIZE;SP = BP + FRAME_SIZE;} else { /* Return */switch (trampoline_info->return_label) {case 0: goto RETURN_LABEL0;case 1: goto RETURN_LABEL1;
...
} }

1
2

のスタックフレームを設定する．invokevirtual で引数とリターンのための 3 つの情報を Java スタック

図9

トランポリン関数に積んでいるので，BP は呼出し元の SP よりその分だけ戻した位置に設定する．SP は BP にフレームの行目で復元される．また，CPU が invokeMethod で大きさを足した位置に設定する．引数の数とフレー

メソッドを呼出すときは trampoline_info を設定しムの大きさはコンパイル時に決まる．その後は先頭て MRAM に書き込んでおく．これも 3 行目で読みのバイトコードの処理に移る．リターンの場合は，出す．

trampoline_info に保存された実行再開位置のラベ

3. 2. 5 メソッドの ID と仮想メソッドの解決

ルにジャンプしてバイトコードの処理を継続する．

メソッドには PIM アプリケーション全体で一意

3. 2. 4 バイナリ入れ替え

な ID をコンパイル時に割り当てる．クラスの継承関メソッド呼出しでバイナリ入れ替えが必要かはトラ係があっても，子クラスのメソッドと親クラスのメ

ンポリン関数が確認し，必要ならばバイナリを入れ

ソッドは別の ID を割り当てる．仮想メソッドの解決

替えてから関数を呼び出す．バイナリを入れ替えるとに用いる vtable の各メソッドのインデックスには対

きは CPU に次のメソッドの ID を知らせる．CPU

応するメソッドの ID が格納される．図 5 に示すとはそれに基づきロードするバイナリを決める．メソッ

おり invokevirtual 命令の中で resolve_virtual

ド ID とバイナリの対応もコンパイル時に生成する．

は，レシーバの vtable の指定されたインデックスに3 がバイナリ入れ替えを伴うメソッド呼図 6 では，○あるメソッドの ID を返す．vtable はアプリケーショ

4 がバイナリ入れ替えを伴うリターンでび出しで，○

ンの開始時にフレームワークが MRAM に書き込む．

ある．
バイナリ入れ替えを実現するトランポリン関数を図3. 3 静的コールグラフに基づくプログラムの分割9 に示す．5 行目でトランポリン関数は次に実行され我々のシステムでは呼出し関係にあるメソッドをるメソッドの ID を読み出し，そのメソッドをコンパ

同じバイナリにまとめる．これにより，プログラム

イルした関数が現在のバイナリに含まれるか確認すオーバーレイの際のバイナリ入れ替えの回数を削減

る．含まれる場合はその関数を呼び出し，含まれなけ

してオーバーヘッドを減らす．呼出し関係を調べるた

ればコンパイルした関数が含まれるバイナリに入れめに，SootUp [6] を使って静的コールグラフを作成

替える (16 行目)．含まれるかどうかの確認は，コンする．

パイル時にバイナリごとに生成するテーブルで行う．

静的コールグラフをたどり，見つけたメソッドを同

バイナリを入れ替える前に，トランポリン関数はじバイナリにまとめてコンパイルする．これにより，trampoline_info を MRAM に保存しておく．保存あるメソッドから直接呼び出されるメソッドだけでなした内容は，次のバイナリのトランポリン関数の 3

く，間接的に呼び出されるメソッドも同じバイナリにまとめることができる．こうすることでできるだけバ

木に追加し，検索を行う．検索を繰り返し行い，そのイナリの入れ替え回数を減らす．

間の実行時間を計測する．

子クラスがある場合は，子クラスのメソッドも同じ

この実験では，合計 1000 ノードからなる二分探索

バイナリに含める．例えば TreeNode クラスの子ク

木を作成し，深さ 5 までのノードを CPU で処理し，ラスが getKey メソッドをオーバーライドしている場

それより深いノードを DPU で処理した．検索は 500

合，それも同じバイナリにコンパイルすることで，二回行い，その実行時間を計測した．このベンチマーク

分木の中に子クラスのノードがある場合でも同じバでは，同じメソッドを繰り返し実行するためバイナリ

イナリを使うことができ，バイナリ入れ替えを削減での入れ替えは発生しなかった．

きる．

結果を表 1 に示す．実行時間は 31% 短縮された．
この原因を調べるために，探索の間の DPU での命

4 実験

令数とサイクル数を計測した．この結果も表 1 に示

本章では提案システムの性能を評価するために行っす．提案システムでは，命令数とサイクル数が先行研

た 2 つの実験の結果を示す．1 つ目の実験では，コ

究のシステムと比べて大幅に減っている．命令フェッ

ンパイル方式である提案システムとインタプリタ方

チやディスパッチのために実行していた命令がコンパ式である先行研究のシステムの実行時間を比較した．

イルすることによって不要になったためと考えられ評価には先行研究で用いられたベンチマークを使用

る．1 命令あたりにかかる平均サイクル数にはほとん

した．2 つ目の実験では，プログラムオーバーレイにど差がないことから，命令数の減少には MRAM ア

よるオーバーヘッドと，その内訳を調べた．

クセス命令も含まれると考えられる．平均サイクル数

実験は以下の構成の UPMEM システムで行った．

は主に MRAM アクセスの際の DMA によって大き• CPU: Intel Xeon Silver 4215 (2.50GHz)

くなるからである．

• DRAM: DDR4-2666 256GB• UPMEM PIM モジュール (バージョン v1A) ×20– DPU (350 MHz) 24 スレッド ×2560• UPMEM SDK: 2023.2.0• Java: OpenJDK 17.0.11
DPU は 1 つのみ使用した．また，DPU は 24 ス

4. 2 プログラムオーバーレイによるオーバーヘッド
プログラムオーバーレイを可能にするためにかかるオーバーヘッドは，次の 3 つに細分化できる．

• CPU に制御を戻しバイナリを入れ替えるオー
バーヘッド

レッドを持つが，1 スレッドのみを使用した．複数の• トランポリンによるオーバーヘッド

DPU の使用とマルチスレッド化は今後の課題である．

• Java スタックを MRAM に置くことによるオーバーヘッド

4. 1 インタプリタとの実行時間の比較

これらのオーバーヘッドを調べるために，それぞれコンパイルによってインタプリタよりどの程度高速のオーバーヘッドを分離して実験を行った．ベンチマーになったかを評価するために，提案システムと先行研

クには，図 10 に示す Tarai クラスを用いた．exec

究のインタプリタを用いたシステムでの実行時間をメソッドは，Tarai 関数 [7] として知られる，再帰的比較した．

な関数呼出しを繰り返す関数である．createObject

先行研究で用いられていたベンチマークは二分探により MRAM 内に Tarai クラスのインスタンスを

索木の探索である．木の根から一定の深さまでのノー

生成し，exec メソッドを呼出した．CPU から呼出ドは従来の DRAM に配置され，メソッド呼出しはしたときの exec の引数は (8, 4, 1) であり，exec

CPU で処理される．一定の深さよりも深いノードは

メソッドは 2345 回呼び出された．

MRAM に配置され，メソッド呼出しは DPU で処理

ベンチマークプログラムをコンパイルして得た Cされる．ランダムに生成したキーと値を持つノードを

言語コードに対して，3 種類変更を加えた．それぞ表1

提案システムと先行研究のシステムにおける二分探索木の平均実行時間および，DPU の平均命令数と平均サイクル数
システム

平均実行時間 (µs)

平均命令数

平均サイクル数

命令あたりサイクル数

53.7
70.3

2.19 × 103

4.12 × 104

5.41 × 103

1.02 × 105

18.8
18.9

提案
先行研究

public class Tarai {public int exec(int x, int y, int z) {3
if (x <= y)4return y;5else
6
return exec(exec(x - 1, y, z),7exec(y - 1, z, x), exec(z - 1, x, y));
8 } }
1
2

図 10

ベンチマークに使用した Tarai クラス

れ，上記で細分化したオーバーヘッドを調べるための変更である．その上で，変更を加えていないプログラ
ムを含む次の 4 つのプログラムを実行し，その実行時間を計測した．

• 変更を加えていないプログラム (proposed)

図 11

オーバーヘッドが異なる複数の Tarai ベンチマー
クプログラムに対する平均実行時間• is_in_same_binary の結果によらず CPU に制御を戻し，バイナリを IRAM にロードし直すプ
ログラム (always change)• トランポリンを利用せず，直接関数を呼び出すプ
ログラム (no trampoline)• Java ス タック を WRAM に 置 く プ ロ グ ラ ム(stack in wram)

5 関連研究Java プログラムの一部をアクセラレータにオフロードする研究として TornadoVM [4] がある．Tor-

nadoVM は GPU や FPGA などの異種アクセラレー
タにオフロードする計算を Java で記述するためのフ結果を図 11 に示す．always change はオーバー

レームワークである．プログラマは int などのプリヘッドが大きすぎるため，グラフに含めていない．

ミティブ型に対する計算をメソッドとして記述でき，

always change は proposed に比べて 210 倍時間がTornadoVM はそのメソッドを OpenCL のコードにかかった．proposed と no trampoline の実行時間

コンパイルして実行する．本研究は，TornadoVM がに大きな差はなく，no trampoline は stack in wram

対象としていない PIM チップを対象としている点，

より 2 倍時間がかかった．これらの結果から，バイ計算がプリミティブ型に限られず，PIM チップ内のナリ入れ替えに備えて MRAM に Java スタックをオブジェクトを扱える点で異なる．

置くオーバーヘッドは大きいが，トランポリンによる

PIM のプログラミングモデルに関して，Chen [3]オーバーヘッドは比較的小さいことがわかる．また，らは PIM と 分散コンピューティングとの類似性にバイナリ入れ替えが発生した場合のオーバーヘッドは着目し，gather などの通信機能や map などのイテ極めて大きく，バイナリ入れ替えを避けることが重要

レータ処理を個々の PIM チップの存在を意識するこであることがわかる．

となく利用できる C 言語フレームワークを提案している．このフレームワークは PIM のプログラムの記述を容易にする一方，オブジェクト指向ではない点，イテレータの処理ごとに別々のプログラムを記述する

必要がある点で本研究とは異なる．

よりバイナリ入れ替えによるオーバーヘッドを抑えプログラムメモリが小さいプロセッサに対してプることや，WRAM に Java スタックの一部をキャッログラムオーバーレイを適用する研究は，近年ではシュして実行時の MRAM アクセスをできるだけ減CELL Broadband Engine (CBE) アーキテクチャにらすことが挙げられる．

対して行われてきた．Baker [1] らは分割したプログ
ラムをアドレスの重複を許してメモリにマップする際謝辞 本研究の一部は，JSPS 科研費 23K24822 の助成を受けたものです．

のアルゴリズムを提案している．アドレスが重複した部分を必要なときに入れ替えながら実行する状況を

参 考 文 献想定し，入れ替えのコストのモデルに基づきオーバー[1] Baker, M. A., Panda, A., Ghadge, N., Kadne,A., and Chatha, K. S.: A performance modeland code overlay generator for scratchpad enhanced embedded processors, Proceedings of theeighth IEEE/ACM/IFIP international conference
on Hardware/software codesign and system synthesis, 2010, pp. 287–296.
[2] Bernhardt, A., Koch, A., and Petrov, I.: Pimdb:
From main-memory dbms to processing-in-memory
dbms-engines on intelligent memories, Proceedingsof the 19th International Workshop on Data Management on New Hardware, 2023, pp. 44–52.
[3] Chen, J., Gómez-Luna, J., Hajj, I. E., Guo, Y.,
and Mutlu, O.: Simplepim: A software frameworkfor productive and eﬃcient processing-in-memory,2023 32nd International Conference on Parallel Architectures and Compilation Techniques (PACT),IEEE, 2023, pp. 99–111.
[4] Fumero, J., Papadimitriou, M., Zakkak, F. S.,
Xekalaki, M., Clarkson, J., and Kotselidis, C.:
Dynamic application reconﬁguration on heterogeneous hardware, Proceedings of the 15th ACM SIGPLAN/SIGOPS International Conference on Virtual Execution Environments, 2019, pp. 165–178.
[5] Huang, W. and Ugawa, T.:
An ObjectOriented Programming Model for Processing-inMemory Computing in Java, Proceedings of the 40th
JSSST Annual Conference, 2023.
[6] Karakaya, K., Schott, S., Klauke, J., Bodden,
E., Schmidt, M., Luo, L., and He, D.: SootUp:
A Redesign of the Soot Static Analysis Framework, International Conference on Tools and Algorithms for the Construction and Analysis of Systems, Springer, 2024, pp. 229–247.
[7] Takeuchi, I.: On a recursive function that does almost recursion only, Memorandum, Musahino Electrical Communication Laboratory, Nippon Telephone and Telegraph Co., Tokyo, (1978).
[8] UPMEM: UPMEM, 2024. https://www.upmem.
com/technology/.

ヘッドが少なくなるようなマッピングを生成する．本
研究では静的コールグラフに基づき呼出し関係にあるメソッドを単にまとめてプログラムメモリにマップしたが，Baker らのアルゴリズムによりさらなる性能の向上の可能性がある．ただし対象とするアーキ
テクチャの違いからそのまま適用することは難しい．
これは今後の課題とする．

6 まとめ
本論文では，Java プログラムの一部を DPU にオ
フロードする上で課題となっていたインタプリタの実
行時オーバーヘッドとプログラムサイズの問題に対して，事前に DPU の機械語にコンパイルして実行するシステムを提案した．静的コールグラフ解析により呼出し関係にあるメソッドをまとめた複数のバイナ
リにコンパイルし，それらのバイナリをプログラム
オーバーレイにより入れ替えながら実行することで，プログラムサイズが小さい PIM チップでも実行できるようにしている．
評価では，提案システムが先行研究のインタプリタ
を用いたシステムに対して 31% の実行時間の短縮を達成した．また，プログラムオーバーレイによるオー
バーヘッドの内訳を明らかにし，バイナリ入れ替え
によるオーバーヘッドが支配的で，その次に MRAM
中の Java スタックにアクセスするオーバーヘッドが大きいことを示した．
今後の課題として，Baker [1] らのアルゴリズムに