はじめてのM-Coreプログラミング

最初のプログラム

mcore

M-Coreは多数の計算ノードを持つアーキテクチャを採用しています. M-Coreでプログラムを実行する場合, 図左のようにすべてのノードがプログラムを並列に実行していきます. また,ノード間で通信することによって, 複数のノードで1つの問題を解くことが出来ます. 各ノードは図右のように2次元メッシュ状に配置され, ノードにはノードID(x,y)が割り振られます. 今回はComp.Nodeのみを使用するので,ノードIDは(1,1)から始まります.

まず,Hello, world!を表示させるプログラムを見ていきましょう. このプログラムではノードIDとノードサイズをhello worldと一緒に表示させます.

$ cd SimMc/app/sample/hello/
$ cat hello.c
#include 
#include "MClib.h"

int main(int argc, char *args[])
{
    int id_x;    /* カレントプロセスIDのx座標 */
    int id_y;    /* カレントプロセスIDのy座標 */
    int rank_x;  /* 構成ノードの横サイズ */
    int rank_y;  /* 構成ノードの縦サイズ */
    MC_init(&id_x, &id_y, &rank_x, &rank_y);

    printf("Hello, world! id(%d,%d), node size(%d,%d).\n",
           id_x, id_y, rank_x, rank_y);

    MC_finalize();
    return 0;
}

ノードIDとノードサイズはMClibライブラリに含まれる MC_init関数によって取得できます. MClibライブラリには, 並列プログラムを開発するための基本的な関数群が定義されています. MClibライブラリを使用するために, libディレクトリにあるMClib.cをコンパイルします.

$ cd SimMc/lib/
$ make

hello worldのコンパイル

hello worldをコンパイルして,実行してみましょう. コンパイルには"-static"オプションが必要です. また"MC"から始まる関数を使用するために, インクルードパスにSimMc/libを設定し, SimMc/lib/MClib.oを一緒にコンパイルしてください.

$ cd SimMc/app/sample/hello/
$ mipsel-linux-gcc -static hello.c ../../../lib/MClib.o -I../../../lib/ -o hello.out

簡単にコンパイルするために,Makefileを用意してあります. 自分のプログラムをコンパイルしたい場合, このMakefileを修正して使うと便利です. includeにインストール時に修正したbase.mkを指定し, TARGETに実行ファイルの名前,SRCにソースコードを指定してください.

$ cat SimMc/app/sample/hello/Makefile
##########################################################################
## Many-Core Architecture Research Project         Arch Lab. TOKYO TECH ##
##########################################################################
include ../../../base.mk

TARGET = hello.out
SRC    = hello.c

hello worldの実行

helloworldを実行してみましょう. SimMcに実行ファイルを渡すことで実行できます.

$ cd SimMc/app/sample/hello/
$ make
$ SimMc hello.out
## SimMc M-Core alpha : Many-Core and NoC Simulator v1.1 2010-07-30
## [SimMips: Simple Computer Simulator of MIPS Version 0.5.0 2008-11-05]
## DEBUG MODE 0, LOG MODE 0
## comp node (1,1) - (4,4)
## memory node (0,0)
## path node (0,1) - (0,4), (1,0) - (4,0)
## Multi-Core library MClib v1.0.0 2009-12-16
Hello, world! id(2,1), node size(4,4).
Hello, world! id(3,1), node size(4,4).
Hello, world! id(4,1), node size(4,4).
Hello, world! id(2,2), node size(4,4).
Hello, world! id(3,2), node size(4,4).
Hello, world! id(4,2), node size(4,4).
Hello, world! id(2,3), node size(4,4).
Hello, world! id(3,3), node size(4,4).
Hello, world! id(4,3), node size(4,4).
Hello, world! id(2,4), node size(4,4).
Hello, world! id(3,4), node size(4,4).
Hello, world! id(4,4), node size(4,4).
Hello, world! id(1,2), node size(4,4).
Hello, world! id(1,3), node size(4,4).
Hello, world! id(1,4), node size(4,4).
Hello, world! id(1,1), node size(4,4).
## Simulation time    0.105 [sec]
## Simulation cycle    20203
## Simulation  192.108 kilo cycle / sec

SimMcをオプションなしで実行すると, 4×4のノード構成でプログラムを実行します. 16ノードで実行した結果,16個のHello,world!が出力されました.

データ通信

MC_dma_put

並列プログラムで重要となるデータ通信を行ってみましょう. データの送信にはDMA転送を使います. DMAを使用すると通信相手のメモリを直接書き換えることができます.

プログラムでDMAを発行する場合, MC_dma_putコマンドを使用します.

void MC_dma_put(int remote_id, void *remote_addr, void *local_addr, size_t size, int remote_stride, int local_stride)

第1引数に通信相手のID,第2引数に通信相手のアドレス, 第3引数に送りたいデータのアドレス,第4引数にデータサイズを指定してください. 第5引数以降はここでは説明しません. 通常は,第5引数と第6引数には4を設定してください. 通信相手のIDはseidxy関数を使用して取得できます.

void setidxy(int *id, int x, int y)

それでは実際のプログラムを見ていきます. 次のプログラムではノード(1,1)が右下のノードへデータを送信します.

$ cat SimMc/app/sample/dma/dma.c
#include 
#include "MClib.h"

#define SIZE 16
volatile int array[SIZE];

int main(int argc, char *args[])
{
    int i;
    int id_x, id_y, rank_x, rank_y;
    MC_init(&id_x, &id_y, &rank_x, &rank_y);

    if (id_x == 1 && id_y == 1) {
        for (i = 0; i < SIZE; i++)
            array[i] = i + 1;

        int dst;
        setidxy(&dst, rank_x, rank_y);
        MC_dma_put(dst, (void*)array, (void*)array, SIZE * sizeof(int), 4, 4);
    }

    if (id_x == rank_x && id_y == rank_y) {
        while(array[SIZE - 1] == 0);

        for(i = 0; i < SIZE; i++)
            printf("array[%d] %4d\n", i, array[i]);
    }

    MC_finalize();
    return 0;
}

このプログラムはノード(1,1)が右下のノードに向かってDMA転送を行うものです. 右下のノードはデータが転送されるまでwhileループで待ち, データが転送されたらそのデータを表示します.

volatile

volatile int array[SIZE];
while(array[SIZE - 1] == 0);

上のソースコードで気になる部分はvolatileという見慣れな修飾子だと思います. SimMcでは, 他のノードによって書き換えられるメモリに対する最適化を防ぐために, volatileを使用します. volatileが付けられた変数はレジスタに値が残っていても, 常にメモリから読み込みを行います. これによって,無限ループに見えるwhileループを 最適化から保護することが出来ます.

実行結果

コンパイルして実行してみましょう.1から16までのデータが送信できていれば成功です.

$ cd SimMc/app/sample/dma
$ make
$ make run
## SimMc M-Core alpha : Many-Core and NoC Simulator v1.1 2010-07-30
## [SimMips: Simple Computer Simulator of MIPS Version 0.5.0 2008-11-05]
## DEBUG MODE 0, LOG MODE 0
## comp node (1,1) - (2,2)
## memory node (0,0)
## path node (0,1) - (0,2), (1,0) - (2,0)
## Multi-Core library MClib v1.0.0 2009-12-16
array[0]    1
array[1]    2
array[2]    3
array[3]    4
array[4]    5
array[5]    6
array[6]    7
array[7]    8
array[8]    9
array[9]   10
array[10]   11
array[11]   12
array[12]   13
array[13]   14
array[14]   15
array[15]   16
## Simulation time    0.090 [sec]
## Simulation cycle    70362
## Simulation  778.306 kilo cycle / sec

M-Coreトップ

Copyright(c) 2010, Tokyo Tech Kise Laboratory. All rights reserved.