Performance Benchmarks
The Toggly .NET SDK is designed for high performance with minimal overhead. All benchmarks were run using BenchmarkDotNet on .NET 9.0.
Feature Flag Evaluation Performance
Simple Flag Evaluation
The primary metric for most use cases - evaluating a simple feature flag with no targeting rules:
| Metric | Value |
|---|---|
| Mean Time | ~360 nanoseconds (0.36 µs) |
| Memory Allocated | 1.55 KB |
| GC Collections (Gen0) | 0.19 per evaluation |
This means you can perform over 2.7 million feature flag evaluations per second on a single core (more than 2.7 million per second).
Evaluation with Many Flags
Performance remains consistent even with large numbers of flags in cache:
| Flag Count | Mean Time | Memory Allocated |
|---|---|---|
| 100 flags | 366 ns | 1.55 KB |
| 500 flags | 356 ns | 1.55 KB |
| 1,000 flags | 363 ns | 1.55 KB |
The SDK uses efficient dictionary lookups, so cache size has minimal impact on evaluation performance.
Non-Existent Flags
Evaluating flags that don't exist is even faster:
| Metric | Value |
|---|---|
| Mean Time | ~163 nanoseconds (0.16 µs) |
| Memory Allocated | 304 bytes |
Targeting Rules Performance
Percentage Rollout
Evaluating flags with percentage-based rollouts:
| Metric | Value |
|---|---|
| Mean Time | ~785 nanoseconds (0.79 µs) |
| Memory Allocated | 2.88 KB |
| Overhead vs Simple | ~2.2x |
User Targeting
Evaluating flags with user-based targeting rules:
| Metric | Value |
|---|---|
| Mean Time | ~1,660 nanoseconds (1.66 µs) |
| Memory Allocated | 3.16 KB |
| Overhead vs Simple | ~4.6x |
Complex Targeting
Evaluating flags with multiple targeting rules:
| Metric | Value |
|---|---|
| Mean Time | ~1,540 nanoseconds (1.54 µs) |
| Memory Allocated | 4.34 KB |
Memory Allocation
Per-Evaluation Memory
Each feature flag evaluation allocates minimal memory:
| Scenario | Memory Allocated |
|---|---|
| Simple evaluation | 1.55 KB |
| With usage stats | 1.55 KB |
| With context | 1.58 KB |
| Percentage rollout | 2.88 KB |
| User targeting | 3.16 KB |
Bulk Evaluations
Memory scales linearly with the number of evaluations:
| Evaluations | Total Memory | Per Evaluation |
|---|---|---|
| 10 flags | 2.97 KB | ~297 bytes |
| 50 flags | 14.84 KB | ~297 bytes |
| 100 flags | 29.69 KB | ~297 bytes |
Usage Statistics Overhead
The SDK tracks usage statistics with minimal overhead:
| Operation | Mean Time | Memory Allocated |
|---|---|---|
| Record check | ~59 ns | 32 bytes |
| Record usage | ~82 ns | 56 bytes |
| Full evaluation with stats | ~360 ns | 1.55 KB |
Usage statistics tracking adds less than 0.1 microseconds of overhead per evaluation.
Initialization Performance
Service Collection Setup
Time to configure the dependency injection container:
| Metric | Value |
|---|---|
| Mean Time | ~139 microseconds (0.14 ms) |
Provider Initialization
Time to initialize the feature provider and load flags from snapshot:
| Metric | Value |
|---|---|
| Mean Time | ~202 milliseconds |
| Memory Allocated | 374 KB |
This initialization happens once at application startup. Flags are loaded from a snapshot provider (e.g., distributed cache, database) to ensure fast startup times.
Initial Flag Load
Time to load flags from snapshot provider:
| Metric | Value |
|---|---|
| Mean Time | ~31 nanoseconds |
| Memory Allocated | 208 bytes |
This demonstrates the efficiency of snapshot-based loading.
Parallel Evaluation
The SDK handles concurrent evaluations efficiently:
| Evaluations | Sequential | Parallel | Overhead |
|---|---|---|---|
| 1 flag | 167 ns | 202 ns | +21% |
| 5 flags | 828 ns | 880 ns | +6% |
| 10 flags | 1,599 ns | 1,722 ns | +8% |
| 50 flags | 8,571 ns | 8,957 ns | +5% |
Parallel evaluation has minimal overhead, making it safe to evaluate flags concurrently.
Performance Summary
Key Metrics
- Simple evaluation: ~360 ns (0.36 µs)
- Memory per evaluation: ~1.55 KB
- Throughput: More than 2.7 million evaluations/second per core
- Targeting overhead: 2-5x for complex rules
- Usage stats overhead: Less than 0.1 µs
Comparison to Alternatives
The Toggly .NET SDK adds minimal overhead compared to alternatives:
- Simple evaluation is comparable to a dictionary lookup
- Memory allocation is optimized with object pooling where applicable
- No blocking I/O during evaluation (flags are cached in memory)
- Thread-safe concurrent evaluations with minimal contention
Best Practices for Performance
1. Use Snapshot Providers
Snapshot providers (Redis, SQL Server, etc.) enable fast startup by loading flags from a persistent cache rather than fetching from the API:
services.AddTogglyWeb(options => {
options.AppKey = "your-app-key";
options.Environment = "production";
});
// Add snapshot provider for fast startup
services.AddSingleton<IFeatureSnapshotProvider>(
new DistributedCacheFeatureSnapshotProvider(cache));
2. Cache Feature Definitions
Feature definitions are automatically cached in memory and refreshed periodically. No additional caching is needed.
3. Evaluate Flags Efficiently
// Good: Evaluate once and reuse
var isEnabled = await _featureManager.IsEnabledAsync("my-feature");
if (isEnabled) {
// Use feature
}
// Avoid: Multiple evaluations in the same request
if (await _featureManager.IsEnabledAsync("my-feature")) { }
if (await _featureManager.IsEnabledAsync("my-feature")) { } // Don't do this
4. Use IFeatureManagerSnapshot
IFeatureManagerSnapshot ensures consistent flag values throughout a request:
// Recommended for web applications
public class MyController : Controller
{
private readonly IFeatureManagerSnapshot _featureManager;
public MyController(IFeatureManagerSnapshot featureManager)
{
_featureManager = featureManager;
}
}
Benchmark Environment
All benchmarks were run on:
- Platform: macOS (Apple M4 Max)
- .NET Version: 9.0.0
- Runtime: Arm64 RyuJIT AdvSIMD
- Tool: BenchmarkDotNet v0.13.12
Results may vary slightly on different hardware and .NET versions, but the relative performance characteristics remain consistent.
Source Code
The complete benchmark source code is available in the Toggly.FeatureManagement repository:
You can run the benchmarks yourself to verify these results or test on your own hardware.
Next Steps
- Learn about Configuration Options to optimize for your use case
- Explore Snapshot Providers for fast startup
- Review Advanced Usage patterns