Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

プログラミング言語のパラダイムシフトーScalaから見る関数型と並列性時代の幕開けー

2015 6/19 に社内勉強会で発表した資料です

  • Be the first to comment

プログラミング言語のパラダイムシフトーScalaから見る関数型と並列性時代の幕開けー

  1. 1. プログラミング言語の パラダイムシフト Scalaから見る関数型と並列性時代の幕開け 安田裕介
  2. 2. concurrency なぜ並列性なのか
  3. 3. ムーアの法則 http://spray.io/duse/#/2 トランジスタの数は1.5年ごとに倍になる
  4. 4. ムーアの法則 http://spray.io/duse/#/2 単一スレッドの パフォーマンスは 限界に達した 今後は コアが増える形で トランジスタの集積率を 増やす形になる トランジスタの数は1.5年ごとに倍になる
  5. 5. アムダールの法則 http://spray.io/duse/#/3 並列計算の分野において、複数のプロセッサを使ったときの 理論上の性能向上の限界を予測する法則
  6. 6. アムダールの法則 98.7% 75% 99.2% 80%の性能を出すのに 必要な並列度 96.4% http://spray.io/duse/#/3 並列計算の分野において、複数のプロセッサを使ったときの 理論上の性能向上の限界を予測する法則
  7. 7. アムダールの法則 並列性の低い プログラムは コアが増えても まったく速くならず、 リソースを無駄にする 並列性の高い プログラムは コアが増えるほど 速くなる http://spray.io/duse/#/3 並列計算の分野において、複数のプロセッサを使ったときの 理論上の性能向上の限界を予測する法則
  8. 8. 並列性の低いアプリケーションは CPUを使い切れない PHP + Apache CPU: 32コア Intel(R) Xeon(R) CPU E5-2650 0 @ 2.00GHz
  9. 9. ハードウェアの進化を 待っていれば プログラムが速くなる 時代は終わった
  10. 10. 時代は 並列分散プログラミングへ
  11. 11. 並列分散処理言語 • Scala • Erlang • Haskell • Elixir • Rust • (Go) • (Pony) いずれもが関数型の性質をもつ
  12. 12. 並列分散処理の技術 http://spray.io/duse/#/5
  13. 13. スレッド http://spray.io/duse/#/5
  14. 14. スレッドは恐ろしい http://docs.scala-lang.org/ja/overviews/core/futures.html 問題: na *16と BATMAN の aとAの合計totalAはいくつ?
  15. 15. スレッドは恐ろしい http://docs.scala-lang.org/ja/overviews/core/futures.html 問題: na *16と BATMAN の aとAの合計totalAはいくつ? A: 2 か 16 か 18 読み込みと書き込みに 原子性がないことと、 実行順序が不明なことが原因
  16. 16. スレッドの問題 • 原子性がない • 順番の保証がない • デッドロック、レースコンディション • リソースとして貴重 • 排他制御、待ち合わせなど、コードが複雑になる • デバッグが困難
  17. 17. Future http://spray.io/duse/#/5
  18. 18. Future • 非同期計算の結果のための統一インターフェースと してのコレクション • Scalaにおける並列実行の単位 • map, flatMapなどのモナドコンビネーターメソッ ド搭載
  19. 19. "Future" must {
 import scala.concurrent.ExecutionContext.Implicits.global 
 "map and flatMap" in {
 val futureMessage = Future {
 Thread.sleep(1000); 1
 }.flatMap(value => Future {
 Thread.sleep(1000); value + 1
 }).map(s => s"This is a value of future after $s seconds")
 Await.result(futureMessage, 5 seconds) must be("This is a value of future after 2 seconds")
 } 
 "for comprehension" in {
 val futureMessage = for {
 s1 <- Future {
 Thread.sleep(1000); 1
 }
 s2 <- Future {
 Thread.sleep(1000); s1 + 1
 }
 } yield s"This is a value of future after $s2 seconds"
 Await.result(futureMessage, 5 seconds) must be("This is a value of future after 2 seconds")
 }
 } Future 並列に実行 並列に実行 並列に実行 forコンプリヘンション 並列に実行 並列に実行
  20. 20. ParallelCollection http://spray.io/duse/#/5
  21. 21. • Scalaコレクションの並列版 • Scalaコレクションと同じAPIだが、内部実行は並 列 • parメソッドによりScalaコレクションから並列コ レクションに透過的に変換できる • Scalaコレクションと使いかたが同じなので簡単に 使える ParallelCollection
  22. 22. ParallelCollection "parallel collection" must {
 "behave same as standard one" in {
 val list = (0 to 9999).toList
 list.map(_ + 1).filter(_ % 2 == 0).fold(0)(_ + _) must be
 list.par.map(_ + 1).filter(_ % 2 == 0).fold(0)(_ + _)
 }
 } 通常のコレクション 並列コレクション 使い方まったく同じ
  23. 23. Actor http://spray.io/duse/#/5
  24. 24. Actorとは • 並列実行単位となるオブジェクト Future: 関数 Actor: オブジェクト 並列実行単位
  25. 25. Actorは速い http://spray.io/blog/2013-05-24-benchmarking-spray/ ActorベースのフレームワークSprayはJVM史上最速
  26. 26. Actorは効率的 • 4096スレッドで1Gbのメモリーを消費(linux x64) • 2700000アクターで1Gbのメモリーを消費 Akka in Actionより
  27. 27. Actorは堅牢 • Erlangで作られたAXD301スイッチは 99.9999999% 可用性を実現
  28. 28. ReactiveManifesto http://www.reactivemanifesto.org/ • 即応性 (Responsive) • 耐障害性 (Resilient) • 弾力性 (Elastic) • メッセージ駆動 (Message Driven) リアクティブなシステムを構築せよ
  29. 29. 以降Akka actorを元に説明 Actor Actor Actorの機能概観 1.SEND 2.CREATE 4.BECOME 3.SUPERVISE 振る舞いを変える 複製する 監視する メッセージを 送る
  30. 30. Actor の仕組み mailbox thread pool partial function message event loop thread
  31. 31. Actorはスレッドの問題を解決 する • 原子性:アクター内部の処理は並列ではなく順次実行されるので、 通常のプログラミングと同じ • 実行順序:アクター内部の処理は並列ではなく順次実行されるので、 通常のプログラミングと同じ • カプセル化:他のスレッドからアクター内はアクセスできない • 安全性:アクターはブロックしないので、デッドロックはおきない • リソース効率性:スレッドプールから論理スレッドが確保できたと きだけ、メッセージが処理される
  32. 32. Actorによる並列プログラミング class CountAActor extends Actor {
 var totalA = 0 
 def receive: Receive = {
 case "How many?" => sender ! totalA
 case text: String => totalA += text.toUpperCase().count(_ == 'A')
 }
 }
 
 object CountAActor {
 def props = Props(new CountAActor)
 } class SampleTest extends TestKit(ActorSystem("SampleSystem", ConfigFactory.empty()))
 with WordSpecLike with MustMatchers with ImplicitSender with StopSystemAfterAll {
 
 "CountAActor" must {
 "count A and a" in {
 val countAActor = system.actorOf(CountAActor.props, "countAActor")
 countAActor ! "na" * 16
 countAActor ! "BATMAN!"
 countAActor ! "How many?"
 expectMsg(18)
 }
 }
 } 非同期にメッセージを送る 必ず18になる Actor内部は順次実行されるので、 安全に状態を更新できる (直ちに処理されるのではなくmailboxに貯まる) 内部状態は絶対に外から見えないので、 他のスレッドから守られている
  33. 33. SEND:メッセージの送信 • メッセージの送信は、非同期でブロックしない • メッセージの送信先はアクター参照(ActorRef)で あり、アクターインスタンスではない • アクターインスタンスを直接取得する方法はなく、 外部から完全に守られている • アクター参照は実際の送信先を抽象化する(位置透 過性)
  34. 34. 位置透過性 LocationTransparency • アクターのインスタンスが物理的にどこに存在しよ うが、同じようにメッセージを送れる doc.akka.io • 同じプロセス • 別のプロセス • 別のネットワーク上のサーバー
  35. 35. CREATE:複製 • アクターは簡単に複製できる • アクターは気軽にどんどん作ろう • アクターはスケール簡単にスケールする
  36. 36. Props:アクターの設計図 class CountAActor extends Actor {
 var totalA = 0
 def receive: Receive = {
 case "How many?" => sender ! totalA
 case text: String => totalA += text.toUpperCase().count(_ == 'A')
 }
 }
 
 object CountAActor {
 def props = Props(new CountAActor)
 }
 
 class SampleTest extends TestKit(ActorSystem("SampleSystem", ConfigFactory.empty()))
 with WordSpecLike with MustMatchers with ImplicitSender with StopSystemAfterAll {
 
 "CountAActor" must {
 "count A and a in parallel" in {
 implicit val dispatcher = system.dispatcher
 implicit val timeout = Timeout(5 seconds)
 import akka.pattern.ask
 val countAActor1 = system.actorOf(CountAActor.props, "countAActor1")
 val countAActor2 = system.actorOf(CountAActor.props, “countAActor2") 
 countAActor1 ! "na" * 16
 countAActor2 ! “BATMAN!" 
 val futures = Seq(countAActor1, countAActor2).map(_ ? "How many?").map(_.mapTo[Int])
 val result = Future.sequence(futures).map(_.reduce(_ + _))
 Await.result(result, 5 seconds) must be(18)
 }
 }
 } 再現可能なインスタンスを作るためのProps Propsからアクターを2つ作成 2つのアクターにメッセージを送る 2つのアクターの計算結果を集計する
  37. 37. Router:負荷分散 trait RouterCreator {
 def createRouter = SmallestMailboxPool(100).props(CountAActor.props)
 }
 
 class CountARouter extends Actor with RouterCreator {
 val countARouter = context.actorOf(createRouter)
 def receive: Receive = {
 case hm@"How many?" => {
 val reducer = context.actorOf(Reducer.props(sender(), 100))
 countARouter.tell(Broadcast(hm), reducer)
 }
 case msg => countARouter forward msg
 }
 }
 
 object CountARouter {
 def props = Props(new CountARouter)
 }
 
 class Reducer(sendTo: ActorRef, maxCount: Int) extends Actor {
 var total = 0; var count = 0
 def receive: Receive = {
 case sum: Int => {
 total += sum; count += 1
 if (count == maxCount) {
 sendTo ! total
 self ! PoisonPill
 }
 }
 }
 }
 
 object Reducer {
 def props(sendTo: ActorRef, maxCount: Int) = Props(new Reducer(sendTo, maxCount))
 } 100個のアクターに分散する ルーター ルーターにメッセージを送る 最終的な結果を集計するアクター すべてのルーティーに集計メッセージを送る
  38. 38. Router class SampleTest extends TestKit(ActorSystem("SampleSystem", ConfigFactory.empty()))
 with WordSpecLike with MustMatchers with ImplicitSender with StopSystemAfterAll {
 
 "Router" must {
 "route messages" in {
 val router = system.actorOf(CountARouter.props, "CountARouter")
 Stream.fill(10000)("BATMAN!").foreach(router ! _)
 router ! "How many?"
 expectMsg(10000 * 2)
 }
 }
 } 10000個のメッセージを送る 集計 ルーターは100個のアクターに分散する
  39. 39. RemoteRouter 複数のサーバー上のアクターに分散する http://doc.akka.io/docs/akka/snapshot/dev/multi-jvm-testing.html sbt-multijvm-pluginを使ったmulti-jvmテストのデモ 位置透過性により、 送る先のアクターがどこにあってもよい 同じようにActorRefに対してメッセージを送るだけ
  40. 40. SUPERVISE:監視する • 障害を起こさないように設計するのではなく、障害がおき てもいいように設計する Let it crash モデルを採用 • アクターは親子構造のヒエラルキーを作る • 親が子の監視者となり、子の障害に対処する • 階層をできるだけ深くして、障害の影響範囲を小さくする • 状態がおかしくなってクラッシュしたアクターは再起動さ れる(デフォルトの設定)
  41. 41. Let it crash! アクターの再起動 親アクター 子アクター 1. 障害発生 親アクター 子アクター 2. 子アクターが停止 親アクター 子アクター子アクター 親アクター 子アクター 3. 子アクターを 再生成して 入れ替え 4. 新しい子アクター インスタンスで 処理再開 (mailboxは保持)
  42. 42. class CrashActor extends Actor with ActorLogging {
 def receive: Receive = {
 case "Crash!!!" => throw new Exception("crashed!")
 }
 override def preStart() {
 log.info("preStart")
 }
 override def preRestart(reason: Throwable, message: Option[Any]) = {
 log.info("preRestart")
 super.preRestart(reason, message)
 }
 override def postRestart(reason: Throwable) {
 log.info("postRestart")
 super.postRestart(reason)
 }
 override def postStop() {
 log.info("postStop")
 }
 }
 
 object CrashActor {
 def props = Props(new CrashActor)
 }
 
 class Supervisor extends Actor {
 val crashActor = context.actorOf(CrashActor.props)
 
 def receive: Receive = {
 case msg => crashActor forward msg
 }
 }
 
 object Supervisor {
 def props = Props(new Supervisor)
 }
 
 class SampleTest extends TestKit(ActorSystem("SampleSystem", ConfigFactory.empty()))
 with WordSpecLike with MustMatchers with ImplicitSender with StopSystemAfterAll {
 
 "Supervisor" must {
 "crush CrashActor" in {
 val supervisor = system.actorOf(Supervisor.props, "supervisor")
 supervisor ! "Crash!!!"
 Thread.sleep(1000)
 }
 } クラッシュするメッセージを 受けるアクター CrashActorを子に持つ監視アクター クラッシュさせる アクターの ライフサイクルフックメソッド
  43. 43. 再起動してる
  44. 44. まとめ • 今後CPUの進化はマルチコア化が駆動 • CPUを使い尽くすために、並列分散プログラミングが必 要 • 並列分散プログラミングは難しい • 抽象度・一般性のトレードオフの中で、並列処理のハー ドルを下げる様々な概念がある • Future, Parallel Collection, Actor

    Be the first to comment

    Login to see the comments

  • ssuser0f2c0b

    Jun. 28, 2015
  • tetsuoyutani

    Jun. 28, 2015
  • ShirouFujitaka

    Jun. 28, 2015
  • kojirof

    Jun. 29, 2015
  • michikanda9

    Jul. 4, 2015
  • zhoubo3766

    Jul. 8, 2015
  • TaKUMA7

    Jul. 10, 2015
  • TsuyoshiImoto

    Aug. 2, 2015
  • HiroakiEtoh

    Sep. 9, 2015
  • ohtsuchi

    Oct. 28, 2015
  • panfcbk

    Nov. 16, 2015
  • tkmrsntr

    Dec. 12, 2015
  • TomohikoHimura

    Dec. 24, 2015
  • minorusano77

    Dec. 24, 2015
  • YukiIshikawa1

    Jan. 9, 2016
  • DaisukeNakatani

    Dec. 7, 2016
  • NobushigeMatohara

    Dec. 26, 2016
  • 107steps

    Feb. 25, 2017
  • moterech

    Mar. 9, 2017
  • kentaromaeda581

    Jul. 11, 2017

2015 6/19 に社内勉強会で発表した資料です

Views

Total views

8,536

On Slideshare

0

From embeds

0

Number of embeds

1,161

Actions

Downloads

21

Shares

0

Comments

0

Likes

49

×