Cerium による並列処理向け I/O の設計と実装

Masataka Kohagura 12th, February

担当教官 : 河野 真治

研究背景と目的

近年のCPUのほとんどはマルチコアであり、それらの性能を引き出すためには並列プログラミングが必須となっている。そこで当研究室では Cerium Library の開発を行い、提供することによって並列プログラミングを容易にしている。

先行研究では Task の並列化によって、プログラム全体の処理速度は向上している。しかし、ファイル読み込み等の I/O と Task が並列に動作するようにはされていない。

現状では、ファイルを全て memory に mapping を行ってから Task が走るようになっているので、非常に大きいサイズのファイルを読み込むと、ファイルを memory に mapping するまでの時間がオーバーヘッドになってしまう。

本研究では I/O と Task が並列に動作するような設計、実装によってプログラム全体の 処理速度を上げていく。

Cerium とは

Cerium の流れ

  1. Taskを生成
  2. 依存関係のチェック
  3. Schedulerに転送
  4. 並列実行

Cerium Task の生成(1)


main.cc の記述

// Task の宣言
HTaskPtr multiply = manager->create_task(MULTIPLY_TASK);
// Task を実行する デバイスの設定
multiply->set_cpu(SPE_ANY);
// Task に入力データのアドレスを追加
multiply->set_inData(0, i_data1, sizeof(float)*length);
multiply->set_inData(1, i_data2, sizeof(float)*length);
// Task に出力データのアドレスを追加
multiply->set_outData(0, o_data1, sizeof(float)*length);
// Task へ値を1つだけ渡す
multiply->set_param(0,(long)length);
// Task を TaskList に set する
multiply->spawn(); 

Cerium Task の生成(2)


Task の記述

static int
multiply(SchedTask *s,void *rbuf, void *wbuf)
{
    // 登録した inData を取得
    float indata1=(float*)s->get_input(rbuf,0);
    float indata2=(float*)s->get_input(rbuf,1);
    // 登録した outData を取得
    float outdata=(float*)s->get_output(wbuf,0);
    // 登録した param を取得
    long  length=(long)s->get_param(0);
    for (int i=0;i < length;i++) {
        outdata[i]=indata1[i]*indata2[i];
    }
    return 0;
} 

並列処理向け I/O の 設計と実装


mmap での I/O の実装(1)


FileRead.h の一部

typedef struct fileRead {
    struct fileRead *self;
    long fd;
    long division_size;
    long file_size;
        ・・・
    char *read_text;
    CPU_TYPE cpu;
} FileRead, *FileReadPtr;

Fileを読み込む際にファイルの情報や、Read Task の情報を構造体の中に格納しておく。

mmap での I/O の実装(2)


mmap の記述

mmap(SchedTask *s, void *in, void *out)
{
    FileReadPtr fr = (FileReadPtr)in;
    int map = MAP_PRIVATE;

    fr->read_text =
        (char*)mmap(NULL,fr->filesize,PROT_READ,map,fr->fd,(off_t)0);
}

mmap の仕様

  1. code がシンプル (memory を自分自身で malloc せず、read を書いて読み込まなくていいため)
  2. memory より大きなファイルは開けない
  3. ファイルを一度で memory に対して mapping する
  4. read の先読みがOS依存

Block Read の設計(1/2)



Block Read の設計(2/2)



Block Read の実装(2)


Block Read の実装

HTaskPtr t_read = manager->create_task(READ_TASK);
t_read->set_cpu(read_spe_cpu);
t_read->set_param(0,w->fd);
t_read->set_param(1,w->task_spawned*w->division_size);
t_read->set_outData(0, w->file_mmap + w->task_spawned * w->division_size,
                        w->task_blocks * w->division_size);

run_tasks(manager,w, w->task_blocks, t_read, t_next,
                     w->division_size + w->extra_len);

t_read->set_param(2,w->task_spawned*w->division_size + w->extra_len);

t_read->spawn();

Block Read の実装(3)


Block Read の記述

static int
read_task(SchedTask *s, void *rbuf, void *wbuf)
{
    long fd = (long)s->get_param(0);
    long start_read_position = (long)s->get_param(1);
    long end_read_position = (long)s->get_param(2);
    char *read_text = (char*)s->get_output(wbuf,0);
    long read_size = end_read_position - start_read_position;

    pread(fd, read_text, read_size , start_read_position);
    return 0;
}

ベンチマーク


考察

まとめ


大学院に進学してしたいこと