Gears OS の並列処理

伊波 立樹 琉球大学理工学研究科 河野研

並列処理の重要性

Gears OS

Gears OS

Code Gear/Data Gear

message

メタ計算

Meta Gear

message

Continuation based C

Continuation based C

__code cg0(int a, int b) {
    goto cg1(a+b);
}

__code cg1(int c) {
    goto cg2(c);
}

Data Gear の表現

/* data Gear define */
union Data {
    struct Timer {
        union Data* timer;
        enum Code start;
        enum Code end;
        enum Code next;
    } Timer;
    struct TimerImpl {
        double time;
    } TimerImpl;
    ....
};

Context

stub Code Gear

message

Interface

Interface の定義

typedef struct Queue<Impl>{
        // Data Gear parameter
        union Data* queue;
        union Data* data;
        __code next(...);
        __code whenEmpty(...);

        // Code Gear
        __code clear(Impl* queue, __code next(...));
        __code put(Impl* queue, union Data* data, __code next(...));
        __code take(Impl* queue, __code next(union Data*, ...));
        __code isEmpty(Impl* queue, __code next(...), __code whenEmpty(...));
} Queue;

Interface の定義

typedef struct Queue<Impl>{
        // Data Gear parameter
        union Data* queue;
        union Data* data;
        __code next(...);
        __code whenEmpty(...);

        // Code Gear
        __code clear(Impl* queue, __code next(...));
        __code put(Impl* queue, union Data* data, __code next(...));
        __code take(Impl* queue, __code next(union Data*, ...));
        __code isEmpty(Impl* queue, __code next(...), __code whenEmpty(...));
} Queue;

Interface の実装

Queue* createSingleLinkedQueue(struct Context* context) {
    struct Queue* queue = new Queue(); // Allocate Queue interface
    struct SingleLinkedQueue* singleLinkedQueue = new SingleLinkedQueue(); // Allocate Queue implement
    queue->queue = (union Data*)singleLinkedQueue;
    singleLinkedQueue->top  = new Element();
    singleLinkedQueue->last = singleLinkedQueue->top;
    queue->clear = C_clearSingleLinkedQueue;
    queue->put  = C_putSingleLinkedQueue;
    queue->take  = C_takeSingleLinkedQueue;
    queue->isEmpty = C_isEmptySingleLinkedQueue;
    return queue;
}

Interface を利用した Code Gear の呼び出し

__code code1() { 
    Queue* queue = createSingleLinkedQueue(context);
    Node* node = new Node();
    node->color = Red;
    goto queue->put(node, code2);
}

Interface を利用した Code Gear の呼び出し(スクリプト変換後)

__code code1(struct Context *context) {
    Queue* queue = createSingleLinkedQueue(context);
    Node* node = &ALLOCATE(context, Node)->Node;
    node->color = Red;
    Gearef(context, Queue)->queue = (union Data*) queue;
    Gearef(context, Queue)->data = (union Data*) node;
    Gearef(context, Queue)->next = C_code2;
    goto meta(context, queue->put);
}

Interface での stub Code Gear

// implement put code gear
__code putSingleLinkedQueue(struct Context *context,struct SingleLinkedQueue* queue,
                            union Data* data, enum Code next) {
    ...
}

// generated by script
__code putSingleLinkedQueue_stub(struct Context* context) {
	SingleLinkedQueue* queue = (SingleLinkedQueue*)GearImpl(context, Queue, queue);
	Data* data = Gearef(context, Queue)->data;
	enum Code next = Gearef(context, Queue)->next;
	goto putSingleLinkedQueue(context, queue, data, next);
} 

並列処理の構成

Task

TaskManger

message
  1. Task を Input Data Gear としてTaskManager の spawn を呼び出す
  2. Task が待っている Data Gear のカウンタである IDGCount をチェックする
  3. IDGCount が0の場合 Data Gear が 揃っているので Worker の Queue に Task を送信する

Worker

message
  1. Worker は Queue から Task(Context)を取得する
  2. Worker の Context からTask の Context へ入れ替える
  3. Task に設定されている Code Gear を実行
  4. Task の Output Data Gear の書き出し
  5. Task Context から Worker の Context へ入れ替える
  6. Worker は再び Queue から Task を取得する

Synchronized Queue

message

依存関係の解決

message
  1. Task に設定されている Code Gear を実行する
  2. Output Data Gear の書き出し処理を行う際にメタレベルの Queue を参照する
  3. 依存関係にある Task を取り出し、IDGCount をデクリメントする

並列構文

par goto 構文

__code code1(Integer *integer1, Integer * integer2, Integer *output) {
    par goto add(integer1, integer2, output, __exit);
    goto code2();
}

CUDA への対応

CUDAWorker

CUDAExecutor

typedef struct Executor<Impl>{
    union Data* Executor;
    struct Context* task;
    __code next(...);
    // method
    __code read(Impl* executor, struct Context* task, __code next(...));
    __code exec(Impl* executor, struct Context* task, __code next(...));
    __code write(Impl* executor, struct Context* task, __code next(...));
}

CUDABuffer

message

CUDA での呼び出し

message

Gears OS の評価

Twice

Twice の結果

Processor Time(ms)
1 CPU 1181.215
2 CPUs 627.914
4 CPUs 324.059
8 CPUs 159.932
16 CPUs 85.518
32 CPUs 43.496
GPU 127.018
GPU(kernel only) 6.018

BitonicSort

BitonicSort の結果

Processor Time(s)
1 CPU 41.416
2 CPUs 23.340
4 CPUs 11.952
8 CPUs 6.320
16 CPUs 3.336
32 CPUs 1.872
GPU 5.420
GPU(kernel only) 0.163

OpenMP との比較

#pragma omp parallel for
for(int i = 0; i < length; i++) {
    a[i] = a[i] * 2;
}

Go 言語との比較

c := make(chan []int)
for i :=0; i < *split; i++ {
    // call goroutine
    go twice(list, prefix, i, c);
}

func twice(list []int, prefix int, index int, c chan []int) {
    for i := 0; i < prefix; i++ {
        list[prefix*index+i] = list[prefix*index+i] * 2;
    }
    c <- list
}

まとめ

今後の課題

今後の課題

message

データ並列

Task 間の同期処理

message