TimeWarp (TimeWarp v0.1.0)

Copy Markdown View Source

Optimistic parallel discrete-event simulation on the BEAM (spec §7).

The engine handles every Time Warp mechanic — speculative execution, rollback, anti-messages, GVT, fossil collection. A domain author implements only TimeWarp.Model.

{:ok, sim} =
  TimeWarp.start_run(
    model: Terminal.Model,
    lps: %{quay_1 => args, yard_a => args},   # lp_id => init_args
    seed: 42,
    until: {:vtime, 86_400_000},
    init_events: [{quay_1, 0, :vessel_arrives}],
    gvt_interval_ms: 50,
    time_window: :none,          # see caveat below (§11.3)
    check_purity: false,         # Mitigation 3b: purity probe
    thrash_guard: nil            # or {rate_per_s, window_ms} (Mitigation 2)
  )

{:done, _} = TimeWarp.await(sim)
report = TimeWarp.report(sim)

Options

  • :model — a TimeWarp.Model implementation (required).
  • :lps%{lp_id => init_args} (required).
  • :seed — integer seed for per-LP PRNGs (default 0).
  • :until{:vtime, t} run horizon, or :infinity (default).
  • :init_events[{target, at, payload}] genesis events kicking off the run.
  • :gvt_interval_ms — wall-clock cadence of GVT cycles (default 50).
  • :check_purity — run the double-call purity probe (Mitigation 3b, default false).
  • :thrash_guard{rollbacks_per_s, window_ms} circuit breaker (Mitigation 2).
  • :time_window{:vtime, W} to bound optimism to [GVT, GVT+W] (§11.3), or :none (default). Deadlock-free (the effective bound is max(GVT+W, global_min_event)). W trades parallelism for conflict, not a free storm fix — see the note below.
  • :cancellation:aggressive (default) | :lazy (§5.5). Lazy suppresses an anti-message when re-execution re-emits an identical event; it only pays on models whose straggler-path events are PRNG-free (see TimeWarp.Model — it is inert and can amplify the storm on random models like PHOLD).

:time_window is a parallelism trade, not a storm fix

A tight W caps how far any LP speculates past GVT, so it bounds rollback depth and the storm for any workload — but it does so by throttling parallelism. A W tight enough to tame a high-conflict storm has throttled execution toward conservative (§11.5): the right answer there is that the workload is a poor fit for optimism, not that a smaller W is a win. Find W empirically per workload; a value tuned on one model says nothing about another.

Summary

Functions

Block until the run terminates. Returns {:done, info} or {:failed, reason}.

Snapshot of the run: GVT, per-LP state/stats, and aggregate totals.

Start a run. Returns {:ok, sim} where sim is the handle for await/report.

Types

config()

@type config() :: keyword()

Functions

await(sim)

@spec await(pid()) :: {:done, map()} | {:failed, term()}

Block until the run terminates. Returns {:done, info} or {:failed, reason}.

report(sim)

@spec report(pid()) :: map()

Snapshot of the run: GVT, per-LP state/stats, and aggregate totals.

start_run(config)

@spec start_run(config()) :: {:ok, pid()}

Start a run. Returns {:ok, sim} where sim is the handle for await/report.