33
Immutable List Gem 2014/07/29 KLab 社内勉強会 ALM 細田 (@gam0022)

Immutable List Gem (KLab ALM版)

Embed Size (px)

DESCRIPTION

関数型スタイルでのクイックソートを高速化した話(Ruby)です。KLab ALM 2014/07/29 で発表しました。

Citation preview

Page 1: Immutable List Gem (KLab ALM版)

Immutable List Gem

2014/07/29 KLab 社内勉強会 ALM

細田 翔 (@gam0022)

Page 2: Immutable List Gem (KLab ALM版)

自己紹介

細田 翔

筑波大学情報学群情報科学類 B4

- 非数値処理アルゴリズム研究室

15卒 KLab内定者

Twitter: @gam0022

http://gam0022.net/

2

Page 3: Immutable List Gem (KLab ALM版)

話す内容

関数型スタイルでのクイックソートを高速化した話(Ruby)

3

Page 4: Immutable List Gem (KLab ALM版)

クイックソート

念のためにおさらい

クイックソートの基本的な考え

- データを軸要素(ピボット)より大きい要素・小さい要素で分割することを再帰的に繰り返してソートを行なう

4

http://www.ics.kagoshima-u.ac.jp/~fuchida/edu/algorithm/sort-algorithm/quick-sort.html

Page 5: Immutable List Gem (KLab ALM版)

Rubyでクイックソート

手続き型スタイルで実装した場合

特徴

1. コードが長い(tempなどの変数が必要)

2. 昇順降順の切り替えに2箇所の変更

3. 動作が追いにくい

5

Page 6: Immutable List Gem (KLab ALM版)

OCamlでクイックソート

OCaml (関数型言語)による実装

特徴

1. 5行で書ける!

2. 昇順降順の切り替えは1箇所の変更

3. 動作が追いやすい

6

Page 7: Immutable List Gem (KLab ALM版)

Ruby vs OCaml

Ruby vs OCaml

- OCaml の圧倒的な勝利

Ruby でも OCaml のようなクイックソートを書きたい!

7

Ruby OCaml

行数 29 5

昇順降順の変更箇所 2 1

可読性 △ ○

Page 8: Immutable List Gem (KLab ALM版)

Ruby でクイックソート その2

OCaml Ruby

左: OCamlの5行のコードを List.partition を使わないで再実装

右: 左のコードを Ruby に移植したコード

Ruby でも関数型スタイルにプログラミングできた!8

Page 9: Immutable List Gem (KLab ALM版)

だが待って欲しい!

Page 10: Immutable List Gem (KLab ALM版)

Ruby でクイックソートその2

このコードは確かに動作する

問題点

- Array#+ や Array#drop は非破壊的メソッド

- Array のインスタンスを大量に生成

- Array を何度もコピー

10

Page 11: Immutable List Gem (KLab ALM版)

処理コストが大きい!

Page 12: Immutable List Gem (KLab ALM版)

データ構造を見直す

関数型スタイルのプログラミングには、Array(配列)は適さない

データ構造から見なおしてみる

(Immutable) Singly Linked List が最適

- イミュータブルな単方向連結リスト

- OCaml や Lisp の List にも使われる

12

Page 13: Immutable List Gem (KLab ALM版)

Immutable Singly Linked List を実装

普通に Ruby で実装してもネタとして面白くない

Array は組み込みクラスであり、C言語で実装されているので、普通にRubyで実装してもパフォーマンス的にも勝ち目がない

「C拡張」として、実装しよう!

13

Page 14: Immutable List Gem (KLab ALM版)

Ruby の C拡張

C拡張

- C言語で書いたコードをリンクして Ruby から利用する仕組み

部分的な処理の高速化・ラッパーライブラリなどに使われる

例:C拡張を利用した gem

- スクレイピングライブラリの nokogiri

- SQlite3 のラッパーライブラリの sqlite3

14

Page 15: Immutable List Gem (KLab ALM版)

ImmutableList の実装

クラス名は ImmutableList

struct immutable_list の定義 (Cの構造体をラップ)

15

Page 16: Immutable List Gem (KLab ALM版)

ImmutableList の実装

OCaml を意識したメソッドを実装

- cons

- head, tail

- rev_append, rev, append

- length

- nth

- inspect

16

Page 17: Immutable List Gem (KLab ALM版)

ImmutableList の実装

cons (先頭への要素追加) の定義

17

Page 18: Immutable List Gem (KLab ALM版)

ImmutableList の実装

全部で200行くらいで実装できた

罠が多くて苦労した

- 明示的にオブジェクトをmarkしないと GC で勝手にオブジェクトが開放されて SEGV が発生したりとハマりどころが多い

- Ruby 処理系の勉強にはすごくなった

18

Page 19: Immutable List Gem (KLab ALM版)

Ruby でクイックソート その3

ImmutableList を使用して再実装

19

Page 20: Immutable List Gem (KLab ALM版)

Ruby でクイックソート 考察

全く同じ処理

クラスを置き換えただけ

性能に違いが出るか?

20

Array ImmutableList

Array ImmutableListfirst head

drop(1) tail[i]+a a.cons(i)

Page 21: Immutable List Gem (KLab ALM版)

ベンチマーク

データの長さn と 処理時間(CPU時間) の関係を調査

ソートデータ: 重複のないランダムなデータを使用

最新の Ruby 2.1.2 を使用

21

Page 22: Immutable List Gem (KLab ALM版)

結果

22

リストの長さのクイックソートの実行時間

実行時間[sec]

0

0.1

0.2

0.3

0.4

リストの長さ[個]0 2000 4000 6000 8000

Array ImmutableList

リストの長さに比例して、ImmutableList が高速になる

最大で4.3倍の高速化!

Page 23: Immutable List Gem (KLab ALM版)

まとめ

処理によっては、再帰を用いた関数型スタイルでプログラミングすることで、シンプルに実装できる場合がある

パフォーマンス的には、Ruby の Array は関数型スタイルには適していない

C拡張として実装した ImmutableList によって、関数型スタイルでのクイックソートを高速化することができた!

23

Page 24: Immutable List Gem (KLab ALM版)

RubyGems に公開中

RubyGems は Ruby ライブラリの管理システム

immutable_list の名前で登録済みなので、コマンド一発で導入可能

24

Page 25: Immutable List Gem (KLab ALM版)

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

Page 26: Immutable List Gem (KLab ALM版)

おまけ

本当にC拡張で作った意味があったので疑問だったので、Rubyでも同じ機能を持つクラスを実装した

ソースコードはC実装の1/3くらいになった(Rubyの生産性すごい)

次のベンチマークをとった

- 先ほどのクイックソート

- 単純な連結(append)処理

26

Page 27: Immutable List Gem (KLab ALM版)

ImmutableListのRuby実装と比較

Ruby で実装した ImmutableList(Ruby)

手続き型スタイルのクイックソート Procedural

27

リストの長さとクイックソートの実行時間

実行時間[sec]

0

0.1

0.2

0.3

0.4

リストの長さ[個]0 2000 4000 6000 8000

Array ImmutableListImmutableList(Ruby) Procedural

C拡張版の方が少し高速

Page 28: Immutable List Gem (KLab ALM版)

ImmutableListのRuby実装と比較

連結の回数と実行時間(対数グラフ)

実行時間[sec]

0.01

0.1

1

10

100

連結回数[回]1000 10000 100000

Array ImmutableListImmutableList(Ruby)

長さ6のリストをn回連結

28

Page 29: Immutable List Gem (KLab ALM版)

ImmutableListのRuby実装と比較

C拡張版の方が高速で良かった

でも、思ったよりは変わらなくて残念(́・ω・`)

関数型スタイルは手続き型スタイルよりすごく遅い

29

Page 30: Immutable List Gem (KLab ALM版)

Immutable List が Array より メモリの消費が少ない説明

Page 31: Immutable List Gem (KLab ALM版)

Arrayの連結

C = A+B を行なった時、AとBの内容を全てコピーする

1 2 3 4 5 6 7 8+

A B

1 2 3 4 5 6 7 8A B

1 2 3 4 5 6 7 8C

Page 32: Immutable List Gem (KLab ALM版)

Immutable Singly Linked List の連結

C = A+B を行なった時、Bとの差分だけがコピーされる

1 2 3 4 5 6 7 8+

A B

1 2 3A

1 2 3C

4 5 6 7 8B

Page 33: Immutable List Gem (KLab ALM版)

head と tail

次の操作は参照を返すだけなので、とても高速

- head: 先頭の要素の取り出し Array#first に対応

- tail: 2番目以降のリストの取り出し Array#drop に対応

33

1 2 3 4 5 6 7 8

tail

head