|
| 1 | +# SafeNIF Benchmark: NIF vs SafeNIF vs CLI Port |
| 2 | +# |
| 3 | +# Compares three approaches to running native code: |
| 4 | +# 1. Direct NIF call - fastest, but crashes take down the node |
| 5 | +# 2. SafeNIF wrapped - isolated via peer nodes (new BEAM per call) |
| 6 | +# 3. CLI Port - isolated via OS process (new process per call) |
| 7 | +# |
| 8 | +# Run with: |
| 9 | +# elixir --sname bench -S mix run bench/nif_vs_port.exs |
| 10 | +# Generated by Claude and reviewed by @probably-not |
| 11 | + |
| 12 | +alias SafeNIF.Bench.Helpers |
| 13 | +alias SafeNIF.Bench.PortWrapper |
| 14 | +alias SafeNIFTest.TestNIF |
| 15 | + |
| 16 | +IO.puts(""" |
| 17 | +================================================================================ |
| 18 | +SafeNIF Benchmark: NIF vs SafeNIF vs CLI Port |
| 19 | +================================================================================ |
| 20 | +
|
| 21 | +This benchmark compares execution overhead for three isolation strategies: |
| 22 | +
|
| 23 | + • Direct NIF: No isolation - crashes kill the node |
| 24 | + • CLI Port: OS process isolation - spawns new process per call |
| 25 | + • SafeNIF: Peer node isolation - spawns new BEAM node per call |
| 26 | +
|
| 27 | +Both CLI Port and SafeNIF provide true isolation (fresh process per call). |
| 28 | +
|
| 29 | +""") |
| 30 | + |
| 31 | +# Setup |
| 32 | +case Helpers.setup() do |
| 33 | + {:ok, _ctx} -> |
| 34 | + IO.puts("✓ Setup complete\n") |
| 35 | + |
| 36 | + # Verify everything works before benchmarking |
| 37 | + IO.puts("Verifying implementations...") |
| 38 | + :ok = TestNIF.safe_noop() |
| 39 | + :ok = PortWrapper.noop() |
| 40 | + {:ok, :ok} = SafeNIF.wrap({TestNIF, :safe_noop, []}) |
| 41 | + |
| 42 | + 42 = TestNIF.safe_add(17, 25) |
| 43 | + 42 = PortWrapper.add(17, 25) |
| 44 | + {:ok, 42} = SafeNIF.wrap({TestNIF, :safe_add, [17, 25]}) |
| 45 | + |
| 46 | + IO.puts("✓ All implementations working\n") |
| 47 | + |
| 48 | + # Run benchmarks |
| 49 | + IO.puts("Running benchmarks...\n") |
| 50 | + |
| 51 | + Benchee.run( |
| 52 | + %{ |
| 53 | + "Direct NIF (unsafe)" => fn -> TestNIF.safe_noop() end, |
| 54 | + "CLI Port" => fn -> PortWrapper.noop() end, |
| 55 | + "SafeNIF" => fn -> SafeNIF.wrap({TestNIF, :safe_noop, []}) end |
| 56 | + }, |
| 57 | + Helpers.benchee_config(markdown_file: "bench/results/noop_comparison.md") |
| 58 | + ) |
| 59 | + |
| 60 | + IO.puts("\n" <> String.duplicate("=", 80) <> "\n") |
| 61 | + |
| 62 | + Benchee.run( |
| 63 | + %{ |
| 64 | + "Direct NIF (unsafe)" => fn -> TestNIF.safe_add(17, 25) end, |
| 65 | + "CLI Port" => fn -> PortWrapper.add(17, 25) end, |
| 66 | + "SafeNIF" => fn -> SafeNIF.wrap({TestNIF, :safe_add, [17, 25]}) end |
| 67 | + }, |
| 68 | + Helpers.benchee_config(markdown_file: "bench/results/add_comparison.md") |
| 69 | + ) |
| 70 | + |
| 71 | + IO.puts(""" |
| 72 | +
|
| 73 | + ================================================================================ |
| 74 | + Benchmark complete! |
| 75 | +
|
| 76 | + Results saved to: |
| 77 | + • bench/results/noop_comparison.md |
| 78 | + • bench/results/add_comparison.md |
| 79 | +
|
| 80 | + Key insights: |
| 81 | + • Direct NIF shows the baseline (no isolation overhead) |
| 82 | + • CLI Port shows OS process spawn overhead (~1-10ms typical) |
| 83 | + • SafeNIF shows BEAM node spawn overhead (~500-2000ms typical) |
| 84 | +
|
| 85 | + Note: SafeNIF is designed for SAFETY, not speed. Use it when you need |
| 86 | + BEAM-level isolation (crash protection, code loading, distribution). |
| 87 | + ================================================================================ |
| 88 | + """) |
| 89 | + |
| 90 | + {:error, :not_distributed} -> |
| 91 | + IO.puts(""" |
| 92 | + ❌ Node is not running in distributed mode. |
| 93 | +
|
| 94 | + Run benchmarks with: |
| 95 | + elixir --sname bench -S mix run bench/nif_vs_port.exs |
| 96 | + """) |
| 97 | + |
| 98 | + System.halt(1) |
| 99 | + |
| 100 | + {:error, reason} -> |
| 101 | + IO.puts("❌ Setup failed: #{inspect(reason)}") |
| 102 | + IO.puts("\nMake sure to run 'mix compile' first to build the NIF and Port.") |
| 103 | + System.halt(1) |
| 104 | +end |
0 commit comments