This guide helps diagnose performance issues in dsrp, particularly non-deterministic slowdowns with Argon2id.
If you're experiencing slow or inconsistent performance (operations taking >10 seconds instead of 2-6 seconds), run the benchmark:
dart benchmark/argon2_benchmark.dartThis will test different Argon2id configurations and identify whether the issue is related to parallelism, system load, or memory pressure.
Symptoms:
- Usually:
User.createSaltedVerificationKeyFromBytes()takes 2-6 seconds - Sometimes: Same operation takes 60+ seconds with high CPU usage
- Inconsistent behavior across test runs
Root Causes:
- Isolate spawning overhead: Argon2id uses Dart isolates for parallelism (default: 4). Spawning isolates has variable overhead
- Thread contention: Multiple concurrent operations × parallelism = many isolates competing for CPU
- Memory pressure: 64 MB per operation × parallelism can trigger GC pauses
- System load: Performance depends on other processes running
Solutions:
- For Tests - Use parallelism=1 for deterministic performance:
final kdf = Argon2idKdf(
name: 'argon2id-test',
argon2: Argon2id(
parallelism: 1, // Single-threaded, no isolates
memory: 65536, // 64 MB
iterations: 3,
hashLength: 32,
),
);
final saltedKey = await User.createSaltedVerificationKeyFromBytes(
userIdBytes: userIdBytes,
passwordBytes: passwordBytes,
customKdf: kdf,
);- For Production - Tune based on your environment:
// Low-resource server (1-2 cores)
parallelism: 1, memory: 32768 // 32 MB
// Standard server (4-8 cores, moderate load)
parallelism: 2, memory: 65536 // 64 MB
// High-performance server (8+ cores, dedicated)
parallelism: 4, memory: 65536 // 64 MB (current default)# For examples
dart --observe --profiler example/srp.dart
# For tests
dart --observe --profiler test
# You'll see output like:
# Observatory listening on http://127.0.0.1:8181/...# In another terminal
dart devtoolsThen open the URL shown (usually http://127.0.0.1:9100) and paste the Observatory URL.
- Go to CPU Profiler tab
- Click Record before the slow operation starts
- Let the operation complete
- Click Stop
- Examine the flame graph:
- Look for
Argon2id.deriveKeyand its duration - Check for isolate spawning overhead
- Identify unexpected bottlenecks
- Look for
Key metrics:
- Total time: How long did the operation take?
- Self time: Time spent in the function itself (not children)
- Isolate activity: Are isolates spawning/dying repeatedly?
- Go to Timeline tab
- Click Record and perform the operation
- Click Stop
- Look for:
- Long GC pauses (indicates memory pressure)
- Isolate spawning events (shows overhead)
- CPU utilization gaps (thread scheduling issues)
- Go to Memory tab
- Take a snapshot before and after the operation
- Look for:
- Large allocations (64 MB+ for Argon2id memory parameter)
- Objects not being garbage collected
- Memory leaks in test suites
# Run with Observatory
dart --observe --profiler test/user_test.dart
# In DevTools, filter CPU profiler to:
# - Function: "createSaltedVerificationKeyFromBytes"
# - Call tree view to see what's slow# Run with CPU profiling
dart --observe --profiler example/srp.dart
# Or get a simple timeline without DevTools:
dart --timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM \
--timeline_recorder=file:timeline.json \
example/srp.dart
# Open timeline.json in chrome://tracing# Simple timing without profiler
time dart example/srp.dart
# Run multiple times to check consistency
for i in {1..5}; do time dart example/srp.dart; doneRun 1: 2341ms
Run 2: 2298ms
Run 3: 2415ms
Variance: ~5%
Run 1: 2341ms
Run 2: 45231ms ← Isolate spawning issue or system load
Run 3: 2298ms
Variance: >90%
Timeline shows:
- Frequent GC pauses during Argon2id
- GC taking >100ms
- Memory usage spikes to >256 MB
Solution: Reduce memory parameter or parallelism
- Tests: Use
parallelism: 1to avoid flaky tests - CI/CD: Consider reducing Argon2id parameters for faster builds
- Benchmarks: Always run on idle system for consistent results
- Development: Use SHA-256 KDF (fast) during development, Argon2id in production
- Testing: Override default KDF in test environments
- Production: Tune Argon2id based on server specs and expected load
Create a test helper to use fast KDF:
// test/test_helpers.dart
import 'package:dsrp/dsrp.dart';
import 'package:dsrp/crypto/kdf.dart';
final testKdf = getKdf(KdfChoice.sha256); // Fast for testing
// Use in tests
final saltedKey = await User.createSaltedVerificationKeyFromBytes(
userIdBytes: userIdBytes,
passwordBytes: passwordBytes,
customKdf: testKdf, // Fast, deterministic
);When reporting performance issues, please include:
- Benchmark results: Output from
dart benchmark/argon2_benchmark.dart - System info: CPU cores, RAM, OS
- Dart version:
dart --version - Reproduction: Minimal code example
- Profiler data: DevTools screenshot or timeline.json (if available)
- Consistency: How often does it occur? (always, 1/10 times, etc.)