Boost.勉強会#4 Boost.Proto

Preview:

DESCRIPTION

http://atnd.org/events/11551

Citation preview

僕はどうして Boost.Proto をぺろぺろしようとしたのか。

Boost.勉強会 #4 2011/02/26

ふじた のりひさ/@fjnli id:fjnl

1

2

とある Twitter の会話履歴 (ログ) ...

最初の発表だれがいいですかhttp://atnd.org/events/11551

3

最初の発表だれがいいですかhttp://atnd.org/events/11551

Protoのどんな所がききたいですか。

3

最初の発表だれがいいですかhttp://atnd.org/events/11551

Protoのどんな所がききたいですか。

Lambda, Spirit, uBLASがあるいま、Protoで 何を作るのか 。

3

僕はどうして Boost.Proto をぺろぺろしようとしたのか。

4

5

Boost.勉強会#4の発表に「なぜ僕はProtoをぺろぺろするのか(仮)」というタイトルがありますが、ぺろぺろする理由とか全く聞きたいと思わないので Protoのことについて話してください。

Agenda

•はじめに

• Boost.Proto のお話

•僕がぺろぺろした理由

•まとめ + 質疑応答

6

Agenda

•はじめに

• Boost.Proto のお話

•僕がぺろぺろした理由

•まとめ + 質疑応答

6

いまここ

Agenda

•はじめに

• Boost.Proto のお話

•僕がぺろぺろした理由

•まとめ + 質疑応答

前半15分ぐらい

後半5分ぐらい

6

いまここ

Agenda

• Boost.Proto のお話

• Boost.Proto は縁の下の力持ち

• Expression Template (ET) の型

• Boost.Proto の拡張方法

7

Agenda

•僕がぺろぺろした理由

•数値計算に Boost.Proto を使う

• Boost.uBLAS では満足できない理由

•General Purpose computing on GPU (GPGPU)について少し

• ET → 関数呼び出しの列への変換

8

自己紹介

•ふじた のりひさ (藤田 典久)

• @fjnli, id:fjnl•東京都の端の大学 → 山奥の大学院 (見込)

•Ariel Networks バイト

•研究テーマ: GPU を用いた数値計算

9

自己紹介

•ふじた のりひさ (藤田 典久)

• @fjnli, id:fjnl•東京都の端の大学 → 山奥の大学院 (見込)

•Ariel Networks バイト

•研究テーマ: GPU を用いた数値計算

9

Hatena IDに i はいりません

自己紹介

•ふじた のりひさ (藤田 典久)

• @fjnli, id:fjnl•東京都の端の大学 → 山奥の大学院 (見込)

•Ariel Networks バイト

•研究テーマ: GPU を用いた数値計算

9

Hatena IDに i はいりません※ 本人も間違えました

自己紹介

•ふじた のりひさ (藤田 典久)

• @fjnli, id:fjnl•東京都の端の大学 → 山奥の大学院 (見込)

•Ariel Networks バイト

•研究テーマ: GPU を用いた数値計算

10

前半.begin();

11

Proto 概要

• Expression Template (ET) の構築を補助するライブラリ

• a + b * c → plus<A, multiply<B, C>>

• Boost 1.37 で導入された

•Headers only

12

Proto は縁の下の力持ち

• Boost.Proto は単体で利用して嬉しいライブラリではない

• Boost 中の Boost.Proto ユーザ:

• Boost.Spirit (Qi, Karma, Lex)

• Boost.Phoenix v3 (in the future)

• Boost.MSM.eUML (experimental, 1.44)

• Boost.Xpressive

13

ETの型

•割と重要

•エラーメッセージで頻出

•一般的におそろしく長い (´・ω・`)

•Qi で Proto 関係の部分でエラーを出すと遭遇する

14

15

namespace proto = boost::proto;using namespace boost::proto;using boost::proto::argsns_::list2,using boost::proto::exprns_::expr;using boost::proto::exprns_::term;

紙面の都合で、以降、特に断りがない場合、以下に示す using 宣言を暗黙に行なっています。

a[b] + (c * d)

16

構文木 (Syntax Tree) で表すと:

a[b] + (c * d)

16

(+)

(*)

c d

([])

a b

構文木 (Syntax Tree) で表すと:

a[b] + (c * d)

17

expr<tag::plus, list2< expr<tag::subscript, list2<, expr<tag::terminal, term<int>, 0>&, expr<tag::terminal, term<int>, 0>& >, 2 > const&, expr<tag::multiplies, list2< expr<tag::terminal, term<int> 0>&, expr<tag::terminal, term<int> 0>& >, 2 > const& >, 2> const

a[b] + (c * d)

18

expr<tag::plus, list2< expr<tag::subscript, list2<, expr<tag::terminal, term<int>, 0>&, expr<tag::terminal, term<int>, 0>& >, 2 > const&, expr<tag::multiplies, list2< expr<tag::terminal, term<int> 0>&, expr<tag::terminal, term<int> 0>& >, 2 > const& >, 2> const

式を表す型 exprns_::expr

a[b] + (c * d)

19

expr<tag::plus, list2< expr<tag::subscript, list2<, expr<tag::terminal, term<int>, 0>&, expr<tag::terminal, term<int>, 0>& >, 2 > const&, expr<tag::multiplies, list2< expr<tag::terminal, term<int> 0>&, expr<tag::terminal, term<int> 0>& >, 2 > const& >, 2> const

式の種類を表すタグplus → +

multiplies → *subscript → []

など…

a[b] + (c * d)

20

expr<tag::plus, list2< expr<tag::subscript, list2<, expr<tag::terminal, term<int>, 0>&, expr<tag::terminal, term<int>, 0>& >, 2 > const&, expr<tag::multiplies, list2< expr<tag::terminal, term<int> 0>&, expr<tag::terminal, term<int> 0>& >, 2 > const& >, 2> const

子ノードを表す argsns_::listN

a[b] + (c * d)

21

expr<tag::plus, list2< expr<tag::subscript, list2<, expr<tag::terminal, term<int>, 0>&, expr<tag::terminal, term<int>, 0>& >, 2 > const&, expr<tag::multiplies, list2< expr<tag::terminal, term<int> 0>&, expr<tag::terminal, term<int> 0>& >, 2 > const& >, 2> const

終端記号を表す型

exprns_::term

a[b] + (c * d)

22

expr<tag::plus, list2< expr<tag::subscript, list2<, expr<tag::terminal, term<int>, 0>&, expr<tag::terminal, term<int>, 0>& >, 2 > const&, expr<tag::multiplies, list2< expr<tag::terminal, term<int> 0>&, expr<tag::terminal, term<int> 0>& >, 2 > const& >, 2> const

とりあえず使ってみる

23

default_context const ctx;std::cout << eval(lit(1) + 2 + 3, ctx) << std::endl;

コード:

実行結果:

6

24

とりあえず使ってみる

default_context const ctx;std::cout << eval(lit(1) + 2 + 3, ctx) << std::endl;

コード:

この式を、このコンテキストで評価する。

とりあえず使ってみる

25

式をどのように評価するかは、コンテキストが知っている。

default_context → C++の標準セマンティックnull_context → 何もしない

+ を - とするコンテキスト

•x + y という式を x - y というセマンティックで評価するコンテキストを作る

•話を簡単にするために、int型以外の終端記号と + 以外の演算子の挙動は定義しない

•→ コンパイルエラー

26

+ を - とするコンテキスト

27

struct my_context { template < class Expr, class Enable = void > struct eval;};

eval時にmy_context::eval<Expr>()(expr, ctx)の形で呼ばれる

+ を - とするコンテキスト

27

struct my_context { template < class Expr, class Enable = void > struct eval;};

eval時にmy_context::eval<Expr>()(expr, ctx)の形で呼ばれる

SFINAE用。なくても構わない。

SFINAE

• SFINAE (すふぃねー?)

• Substitution Failure Is Not An Error

•置き換え失敗はエラーに非ず

• boost::enable_ifなど

•template <class T>typename enable_if<is_const<T>>::type foo();

• SFINAEを用いて eval を分岐する。

28

+ を - とするコンテキスト

29

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, terminal<int>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const&) const { return value(e); }}

+ を - とするコンテキスト

30

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, terminal<int>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const&) const { return value(e); }}

proto式パターンマッチ:

Expr が terminal<int> にマッチするならば、メタ true

+ を - とするコンテキスト

31

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, terminal<int>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const&) const { return value(e); }}

評価した結果の型。C++0xならdecltypeで書ける。

+ を - とするコンテキスト

32

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, terminal<int>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const&) const { return value(e); }}

終端記号の値を取る

+ を - とするコンテキスト

33

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, terminal<int>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const&) const { return value(e); }}

+ を - とするコンテキスト

34

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, plus<_, _>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const& ctx) const { return proto::eval(left(e), ctx) - proto::eval(right(e), ctx); }}

+ を - とするコンテキスト

35

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, plus<_, _>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const& ctx) const { return proto::eval(left(e), ctx) - proto::eval(right(e), ctx); }}

_ は何にでもマッチする(wildcard)

+ を - とするコンテキスト

36

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, plus<_, _>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const& ctx) const { return proto::eval(left(e), ctx) - proto::eval(right(e), ctx); }}

left: 1番目の子ノードを得るright: 2番目の子ノードを得る

+ を - とするコンテキスト

37

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, plus<_, _>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const& ctx) const { return proto::eval(left(e), ctx) - proto::eval(right(e), ctx); }}

足すのではなく、引く

+ を - とするコンテキスト

38

template <class Expr>struct eval< Expr, typename boost::enable_if< matches<Expr, plus<_, _>> >::type> { typedef int result_type;

result_type operator ()(Expr& e, my_context const& ctx) const { return proto::eval(left(e), ctx) - proto::eval(right(e), ctx); }}

とりあえず使ってみる その2

39

my_context const ctx;std::cout << eval(lit(1) + 2 + 3, ctx) << std::endl;

コード:

実行結果:

-4

実行時/コンパイル時

•実行時関数:

• proto::left, proto::right

•コンパイル時の返り値型:

• proto::result_of::left, proto::result_of::right

•C++0xでは若干いらない子

40

その他の拡張•Context

•評価に関する情報の提供

•Domain

•式の生成手段の提供

• Extends

•式を表す型を自分で提供する

•Grammer

•文法を定義する。演算子オーバーロードの制御41

その他の拡張

42

入りきらないので参考文献:

1.User’s Guide

http://www.boost.org/doc/libs/1_46_0/doc/html/proto/users_guide.html

2. 自ブログ (C++ Advent Calender JP 2010; 16日目)

http://d.hatena.ne.jp/fjnl/20101222/

前半.end();

後半.begin();

43

Proto を数値計算に使う

• ET と数値計算は相性がいい

•「アルゴリズム」と「計算実装」を分離できる

•組み合わせの爆発を抑制

•見た目が良い (+ が加算を表す)

• (僕のやっている分野では)

単純な計算の組み合わせで表現できる

• Basic Linear Algebra Subprograms (BLAS)

44

Boost.uBLAS ではだめなのか

• Boost.uBLAS は可読性を重視している

• Boost.uBLAS は遅い

• Boost.uBLAS は拡張できない

45

Boost.uBLAS ではだめなのか

•アルゴリズムは CPU, GPU 共通

•コードを共有したい→バグが減る

•計算実装は CPU と GPU で異なり、内部実装が多種多様

•例:

•密行列の実装、疎行列の実装

• CPU: オリジナル, ATLAS, GotoBLAS, OpenCL

• GPU: オリジナル, CUBLAS, OpenCL

46

ET → 関数呼び出し列

47

Backend::scalar a;Backend::vector b, x, y;y = a * x + b;

Backend::eval< multiply, scalar, vector>()(y, a, x);Backend::eval< plus, vector, vector>()(y, y, b);

最適化

48

[a * x + y]plus<multiply<scalar, vector>, vector>

axpy<scalar, vector, vector>

積和演算の最適化をコンパイル時に行う。積和演算を一気に、かつ高速に行なえる

アーキテクチャが存在する。(Cell, GPU など)

49

ご清聴ありがとうございました。

そして

質疑応答