このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
ソフトウェアパイプライン (SWPL) を、Don’t Repeat Yourself で書くというお話です。
サンプルとして次のプログラムを考えます。
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define NUM_DATA 8
#define TRACE(i, c) trace(i, c)
void trace(int i, char c) {
for (int j = 0; j < 2*i; ++j)
putchar(' ');
putchar(c);
putchar('\n');
}
int main(int argc, char** argv) {
int input[NUM_DATA];
int output[NUM_DATA];
// Initialize
for (int i = 0; i < NUM_DATA; ++i) {
input[i] = i;
}
// Target Loop
{
int r1, r2;
for (int i = 0; i < NUM_DATA; ++i) {
r1 = input[i]; TRACE(i, 'L');
r2 = r1 + r1; TRACE(i, 'C');
output[i] = r2; TRACE(i, 'S');
}
}
// Test
for (int i = 0; i < NUM_DATA; ++i) {
int v = 2 * input[i];
if (output[i] != v) {
fprintf(stderr, "Error: i=%d: %d != %d\n", i, output[i], v);
return 1;
}
}
return 0;
}
入力を2倍して出力するだけのプログラムです。
Target Loop の部分を変更して、SWPLを実装します。
実行順がわかりやすいように、TRACE でログを出力しています。
次のように、行で実行順、桁でループのiの値、文字で Load, Calc, Store のどれを行ったかを表すチャートが出力されます。
L
C
S
L
C
S
L
C
S
L
C
S
L
C
S
L
C
S
L
C
S
L
C
S
SWPL化の準備として、i==0 の時の実行だけ、ループの外に出しましょう。
同じ処理を2回書くのはいやなので、マクロにしてしまいます。
ついでに、Load, Calc, Store のどのパートを実行するか、マクロの引数で切り替えられるようにしましました(今回は1行を1つのパートにに分けましたが、実戦的には、複数行を1つのパートにしたり、パート数も3とは限らず必要に応じて設定したりします)。
// Target Loop
{
#define BODY(i, L, C, S) BODY_((i), (L), (C), (S))
#define BODY_(i, L, C, S) \
if (L) { \
r1 = input[i]; TRACE(i, 'L'); \
} \
if (C) { \
r2 = r1 + r1; TRACE(i, 'C'); \
} \
if (S) { \
output[i] = r2; TRACE(i, 'S'); \
} \
/* end of macro */
int r1, r2;
int i = 0;
if (i < NUM_DATA) {
BODY(i, 1, 0, 0); /*L=1*/
BODY(i, 0, 1, 0); /*C=1*/
BODY(i, 0, 0, 1); /*S=1*/
for (++i; i < NUM_DATA; ++i) {
BODY(i, 1, 0, 0); /*L=1*/
BODY(i, 0, 1, 0); /*C=1*/
BODY(i, 0, 0, 1); /*S=1*/
}
}
#undef BODY
#undef BODY_
}
i==0 の処理をループの外に出しただけなので、実行しても TRACE の出力結果は変わっていません。
それでは、
各iのStore処理を、i+1のLoad処理の後ろに移動してSWPLしましょう。マクロの定義は変わりません。
int r1, r2;
int i = 0;
if (i < NUM_DATA) {
BODY(i, 1, 1, 0); /*L=1, C=1*/
for (++i; i < NUM_DATA; ++i) {
BODY(i, 1, 0, 0); /*L=1*/
BODY(i-1, 0, 0, 1); /*S=1*/
BODY(i, 0, 1, 0); /*C=1*/
}
BODY(i-1, 0, 0, 1); /*S=1*/
}
S=1 の行を移動させました。移動後の文脈ではiの値が増えているので、-1しています。なお、i==0の時のL=1, C=1は、2行に分ける必要がないのでまとめまています。
TRACEの結果は…
L
C
L
S
C
L
S
C
L
S
C
L
S
C
L
S
C
L
S
C
L
S
C
S
きれいにSWPL化されました。
コンピュータビジョンセミナーvol.2 開催のお知らせ - ニュース一覧 - 株式会社フィックスターズ in Realizing Self-Driving Cars with General-Purpose Processors 日本語版
[…] バージョンアップに伴い、オンラインセミナーを開催します。 本セミナーでは、...
【Docker】NVIDIA SDK Managerでエラー無く環境構築する【Jetson】 | マサキノート in NVIDIA SDK Manager on Dockerで快適なJetsonライフ
[…] 参考:https://proc-cpuinfo.fixstars.com/2019/06/nvidia-sdk-manager-on-docker/ […]...
Windowsカーネルドライバを自作してWinDbgで解析してみる① - かえるのほんだな in Windowsデバイスドライバの基本動作を確認する (1)
[…] 参考:Windowsデバイスドライバの基本動作を確認する (1) - Fixstars Tech Blog /proc/cpuinfo ...
2021年版G検定チートシート | エビワークス in ニューラルネットの共通フォーマット対決! NNEF vs ONNX
[…] ONNX(オニキス):Open Neural Network Exchange formatフレームワーク間のモデル変換ツー...
YOSHIFUJI Naoki in CUDAデバイスメモリもスマートポインタで管理したい
ありがとうございます。別に型にこだわる必要がないので、ユニバーサル参照を受けるよ...