Snow Leopard には Grand Central Dispatch (以下 GCD) という機能がある。これは C と当然 C++, Objective-C, Objective-C++ にクロージャのようなものを追加し、マルチコアな CPU における計算の並列実行を簡単に書けるようにするらしい。

私は並列化には全然詳しくないのだけど、クロージャのようなものは気になるので調べてみた。

Block

Apple Developer Connection にある Introducing Blocks and Grand Central Dispatch は GCD の全体像をコードを交えつつわかりやすく説明している。この “blocks” というのがクロージャのようなものだ。

Blocks are similar to — but far more powerful than — traditional function pointers. The key differences are: Blocks can be defined inline, as “anonymous functions.”

Blocks capture read-only copies of local variables, similar to “closures” in other languages

上記文章中ではこの一カ所でしか “closures” という語は使われていなく、あとは “blocks” と呼ばれている。これを使って 技術野郎の復讐 (書籍『ハッカーと画家』に「オタク野郎の復習」として収録されている) にあるアキュムレータを書いてみた。

#include <stdio.h> #include <Block.h> typedef int (^IntAccumulator)(int); IntAccumulator foo(int n) { __block int s = n; int (^bar)(int) = ^(int i){ return s += i; }; return Block_copy(bar); } int main(int argc, char* argv[]) { IntAccumulator f = foo(1); printf("%dn", f(1)); printf("%dn", f(1)); printf("%dn", f(2)); Block_release(f); return 0; }

このコードのうち、いままでの C になかった部分を簡単に説明する:

block は関数ポインタと環境をあらわすデータで実現され、文法も関数ポインタを模し * を ^ に代えたものになっている。

を に代えたものになっている。 外のローカル変数は参照できるが変更は通常できない。グローバル変数と static 変数、さらに新たに導入された __block 修飾子のつけられたローカル変数は変更できる。

block は効率上の理由でスタックに確保される。もとのスコープから脱したい場合は明示的に Block_copy して複製を作り Block_release で解放する必要がある。

実行結果は当然こうなる。

% cc -Wextra 1.c && ./a.out 2 3 5 %

まだ並列化は関係がなく、個々の関数呼び出しは順に実行されている。

並列化抜きの block はこれから使われていくんだろうか。callback として、スコープのより狭い関数ポインタとしては便利だ。実際、Apple は qsort_b, atexit_b といった block を引数にとる関数をいくつか提供している。一方、関数が関数を返すような使い方は微妙に思う。ただ Block_copy/Block_release の「だささ」は C では日常茶飯事だし、Objective-C 上なら NSAutoreleasePool やガベージコレクタの恩恵にもあずかれるらしい。

ちなみに、Apple は block の標準化を目指しているらしく、ISO の C のワーキンググループには 今年3月 に Apple’s extensions to C として提出されている。実装も、GCC では Apple の独自拡張あつかいだけど、LLVM のフロントエンドである Clang には とりこまれて いる。

並列化

さて、この block を並列に実行するために、GCD は dispatch queue というキューを提供する。キューの先から取り出された block はスレッドプールのなかのスレッド上で実行される。スレッドプールがキューを提供するのは 一般的 なようだ。

While initially inspired by the challenge of multicore computing, these actually solve a more general problem: how to efficiently schedule multiple independent chunks of work. GCD does this using four primary abstractions: block objects

dispatch queues

synchronization

event sources

block と dispatch queue と、その同期のためのいくつかの方法、さらに非同期 IO などに block を callback として使う event とが、GCD の提供する主な抽象とされている。

GCD の特長は、スレッドが完全に隠蔽されることと、block の導入で、分割した処理を記述量もふくめ簡単に書けることのように思う。Apple は OS との密な連携もあげているけど、私は OS に詳しくないのであまりうなづけなかった。

次回予告

息切れしたのでここで一度おわり。次回はコンパイラ側のはなしをしたいです。