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);
}

Context

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;
    ....
};

stub Code Gear

// normal level Code Gear
__code cg0(struct Context* context, struct Integer integer, struct Queue queue) {
    ...
}

// meta level stub Code Gear
__code cg0_stub(struct Context* context) {
    // get data index number
    Integer integer = &context->data[context->dataNum]->Integer
    // get enum data
    Queue* queue = &context->data[Queue]->Queue;
    // continuation Code Gear
    goto cg0(context, integer, queue);
}

Context での stub Code Gear の記述の問題点

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 の実装

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 putSingleLinkedQueue(struct SingleLinkedQueue* queue, union Data* data, __code next(...)) {
    Element* element = new Element();
    element->data = data;
    element->next = NULL;
    queue->last->next  = element;
    queue->last = element;
    goto next(...);
}

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

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

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_queueTest2;
    goto meta(context, queue->put);
}

Interface での stub Code Gear

__code putSingleLinkedQueue(struct Context *context,struct SingleLinkedQueue* queue, union Data* data, enum Code next) {
    Element* element = &ALLOCATE(context, Element)->Element;
    element->data = data;
    element->next = NULL;
    queue->last->next  = element;
    queue->last = element;
    goto meta(context, 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(Context)

__code add(struct Integer* input1, struct Integer* input2, __code next(struct Integer* output, ...)) {
    output->value = input1->value + input2->value;
    goto next(output, ...);
}

TaskManger

message
  1. Task を Input Data Gear として
  2. TaskManager の spawn を呼び出す
  3. Input Data Gear が揃っているかを確認する
  4. 揃っている場合、 Worker の Queue に
  5. Task を送信する

Worker

message
  1. Worker は Queue から Task を取得する
  2. Worker の Context から
  3. Task の Context へ入れ替える
  4. Task の Code Gear を実行
  5. Task の Output Data Gear の書き出し
  6. Task Context から
  7. Worker の Context へ入れ替える
  8. Worker は再び Queue から Task を取得する

Synchronized Queue

struct SynchronizedQueue {
    struct Element* top;
    struct Element* last;
    struct Atomic* atomic;
};
// Singly Linked List element
struct Element {
    union Data* top;
    struct Element* next;
};

依存関係の解決

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

並列構文

__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 での呼び出し

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

message

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;
}

OpenMP との比較

message

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
}

Go 言語との比較

message

まとめ

今後の課題