I noticed the signature signing and verification code uses futures::future::join_all, which helps to run IO-bound futures concurrently.
Signature signing/verification code is CPU-bound - for example, elliptic curve cryptography for ECDSA-P256 signatures could take 100-300 microseconds, which is an order of magnitude longer than tokio::task::spawn_blocking blocking thread handoff (~<10 microseconds).
Effectively, the current code runs sequentially, but adds join_all scheduling overhead.
My recommendations:
- run CPU-bound code sequentially in a loop on an async worker thread by default (default-features = false), which is a pragmatic choice for single-signature per request use cases.
- add support for a configurable limit of request signatures to control spent CPU resources. Reject requests exceeding the limit before signature verification. Allow at most one signature per request by default.
- The default loop works well if offloaded to a dedicated rayon thread pool by the caller
- For users, not managing custom thread pools introduce a runtime-agnostic trait for offloading CPU-bound work on a blocking thread or provide integrations for popular runtimes like Tokio. The risk is too high default parallelism (512) of the blocking pool, designed for offloading blocking IO.
- allow sequential offload by default, which is good to control per-request resources. Add it as a feature dependency for the
default feature.
- optionally allow parallel offload of multiple request signatures to multiple blocking threads - not sure if anybody wants this setup, it's more suitable for trusted clients
I noticed the signature signing and verification code uses
futures::future::join_all, which helps to run IO-bound futures concurrently.Signature signing/verification code is CPU-bound - for example, elliptic curve cryptography for ECDSA-P256 signatures could take 100-300 microseconds, which is an order of magnitude longer than
tokio::task::spawn_blockingblocking thread handoff (~<10 microseconds).Effectively, the current code runs sequentially, but adds
join_allscheduling overhead.My recommendations:
defaultfeature.