> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/App-vNext/Polly/llms.txt
> Use this file to discover all available pages before exploring further.

# Performance

> Optimize Polly resilience pipelines for maximum performance and minimal allocations

Polly is fast and avoids allocations wherever possible. We use a comprehensive set of [performance benchmarks](https://github.com/App-vNext/Polly/tree/main/bench/Polly.Core.Benchmarks) to monitor Polly's performance.

Here's an example of results from an advanced pipeline composed of the following strategies:

* Timeout (outer)
* Rate limiter
* Retry
* Circuit breaker
* Timeout (inner)

## Benchmark Results

| Method              |     Mean |     Error |    StdDev | Ratio | RatioSD |   Gen0 | Allocated | Alloc Ratio |
| ------------------- | -------: | --------: | --------: | ----: | ------: | -----: | --------: | ----------: |
| Execute policy v7   | 2.277 μs | 0.0133 μs | 0.0191 μs |  1.00 |    0.00 | 0.1106 |    2824 B |        1.00 |
| Execute pipeline v8 | 2.089 μs | 0.0105 μs | 0.0157 μs |  0.92 |    0.01 |      - |      40 B |        0.01 |

Compared to older versions, Polly v8 is both faster and more memory efficient.

## Performance Tips

If you're aiming for the best performance with Polly, consider these tips:

### Use Static Lambdas

Lambdas capturing variables from their outer scope will allocate on every execution. Polly provides tools to avoid this overhead, as shown in the example below:

```csharp theme={null}
// This call allocates for each invocation since the "userId" variable is captured from the outer scope.
await resiliencePipeline.ExecuteAsync(
    cancellationToken => GetMemberAsync(userId, cancellationToken),
    cancellationToken);

// This approach uses a static lambda, avoiding allocations.
// The "userId" is passed to the execution via the state argument, and the lambda consumes it as the first
// parameter passed to the GetMemberAsync() method. In this case, userIdAsState and userId are the same value.
await resiliencePipeline.ExecuteAsync(
    static (userIdAsState, cancellationToken) => GetMemberAsync(userIdAsState, cancellationToken),
    userId,
    cancellationToken);
```

<Tip>
  Using static lambdas eliminates closure allocations and can significantly improve performance in hot paths.
</Tip>

### Use Switch Expressions for Predicates

The `PredicateBuilder` maintains a list of all registered predicates. To determine whether the results should be processed, it iterates through this list. Using switch expressions can help you bypass this overhead.

<CodeGroup>
  ```csharp PredicateBuilder (Slower) theme={null}
  // Here, PredicateBuilder is used to configure which exceptions the retry strategy should handle.
  new ResiliencePipelineBuilder()
      .AddRetry(new()
      {
          ShouldHandle = new PredicateBuilder()
              .Handle<SomeExceptionType>()
              .Handle<InvalidOperationException>()
              .Handle<HttpRequestException>()
      })
      .Build();
  ```

  ```csharp Switch Expression (Faster) theme={null}
  // For optimal performance, it's recommended to use switch expressions instead of PredicateBuilder.
  new ResiliencePipelineBuilder()
      .AddRetry(new()
      {
          ShouldHandle = args => args.Outcome.Exception switch
          {
              SomeExceptionType => PredicateResult.True(),
              InvalidOperationException => PredicateResult.True(),
              HttpRequestException => PredicateResult.True(),
              _ => PredicateResult.False()
          }
      })
      .Build();
  ```
</CodeGroup>

### Execute Callbacks Without Throwing Exceptions

Polly provides the `ExecuteOutcomeAsync` API, returning results as `Outcome<T>`. The `Outcome<T>` might contain an exception instance, which you can check without it being thrown. This is beneficial when employing exception-heavy resilience strategies, like circuit breakers.

```csharp theme={null}
// Execute GetMemberAsync and handle exceptions externally.
try
{
    await pipeline.ExecuteAsync(cancellationToken => GetMemberAsync(id, cancellationToken), cancellationToken);
}
catch (Exception e)
{
    // Log the exception here.
    logger.LogWarning(e, "Failed to get member with id '{id}'.", id);
}

// The example above can be restructured as:

// Acquire a context from the pool
ResilienceContext context = ResilienceContextPool.Shared.Get(cancellationToken);

// Instead of wrapping pipeline execution with try-catch, use ExecuteOutcomeAsync(...).
// Certain strategies are optimized for this method, returning an exception instance without actually throwing it.
Outcome<Member> outcome = await pipeline.ExecuteOutcomeAsync(
    static async (context, state) =>
    {
        // The callback for ExecuteOutcomeAsync must return an Outcome<T> instance. Hence, some wrapping is needed.
        try
        {
            return Outcome.FromResult(await GetMemberAsync(state, context.CancellationToken));
        }
        catch (Exception e)
        {
            return Outcome.FromException<Member>(e);
        }
    },
    context,
    id);

// Handle exceptions using the Outcome<T> instance instead of try-catch.
if (outcome.Exception is not null)
{
    logger.LogWarning(outcome.Exception, "Failed to get member with id '{id}'.", id);
}

// Release the context back to the pool
ResilienceContextPool.Shared.Return(context);
```

<Note>
  Using `ExecuteOutcomeAsync` avoids the overhead of exception throwing and catching, which can be significant in high-throughput scenarios.
</Note>

### Reuse Resilience Pipeline Instances

Creating a resilience pipeline can be resource-intensive, so it's advisable not to discard the instance after each use. Instead, you can either cache the resilience pipeline or use the `GetOrAddPipeline(...)` method from `ResiliencePipelineRegistry<T>` to cache the pipeline dynamically:

```csharp theme={null}
public sealed class MyApi
{
    private readonly ResiliencePipelineRegistry<string> _registry;

    // Share a single instance of the registry throughout your application.
    public MyApi(ResiliencePipelineRegistry<string> registry)
    {
        _registry = registry;
    }

    public async Task UpdateData(CancellationToken cancellationToken)
    {
        // Get or create the pipeline, and then cache it for subsequent use.
        // Choose a sufficiently unique key to prevent collisions.
        var pipeline = _registry.GetOrAddPipeline("my-app.my-api", builder =>
        {
            builder.AddRetry(new()
            {
                ShouldHandle = new PredicateBuilder()
                    .Handle<InvalidOperationException>()
                    .Handle<HttpRequestException>()
            });
        });

        await pipeline.ExecuteAsync(async token =>
        {
            // Place your logic here
        },
        cancellationToken);
    }
}
```

<Note>
  You can also define your pipeline on startup using [dependency injection](/advanced/dependency-injection#basic-usage) and then use `ResiliencePipelineProvider<T>` to retrieve the instance.
</Note>

## Performance Best Practices Summary

<CardGroup cols={2}>
  <Card title="Static Lambdas" icon="function">
    Use static lambdas and pass state as parameters to avoid closure allocations.
  </Card>

  <Card title="Switch Expressions" icon="code-branch">
    Prefer switch expressions over PredicateBuilder for better performance.
  </Card>

  <Card title="Outcome API" icon="arrow-right-arrow-left">
    Use ExecuteOutcomeAsync to avoid exception throwing overhead.
  </Card>

  <Card title="Reuse Pipelines" icon="recycle">
    Cache and reuse resilience pipeline instances instead of creating new ones.
  </Card>
</CardGroup>
