Async

Std.Internal.IO.Async.Async は、(まだ実験的な機能で正式リリースされていない機能ですが)非同期計算をサポートします。ここで非同期計算とは、複数の計算を「他の計算の結果を待たずに」実行するようなものを指します。

import Std.Internal.Async.Basic
import Lean

open Std.Internal.IO.Async

/-- 疑似的に重い処理を非同期で行う関数 -/
def expensiveOperationAsync (n : Nat) : Async Nat := do
  IO.sleep 100  -- 100 ミリ秒スリープ
  return n

/-- 疑似的に重い処理を同期で行う関数 -/
def expensiveOperationSync (n : Nat) : IO Nat := do
  IO.sleep 100  -- 100 ミリ秒スリープ
  return n

/-- `n` 回、非同期に重い処理を並列実行し、その結果の合計を返す -/
def manyInParallel (n : Nat) : Async Nat := do
  -- `expensiveOperationAsync` を n 個生成
  let tasks := (Array.range n).map expensiveOperationAsync
  -- すべての非同期タスクを並列に実行し、結果を集める
  let results ← concurrentlyAll tasks
  return results.sum

/-- `n` 回、同期に重い処理を順次実行し、その結果の合計を返す -/
def manyInSync (n : Nat) : IO Nat := do
  -- `expensiveOperationSync` を n 個生成
  let tasks := (Array.range n).map expensiveOperationSync
  -- 順次に全て実行して結果を集める
  let results ← tasks.mapM id
  return results.sum


open Lean Elab Command in

/-- 2つのコマンドのうち最初のコマンドのほうが `n` 倍早く終わることを確かめるコマンド -/
elab "#speed_rank " "(" "ratio" ":=" n:num ")" "|" stx1:command "|" stx2:command : command => do
  let start_time ← IO.monoMsNow
  elabCommand stx1
  let end_time ← IO.monoMsNow
  let time1 := end_time - start_time

  let start_time ← IO.monoMsNow
  elabCommand stx2
  let end_time ← IO.monoMsNow
  let time2 := end_time - start_time

  logInfo m!"1つめのコマンドの実行時間: {time1}ms"
  logInfo m!"2つめのコマンドの実行時間: {time2}ms"

  let threshold := n.getNat
  unless time1 * threshold < time2 do
    throwError m!"エラー: 1つめのコマンドが期待されるより速くありません。"

-- 非同期版の方が速い
#speed_rank (ratio := 1)
  | #eval (manyInParallel 3).wait
  | #eval manyInSync 3