SimAlpha : C++で 記述した

SimAlpha : C++で記述した
もうひとつのAlphaプロセッサシミュレータ
電気通信大学 大学院情報システム学研究科
吉瀬謙二 本多弘樹 弓場 敏嗣
2002-08-23 SWoPP 2002 発表資料
1
はじめに

アーキテクチャの研究、教育のツールとして様々なプロセッサ
シミュレータが利用されている。

定番の SimpleScalarは、高速なシミュレーションが目的の一つ
であり、変更が容易なコード記述にはなっていない。

シンプルで理解しやすく、変更が容易なコード記述を第一の条
件として、Alphaプロセッサシミュレータ SimAlpha Version 1.0 を
構築した。

本発表では SimAlpha の高いコード可読性を示すために、変更
を加えていないC++の本物のコードを示しながら、その実装内
部の説明を試みる。

利用列として、SPEC CINT95とCINT2000の理想的な命令レベ
ル並列性を測定した結果を報告する。
2
SimAlpha Version 1.0 の設計方針

可読性と拡張性






もう一つの実装



世の中のコードが可読性を考慮している訳ではない。
シンプルで理解しやすく、変更が容易なコード記述を第一の条件と
してゼロから記述した。
グローバル変数、goto文、条件付きコンパイルを利用していない。
インクルードファイルを含むソースはC++で約2800行と少ない。
SPEC CINT95 と CINT2000 のバイナリをシミュレートできる。
SimpleScalar の置換えをねらうのではなく、もう一つの実装を示す。
利用者が多くの選択肢の中から、適切なツールを選べる利点は大
きい。
機能レベルシミュレータ(クロックレベルではない)


アウトオブオーダ実行の性能評価シミュレータを考慮した記述
次の版において、性能評価シミュレータを提供予定
3
機能レベル(命令レベル)シミュレータ

Functional
simulation





Performance
modeling
SimpleScalar では sim-safe, sim-fast
動作検証
命令の実行頻度 (Instruction Mix)
命令レベルで測定した分岐予測のヒット率
命令レベルで測定したキャッシュシステムのヒット率
理想的な命令レベル並列性
Logic
design
Circuit
design
Layout
design
Figure 1. Typical flow in microprocessor design process. Interaction
between the process steps refines the performance model
throughout the process.
(IEEE Computer, February 2002, p 30)
4
SimAlpha のファイル構成
Line
Word
340
73
356
871
73
201
368
271
187
179
15
562
2968
193
1570
2948
199
626
1173
811
647
491
49
1234
3496
2727
12909
8178
Byte Filename
17992
1556
13174
21347
1882
5286
11142
7751
4505
4412
417
14871
COPYING
Makefile
README.txt
arithmetic.cc
chip.cc
debug.cc
define.h
etc.cc
instruction.cc
memory.cc
sim.cc
syscall.cc





ソースコードとヘッダファイ
ルの合計C++で2727行と非
常に少ない。
GNU GENERAL PUBLIC
LICENSE Version 2
arithmetic.cc 871行は加算
や減算などの命令実行の記
述で、理解は容易
syscall.cc 562行はシステム
コールの実装
これらを除いた核の部分は
1300行程度
104335 total
71613 total *.cc *.h
5
SimAlpha の実行と動作検証

Intel Linux 上で動作

SimpleScalar を用いた動作検証



1命令実行する度に、SimpleScalar と SimAlpha の全てのアーキ
テクチャステートを比較し、一致することを確認

SPEC CINT95と CINT2000を用いて動作検証
シミュレーション動作速度

sim-safe (SimpleScalar) の3倍の時間を必要とする。

シミュレーション開発の時間を短縮できれば、3倍程度の速度差
は問題にならない範囲
独自形式の実行イメージファイルを入力

Alphaバイナリと入力パラメータを用いて構築
6
SimAlpha の実行イメージファイル
独自形式の実行イメージ
/* SimAlpha 1.0 Image File */
ファイルを採用した。
/*** Registers ***/
 テキストファイルの前半にレ
/@reg 16 0000000000000003
ジスタの内容を列挙する。
/@reg 17 000000011ff97008
 後半でメモリの内容を列挙
/@reg 29 0000000140023e90
する。
/@reg 30 000000011ff97000
 テキスト形式、容量の大き
/@pc 32 0000000120007d80
いもので4MB程度
/*** Memory
***/
 ELF,COFF といった実行ファ
@11ff97000 00000003
イル形式やローダの知識が
@11ff97008 1ff97138
必要ない。
@11ff9700c 00000001
 初心者に優しい。

7
SimAlpha のソフトウェアアーキテクチャ

コードの高い可読性を示すために、変更を加えていないC++の本
物のコードを示しながら、SimAlpha の実装内部の説明を試みる。

メイン関数で生成されるオブジェクトchipのコンストラクタが、7つ
のオブジェクトを生成する。
simple_chip chip
instruction p
system_manager sys
architecture_state as
system_config sc
memory_system mem
evaluation_result e
SimAlpha Version 1.0 における7つのオブジェクトの参照関係
8
SimAlpha のメイン関数 : sim.cc
int main(int argc, char **argv){
if(argc==1) usage();
char *p
= argv[argc-1]; /* program name */
char **opt = argv;
/* options
*/
simple_chip *chip = new simple_chip(p, opt);
while(chip->step());
delete chip;
return 0;
}
実行例
SimAlpha -e10000 -v100 aout.txt
option


program name
シンプルな記述
グローバル変数を利用していない。
9
SimAlpha のメイン関数 : sim.cc
2つの実行イメージを作成する例
int main(int argc, char **argv){
if(argc==1) usage();
char *p
= argv[argc-1]; /* program name */
char **opt = argv;
/* options
*/
simple_chip *chipA = new simple_chip(p, opt);
simple_chip *chipB = new simple_chip(p, opt);
while(chipA->step() || chipB->step());
delete chipA;
delete chipB;
return 0;
}
10
simple_chip のコンストラクタ
simple_chip::simple_chip(char *prog, char **opt){
sc = new system_config(prog, opt);
e = new evaluation_result;
as = new architecture_state(sc, e);
mem = new memory_system(sc, e);
deb = new debug(as, mem, sc, e);
sys = new system_manager(as, mem, sc, e);
p
= new instruction(as, mem, sys, sc, e);
}
instruction p
system_manager sys
architecture_state as
system_config sc
memory_system mem
evaluation_result e
simple_chip で作られる6つのオブジェクトの参照関係
11
データオブジェクトとアーキテクチャステートの定義
class data_t{
uint64_t value;
public:
int cmov;
uint64_t ld();
int st(uint64_t);
int init(uint64_t);
};


SimAlpha の利用するデータはメモリを
含め 左に示す data_t型のオブジェク
トにより表現する。
データ値以外の特性を持たせることが
できる。様々なデータを収集できる。
class architecture_state{
public:
data_t pc;
/* program counter
*/
data_t r[32]; /* general purpose regs */
data_t f[32]; /* floating point regs */
architecture_state(system_config *,
evaluation_result *);
};


data_t型の配列
によりレジスタファ
イルを構成する。
整数レジスタ、浮
動小数点レジスタ、
プログラムカウン
タによりアーキテ
クチャステートを
定義
12
1つの命令を処理する関数 step のコード
int simple_chip::step(){
p->Fetch(&as->pc);
/*
p->Slot();
/*
p->Rename();
/*
p->Issue();
/*
p->RegisterRead();
/*
p->Execute(&as->pc); /*
p->Memory();
/*
p->WriteBack();
pipeline
pipeline
pipeline
pipeline
pipeline
pipeline
pipeline
stage
stage
stage
stage
stage
stage
stage
0
1
2
3
4
5
6
*/
*/
*/
*/
*/
*/
*/
/* split a conditional move,see README.txt */
execute_cmovb(p, as);
e->retired_inst++;
house_keeper(sys, sc, e, deb);
return sys->running;
}
Alpha 21264 の命令パイプラインを参考にして処理を分割した。
13
SimAlpha Version 1.0 のソフトウェアアーキテクチャ
simple_chip -> step() は、instruction の8つの関数を呼び出
すことにより1命令を処理する。
 instruction の8つの関数は、必要に応じてアーキテクチャス
テートを参照・更新する。
 重要な役割を果たすクラスinstructionの定義とコードを説明
する。

architecture_state
simple_chip -> step()
instruction
Fetch Slot Ren Issue RR
Ex Mem Wb
architecture_state
architecture_state, simple_chip -> step, instruction の関係
14
クラス instruction の定義
class instruction{
evaluation_result
architecture_state
system_manager
memory_system
INST_TYPE ir;
int Op;
int RA;
int RB;
int RC;
int ST;
int LD;
int LA;
int BR;
int Ai;
int Bi;
int Af;
int Bf;
int WF;
int WB;
data_t Npc;
data_t Imm;
data_t Adr;
data_t Rav;
data_t Rbv;
data_t Rcv;
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
*e;
*as;
*sys;
*mem;
32bit instruction code
Opcode field
Ra field of the inst
Rb field of the inst
Rc field of the inst
store inst ?
load inst ?
load address inst ?
branch inst ?
Rav is immediate ?
Rbv is immediate ?
Rav from floating-reg ?
Rbv from floating-reg ?
Write to the f-reg ?
Writeback reg index
Update PC or PC + 4
immediate
load & store address
Ra
Rb
Rc
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
public:
int Fetch(data_t *);
int Fetch(data_t *, INST_TYPE);
int Slot();
int Rename();
int Issue();
int RegisterRead();
int Execute(data_t *);
int Memory();
int WriteBack();
INST_TYPE get_ir();
int data_ld(data_t *, data_t *);
int data_st(data_t *, data_t *);
instruction(architecture_state *,
memory_system *,
system_manager *,
system_config *,
evaluation_result *);
};
15
命令フェッチステージのコード
int instruction::Fetch(data_t *pc){
mem->ld_inst(pc, &ir);
Npc.init(pc->ld() + 4);
return 0;
}


メモリ上の PC のアドレスから4バイトの命令を
フェッチし、変数 ir に格納する。
PC の値に4を加え、更新された PC を生成する。
16
スロットステージ(デコード)のコード
int instruction::Slot(){
Op = (ir>>26) & 0x3F;
RA = (ir>>21) & 0x1F;
RB = (ir>>16) & 0x1F;
RC = (ir
) & 0x1F;
WF = ((Op&MSK2)==0x14 || (Op&MSK2)==0x20);
LA = (Op==0x08 || Op==0x09);
LD = (Op==0x0a || Op==0x0b || Op==0x0c ||
(Op&MSK2)==0x20 || (Op&MSK2)==0x28);
ST = (Op==0x0d || Op==0x0e || Op==0x0f ||
(Op&MSK2)==0x24 || (Op&MSK2)==0x2c);
BR = ((Op&MSK4)==0x30);
WB = (LD || (Op&MSK2)==0x08 || Op==0x1a ||
Op==0x30 || Op==0x34) ? RA :
((Op&MSK3)==0x10 || Op==0x1c) ? RC : 31;
Af = (Op==0x15 || Op==0x16 || Op==0x17 ||
Op==0x1c ||
(Op&MSK2)==0x24 || (Op&MSK3)==0x30);
Bf = ((Op&MSK2)==0x14);
Ai = (Op==0x08 || Op==0x09 || LD);
Bi = (BR || (Op&MSK2)==0x10 && (ir & BIT12));


フェッチした命令 ir を
デコードする。
Verilog-HDL を意識し
た記述。
/** For the CMOV Split Code (CMOV1) **/
if(cmov_ir_create(ir)){
RB = RC;
Bi = 0;
}
return 0;
}
17
発行ステージ(即値計算)のコード
int instruction::Issue(){
DATA_TYPE Lit, D16, D21, tmp, d21e, d16e;
d21e = ((ir & MASK21) | EXTND21) << 2;
d16e = (ir & MASK16) | EXTND16;
Lit = (ir>>13) & 0xFF;
D21 = (ir & BIT20) ? d21e : (ir&MASK21)<<2;
D16 = (ir & BIT15) ? d16e : (ir&MASK16);
if(Op==0x09) D16 = (D16 << 16);
}
tmp = (LA||LD||ST) ? D16 : (BR) ? D21 : Lit;
Imm.init(tmp);
return 0;
 即値 Imm を計算

命令により以下から選択



8ビットの Lit
21ビットの符号拡張
16ビットの符号拡張
18
レジスタリードステージのコード
int instruction::RegisterRead(){
Rav = Ai ? Imm : Af ? as->f[RA] : as->r[RA];
Rbv = Bi ? Imm : Bf ? as->f[RB] : as->r[RB];
return 0;
}

デコードしたフラグに従って整数レジスタ、浮動小
数点レジスタ、即値のどれかを入力オペランドとし
て Rav, Rbv に格納する。
19
実行ステージのコード

Rcv の値を計算

ロードストア命令のためにメモ
リ参照アドレス Adr を計算

分岐命令では、分岐先アドレ
ス Tpc を計算
int instruction::Execute(data_t *Tpc){
/*** Update Rcv ***/
if(BR || Op==OP_JSR){
Rcv=Npc;
}
else if(!LD){
ALU(ir, &Rav, &Rbv, &Rcv);
}
/*** Update Adr ***/
Adr.init(0);
if(LD || ST){
ALU(ir, &Imm, &Rbv, &Adr);
}
/*** Update Tpc ***/
*Tpc = Npc;
if(Op==OP_JSR){
*Tpc = Rbv;
Tpc->st(Tpc->ld() & ~3ull);
}
if(BR){
BRU(ir, &Rav, &Rbv, &Npc, Tpc);
}
return 0;
}
20
メモリアクセスとライトバックのコード

ストア命令は Rav の内容をア
ドレス Adr にストア

ロード命令はアドレス Adr の
内容を Rcv にロード


int instruction::Memory(){
if(ST) data_st(&Adr, &Rav);
if(LD) data_ld(&Adr, &Rcv);
return 0;
}
int instruction::WriteBack(){
if(Op==OP_PAL){
PAL命令の場合には、それを
sys->execute_pal(this);
実行する関数をコール
}
Rcv の値をレジスタファイルに
ライトバック
if(!WF && WB!=31) as->r[WB] = Rcv;
if( WF && WB!=31) as->f[WB] = Rcv;
return 0;
}
21
SimAlpha Version 1.0 のソフトウェアアーキテクチャ
Line
871
73
201
368
271
187
179
15
562
Word
2948
199
626
1173
811
647
491
49
1234
Byte
21347
1882
5286
11142
7751
4505
4412
417
14871
architecture_state
Filename
arithmetic.cc
chip.cc
debug.cc
define.h
etc.cc
instruction.cc
memory.cc
sim.cc
syscall.cc
説明済み
ほぼ説明済み
説明済み
説明済み
simple_chip -> step()
instruction
Fetch Slot Ren Issue RR
Ex Mem Wb
architecture_state
22
SimAlpha の利用例



理想的な命令レベル並列性 (Oracle IPC)

制御依存関係や資源競合を解消し、データ依存関
係のみを制約として得られる並列性を測定

それぞれのデータについて、データフローグラフの
高さに相当する rank を計算
測定した結果を報告
拡張性を示す例
OP
OP
OP
Rank = 1
Rank = 1
OP
Rank = 2
Rank = 1
OP
Oracle IPC = 6/4 = 1.5
Rank = 3
OP
Max Rank = 4
23
ランクの計算方法

ランク値を保存する変数を data_t型に追加
class data_t{
uint64_t value;
 データが生成される時点で、ランク値を計算
public:
 全ての命令のコストを1として計算
int cmov;
uint32_t rank;
 追加したコードはわずか26行
uint64_t ld();
 データが生成される時点でランク値の最大値を計算
int
st(uint64_t);
 ランク値の最大値と実行命令数から並列性を計算
int init(uint64_t);
};
Rav
Rbv
Imm
Rbv
Rav Imm
Rbv
data_t型の拡張
OP
add
Adr
Rcv
mem
add
Adr
mem
Rcv
(a) Arithmetic
(a)
(b)
(c)
(b) Load
(c) Store
rank(Rcv) = max(rank(Rav), rank(Rbv)) + latency(OP)
rank(Rcv) = rank(Rbv) + latency(add) + latency(mem)
rank = max(rank(Rav), rank(Rbv) + latency(add))
命令タイプ毎の
ランクの計算方法
24
12 09
4.m 9.g
88 o
ks
i
12 12 m
9.c 6.g
om cc
pr
es
s
13
13 0.li
2.i
jp
13 eg
14 4.pe
7.v rl
or
t
16 ex
4.g
z
17 ip
5.v
17 pr
6.g
18 cc
1
18 .mc
6.c f
19 raf
7.p ty
ar
s
25 er
25 2.e
3.p on
er
lbm
25 k
25 4.ga
5.v p
o
25 rtex
6.b
z
30 ip2
0.t
wo
lf
Oracle IPC
理想的な命令レベル並列性の測定結果
120
107
60
41.8
40
20
20
10.5
108
100
80
64.2
56.6
53.1
43.3
47.1
32
49.7
43.6
30.9
32.1
25.1
16.9
29.3
21.9
8.4
0
25
おわりに




プロセッサアーキテクチャ研究とプロセッサ教育における
利用を目的として、プロセッサシミュレータ SimAlpha
Version 1.0 を構築した。
実際のC++のコードを示しながらソフトウェアアーキテク
チャを明らかにした。
利用例として、理想的な命令レベル並列性を測定する方
法とSPEC CINT95, CINT 2000の測定結果を示した。
もうひとつのAlphaプロセッサシミュレータとして、プロセッ
サ研究の選択肢の一つ
謝辞
SimAlpha の初期のバージョンをテストし、貴重なコメントやパッチを頂
いた飯塚大介氏に深く感謝致します。
26
SimAlpha のダウンロード

SimAlpha Version 1.0 のダウンロード

Version 1.0 のソースコードと、理想的な命令レベル並列性を得る
ために変更を加えたソースコードは次のURLからダウンロードで
きる。
http://www.yuba.is.uec.ac.jp/~kis/SimAlpha/

SimAlpha Version 1.0 に関する記述

吉瀬謙二, 本多弘樹, 弓場敏嗣:
SimAlpha: シンプルで理解しやすいコード記述を目指した Alpha
プロセッサシミュレータ,
Technical Report UEC-IS-2002-2,
電気通信大学 大学院情報システム学研究科 (July 2002).
27