Passing a prover to prove() is around fifty times slower than leaving the argument off, for the same transaction.
I hit this while timing proving for an app, and the gap was large enough that I assumed my measurement was wrong. It reproduces on the same request object, in the same page, back to back.
Repro
const script = await client.compile.txScript({
code: "use miden::core::sys\nbegin\n push.1 drop\n exec.sys::truncate_stack\nend\n",
});
const request = new TransactionRequestBuilder().withCustomScript(script).build();
// A — no prover argument
let staged = await client.transactions.executeRequest(account, request);
await staged.prove();
// B — explicit local prover
const prover = TransactionProver.newLocalProver();
staged = await client.transactions.executeRequest(account, request);
await staged.prove({ prover });
Timings on macOS, 8 cores, Chrome 152, @miden-sdk/miden-sdk@0.15.9 single threaded, testnet:
A prove() 1925 ms 1267 ms
B prove({ prover }) 209043 ms 62244 ms
A second run on the same setup gave 2940 / 2726 ms for A and 53242 / 49823 ms for B. The later B call is faster than the first in both runs, so this is not degradation over time. The ratio lands between 18x and 49x depending on the run.
Both paths are local. I checked that by pulling the network down after the client was ready and the transaction executed, then calling prove() with no argument: it completed offline in 1742 ms. So the fast path is not delegating to a remote prover.
Where the paths diverge
proveResult in the SDK sends the two cases to different calls, and defaultProver is null unless the app sets one:
const prover = opts?.prover ?? defaultProver;
return prover
? await inner.proveTransaction(result, prover)
: await inner.proveTransaction(result);
The two branches resolve different provers: custom_prover.get_prover() versus client.prover(). newLocalProver() builds LocalTransactionProver::new(ProvingOptions::default()). I could not work out from here what client.prover() resolves to, so I am reporting the measurement rather than guessing at the cause.
This may be the same routing asymmetry described in #312, though that one is about batch() and the main thread, and here both calls go through the worker wrapper.
Why it matters
newLocalProver() reads as the ordinary way to prove locally, and an app that wants local proving will reach for it. Nothing in the README or the API surface suggests the argument changes performance by this much. A note in the docs would help even if the behaviour is intended.
Environment: macOS, Chrome 152, node 24, pnpm 10.18, SDK 0.15.9, testnet.
Passing a prover to
prove()is around fifty times slower than leaving the argument off, for the same transaction.I hit this while timing proving for an app, and the gap was large enough that I assumed my measurement was wrong. It reproduces on the same request object, in the same page, back to back.
Repro
Timings on macOS, 8 cores, Chrome 152,
@miden-sdk/miden-sdk@0.15.9single threaded, testnet:A second run on the same setup gave 2940 / 2726 ms for A and 53242 / 49823 ms for B. The later B call is faster than the first in both runs, so this is not degradation over time. The ratio lands between 18x and 49x depending on the run.
Both paths are local. I checked that by pulling the network down after the client was ready and the transaction executed, then calling
prove()with no argument: it completed offline in 1742 ms. So the fast path is not delegating to a remote prover.Where the paths diverge
proveResultin the SDK sends the two cases to different calls, anddefaultProveris null unless the app sets one:The two branches resolve different provers:
custom_prover.get_prover()versusclient.prover().newLocalProver()buildsLocalTransactionProver::new(ProvingOptions::default()). I could not work out from here whatclient.prover()resolves to, so I am reporting the measurement rather than guessing at the cause.This may be the same routing asymmetry described in #312, though that one is about
batch()and the main thread, and here both calls go through the worker wrapper.Why it matters
newLocalProver()reads as the ordinary way to prove locally, and an app that wants local proving will reach for it. Nothing in the README or the API surface suggests the argument changes performance by this much. A note in the docs would help even if the behaviour is intended.Environment: macOS, Chrome 152, node 24, pnpm 10.18, SDK 0.15.9, testnet.