You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Many Polly policies allow callback delegates to be configured, eg onRetry, onBreak, onBulkheadRejected. When these are not configured, Polly currently handles this internally by substituting an empty delegate { }.
This issue proposes non-configured callbacks instead be handled internally as null, with eg onRetry?.Invoke(...) (and async equivalent).
The change saves allocations of 64 bytes per execution for some execution cases. (There are also execution-speed improvements, but in the order of nanoseconds - mostly marginal compared to the kind of I/O work Polly normally guards.)
A comment here proposed non-configured callbacks would be better handled as an empty method. A simple BenchmarkDotNet benchmark suggests this is not so:
public class BenchmarkLambdaMethodLocalFunctionInlining
{
public static void Execute(Action action)
{
action?.Invoke();
}
[Benchmark(Baseline = true)]
public void AllocationNullToActionV0()
{
Action doNothingAction = null;
Execute(doNothingAction);
}
[Benchmark]
public void AllocationToActionV0()
{
// ReSharper disable once ConvertToLocalFunction
Action doNothingAction = () => { };
Execute(doNothingAction);
}
[Benchmark]
public void LocalFunctionV0()
{
void DoNothingLocalFunction()
{
}
Execute(DoNothingLocalFunction);
}
[Benchmark]
public void DeclaredMethodV0()
{
Execute(DoNothingDeclaredMethodV0);
}
private void DoNothingDeclaredMethodV0()
{
}
}
(the ? in the results is BenchmarkDotNet telling us the actual execution time of the baseline null case was too close to zero (strictly, too close to the benchmark overhead) for the ratios to be considered accurate to calculate)
Both an empty local function and empty declared method cost an extra 64 bytes. It appears that the compiler/JITter can inline the empty delegate { } in this case, but not a pointer to an empty method/local function.
Interestingly, there is no allocation improvement (in this benchmark) between a null and an empty { }. It appears the empty delegate { } in the benchmark can be inlined enough to remove the allocation. However, when the same change is applied to the more complex Polly codebase (always test real code, not only artificial benchmarks 🙂 ), some policy-executions do consistently save 64 bytes of allocs per execution by changing from { } to null.
Handling non-configured callbacks as null will also play more nicely with new syntax proposals.
The text was updated successfully, but these errors were encountered:
Many Polly policies allow callback delegates to be configured, eg
onRetry
,onBreak
,onBulkheadRejected
. When these are not configured, Polly currently handles this internally by substituting an empty delegate{ }
.This issue proposes non-configured callbacks instead be handled internally as
null
, with egonRetry?.Invoke(...)
(and async equivalent).The change saves allocations of 64 bytes per execution for some execution cases. (There are also execution-speed improvements, but in the order of nanoseconds - mostly marginal compared to the kind of I/O work Polly normally guards.)
The change will also play more nicely with new syntax proposals.
Detail
A comment here proposed non-configured callbacks would be better handled as an empty method. A simple BenchmarkDotNet benchmark suggests this is not so:
Results:
(the
?
in the results is BenchmarkDotNet telling us the actual execution time of the baselinenull
case was too close to zero (strictly, too close to the benchmark overhead) for the ratios to be considered accurate to calculate)Both an empty local function and empty declared method cost an extra 64 bytes. It appears that the compiler/JITter can inline the empty delegate
{ }
in this case, but not a pointer to an empty method/local function.Interestingly, there is no allocation improvement (in this benchmark) between a
null
and an empty{ }
. It appears the empty delegate{ }
in the benchmark can be inlined enough to remove the allocation. However, when the same change is applied to the more complex Polly codebase (always test real code, not only artificial benchmarks 🙂 ), some policy-executions do consistently save 64 bytes of allocs per execution by changing from{ }
tonull
.Handling non-configured callbacks as
null
will also play more nicely with new syntax proposals.The text was updated successfully, but these errors were encountered: