l'essentiel est invisible pour les yeux

Saturday, February 17, 2007

[Cell] アセンブリレベルでのSPUプログラミングチューニング::コンパイル編

CELLで本格的にプログラムを書くための低レイアに関する知識が全く無いことに気づいたため、1ヶ月半ほど低レイア関連の勉強をしていました。久しぶりのCELLに関するエントリです。

SPUプログラミングのチューニングはCELLプログラミングにおいて大変重要です。
そのためには、SPUのアーキテクチャ・アセンブリ・ABIを知らないと何もできません。
まずは前提知識となる参考資料のリンク集です。


とりあえず、あとでよむ。

このエントリでは、
  1. 簡単なSPUプログラムを作成する。
  2. アセンブリコードの作成
  3. 奇数/偶数パイプラインでの命令2重実行に関するアセンブリレベルでの静的プロファイリング
  4. アセンブリから実行可能オブジェクトファイルの作成
を扱う。

基本だけどアセンブリレベルでのチューニング&コンパイルができるようになれば、低いレベルでのチューニングが可能になる。


まず、SPUプログラムhello.cを作成する。

hello.c

#include <stdio.h>
#include <stdint.h>
#include <profile.h>

int main(uint64_t speid __attribute__((unused)), uint64_t argp __attribute__((unused))) {
prof_cp0();
prof_cp30();
printf("Hello, world from SPU.\n");
prof_cp31();
return 0;
}


Makefile

PROGRAM_spu = hello
LIBRARY_embed = hello.a
include /opt/ibm/cell-sdk/prototype/make.footer


C言語のソースファイルからアセンブリコードを生成する
CBE SDK付属のMakefileを使用して、アセンブリコードを生成するには次のように実行する。SPU_TIMING=1は必須ではないが、設定することで奇数/偶数パイプラインでのCPUサイクル単位での命令実行の静的プロファイリング結果を得ることができる。ファイル名は#{プログラム名}.s.timmingとなる。

% SPU_TIMING=1 make hello.s
/opt/cell/toolchain-3.3/bin/spu-gcc -W -Wall -Winline -Wno-main -I. -I /opt/ibm/cell-sdk/prototype/sysroot/usr/spu/include -include spu_intrinsics.h -O3 -S hello.c ; /opt/ibm/cell-sdk/prototype/bin/spu_timing -running-count hello.s
%


hello.s : 作成されたアセンブリ
SPUは3アドレス方式のアーキテクチャです。$0~$127は128bit幅のレジスタファイルを参照します。

.file "hello.c"
.section .rodata.str1.16,"aMS",@progbits,1
.align 4
.LC0:
.string "Hello, world from SPU."
.text
.align 3
.global main
.type main, @function
main:
hbra .L3,puts
stqd $lr,16($sp)
stqd $sp,-32($sp)
ai $sp,$sp,-32
and $0,$0,$0; lnop
and $30,$30,$30; lnop
ila $3,.LC0
.L3:
brsl $lr,puts
and $31,$31,$31; lnop
ai $sp,$sp,32
fsmbi $3,0
lqd $lr,16($sp)
bi $lr
.size main, .-main
.ident "GCC: (GNU) 4.1.1"


CPUサイクル単位でのパイプラインごとの命令実行を示すspu_timingファイル。
[番号][パイプライン] [クロック] [オペランド] [オペコード]の順に並んでいて、0が偶数パイプ、1が奇数パイプラインを表す。Dとついている場合は、偶数パイプ命令と奇数パイプ命令が2命令同時に実行されていることを表している。偶数/奇数パイプで2命令同時することで性能が向上する。

.file "hello.c"
.section .rodata.str1.16,"aMS",@progbits,1
.align 4
.LC0:
.string "Hello, world from SPU."
.text
.align 3
.global main
.type main, @function
main:
000000 1 012345 hbra .L3,puts
000001 1 123456 stqd $lr,16($sp)
000002 1 234567 stqd $sp,-32($sp)
000003 0 34 ai $sp,$sp,-32
000004 0 45 and $0,$0,$0; lnop
000005 0 56 and $30,$30,$30; lnop
000006 0D 67 ila $3,.LC0
.L3:
000006 1D 6789 brsl $lr,puts
000007 0 78 and $31,$31,$31; lnop
000008 0 89 ai $sp,$sp,32
000009 1 9012 fsmbi $3,0
000010 1 012345 lqd $lr,16($sp)
000016 1 -----6789 bi $lr
.size main, .-main
.ident "GCC: (GNU) 4.1.1"


上記の静的プロファイリング結果を元に、アセンブリプログラム(hello.s)に変更を加えた際にはアセンブリプログラムからオブジェクトコードを生成する必要がある。

そこでアセンブリコードから目的のオブジェクトファイルを生成するためのシェルスクリプトを作成する。このファイルはmakeコマンドの実行結果を貼り付けソースファイル名を「ソースファイル.s」に変更する。

※CBE SDKで用意されているMakefileを使用してアセンブリからオブジェクトコードを生成するもっとエレガントな方法があるかもしれません。
make

% cat > make-obj-file
/opt/cell/toolchain-3.3/bin/spu-gcc -W -Wall -Winline -Wno-main -I. -I /opt/ibm/cell-sdk/prototype/sysroot/usr/spu/include -include spu_intrinsics.h -O3 -c hello.s
/opt/cell/toolchain-3.3/bin/spu-gcc -o hello hello.o -Wl,-N
/opt/cell/toolchain-3.3/bin/ppu-embedspu -m32 hello hello hello-embed.o
/opt/cell/toolchain-3.3/bin/ppu-ar -qcs hello.a hello-embed.o
% chmod +x make-obj-file

コンパイル

% ./make-obj-file


SPUは演算実行パイプラインを2本持っています。
それぞれの役割は次の通りです。

偶数パイプ
  • 論理演算
  • 整数加減算
  • 浮動小数点演算
  • 整数乗算
  • SIMDシフト
  • SIMDロテート


奇数パイプ
  • ロード・ストア
  • チャネル命令
  • 128ビットシフト
  • 128ビットロテート
  • シャッフル演算
  • 分岐命令


と、ここから実際にアセンブリレベルでチューニングを開始するには、SPUのアーキテクチャ・アセンブリ言語・ABIに関する理解が必須です。CELLは本当に技術者心をくすぐってくれます。

再掲。参考リンク集です。
プリントアウトして手元においておくのがよさそうです。
4つ目はMITでのCELLプログラミングに関する講義資料です。