圧倒亭グランパのブログ

30年後の自分にもわかるように書くブログ

Crystalのmacro紹介 "parallel"

この記事は、 Crystal Advent Calendar 2017 の9日目の記事です。

crystalには便利なmacroがあります。macroを使えば、記述量も削減でき表現力も向上します。しかし、ただcrystalを書いているだけでは、なかなかそれらのmacroに出会うことができません。

ということで、アドベント・カレンダーの日数稼ぎも兼ねて、macroの紹介をしたいと思います。

そもそも「macroってなんだ」という場合は、下記の本家docsを参照してください。

Macros · GitBook

 

今回は、 parallel を紹介します。

parallel

crystal-lang.org/api - Top Level Namespace - parallel

引数に与えた処理を並行処理します。それぞれの処理の返り値も受け取ることができます。

# 1秒後に、文字列を出力し文字列を返すlambda
job1 = ->{
  sleep 1
  puts "job1 : sleep 1 / now: #{Time.now.to_s("%F %T")}"
  "return value of job1"
}

# 2秒後に、文字列を出力し文字列を返すlambda
job2 = ->{
  sleep 2
  puts "job2 : sleep 2 / now: #{Time.now.to_s("%F %T")}"
  "return value of job2"
}

# 3秒後に、文字列を出力し文字列を返すlambda
job3 = ->{
  sleep 3
  puts "job3 : sleep 3 / now: #{Time.now.to_s("%F %T")}"
  "return value of job3"
}

# 動かしてみる
puts "program start  / now: #{Time.now.to_s("%F %T")}"
ret1, ret2, ret3 = parallel job1.call, job2.call, job3.call

# それぞれの返り値を表示
puts "return -> \"#{ret1}\" /  now: #{Time.now.to_s("%F %T")}"
puts "return -> \"#{ret2}\" /  now: #{Time.now.to_s("%F %T")}"
puts "return -> \"#{ret3}\" /  now: #{Time.now.to_s("%F %T")}"

実行すると、以下のようになります。

$ crystal run src/parallel.cr
program start  / now: 2017-12-08 22:03:34
job1 : sleep 1 / now: 2017-12-08 22:03:35
job2 : sleep 2 / now: 2017-12-08 22:03:36
job3 : sleep 3 / now: 2017-12-08 22:03:37
return -> "return value of job1" /  now: 2017-12-08 22:03:37
return -> "return value of job2" /  now: 2017-12-08 22:03:37
return -> "return value of job3" /  now: 2017-12-08 22:03:37

job3job1, job2の処理を待たずに実行されているので、それぞれが並行処理されていることがわかります。さらに、job3の処理が終わり、処理を同期させてから返り値を受け取り、その後のputs処理が走っていることがわかります。

簡単に並行処理ができるので、ぜひ使ってみてください。

 

並行と並列

parallelは訳すと「並列」なので、「お、crystalって並列処理をサポートしているのか!」と思いました。しかし、並列処理と並行処理に関して書かれている以下の公式ドキュメントを読んでみると、冒頭に、

Concurrency · GitBook

At the moment of this writing, Crystal has concurrency support but not parallelism: several tasks can be executed, and a bit of time will be spent on each of these, but two code paths are never executed at the same exact time.

と書かれています。parallelの中身の実装を見てみると、難しいことはせずに spawn を使って並行処理をしているようでした。コードをblameで追ってみると、「Concurrent: use Channel(Nil) in parallel」などのコミットメッセージもあり、このマクロに関してはあまり気にしてないのかなーと思います。実際、本家crystalのリポジトリで他にparallelが使われている箇所はありません。

ちなみに、並列処理に関しては別ブランチで進められています。詳しくはwikiを参照してください。

Threads support · crystal-lang/crystal Wiki · GitHub

非常に楽しみですね。