Skip to content

Commit f9e60bf

Browse files
authored
perf: eliminate policy evaluation overhead (#93)
2 parents 9b1cec8 + 62d6776 commit f9e60bf

9 files changed

Lines changed: 423 additions & 104 deletions

File tree

Benchmarks/Diagnostics/Support/NoOpAuditor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace ModularityKit.Mutator.Benchmarks.Diagnostics.Support;
77
/// </summary>
88
internal sealed class NoOpAuditor : IMutationAuditor
99
{
10+
public bool IsEnabled => false;
1011
/// <summary>
1112
/// Ignores the supplied audit entry.
1213
/// </summary>

Benchmarks/Diagnostics/Support/NoOpHistoryStore.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace ModularityKit.Mutator.Benchmarks.Diagnostics.Support;
77
/// </summary>
88
internal sealed class NoOpHistoryStore : IMutationHistoryStore
99
{
10+
public bool IsEnabled => false;
1011
/// <summary>
1112
/// Ignores the supplied history entry.
1213
/// </summary>

Benchmarks/Policy/PolicyBenchmarkSupport.cs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using ModularityKit.Mutator.Abstractions;
3+
using ModularityKit.Mutator.Abstractions.Audit;
34
using ModularityKit.Mutator.Abstractions.Changes;
45
using ModularityKit.Mutator.Abstractions.Context;
56
using ModularityKit.Mutator.Abstractions.Engine;
7+
using ModularityKit.Mutator.Abstractions.History;
68
using ModularityKit.Mutator.Abstractions.Intent;
79
using ModularityKit.Mutator.Abstractions.Policies;
810
using ModularityKit.Mutator.Abstractions.Results;
11+
using ModularityKit.Mutator.Benchmarks.Diagnostics.Support;
912
using ModularityKit.Mutator.Runtime;
1013

1114
namespace ModularityKit.Mutator.Benchmarks.Policy;
@@ -21,6 +24,8 @@ public static IMutationEngine BuildEngine(Action<IMutationEngine>? configure = n
2124
{
2225
var services = new ServiceCollection();
2326
services.AddMutators(MutationEngineOptions.Performance);
27+
services.AddSingleton<IMutationAuditor>(new NoOpAuditor());
28+
services.AddSingleton<IMutationHistoryStore>(new NoOpHistoryStore());
2429

2530
var engine = services
2631
.BuildServiceProvider()
@@ -85,7 +90,15 @@ public MutationResult<PolicyBenchmarkState> Apply(PolicyBenchmarkState state)
8590
/// </summary>
8691
public sealed class SyncAllowBenchmarkPolicy : IMutationPolicy<PolicyBenchmarkState>
8792
{
88-
public SyncAllowBenchmarkPolicy(int priority) => Priority = priority;
93+
private readonly PolicyDecision _decision;
94+
private readonly Task<PolicyDecision> _decisionTask;
95+
96+
public SyncAllowBenchmarkPolicy(int priority)
97+
{
98+
Priority = priority;
99+
_decision = PolicyDecision.Allow(Name, "Synchronous benchmark policy allowed the mutation.");
100+
_decisionTask = Task.FromResult(_decision);
101+
}
89102

90103
public string Name => $"{nameof(SyncAllowBenchmarkPolicy)}_{Priority}";
91104

@@ -94,31 +107,43 @@ public sealed class SyncAllowBenchmarkPolicy : IMutationPolicy<PolicyBenchmarkSt
94107
public string? Description => "Synchronous allow policy for benchmark measurements.";
95108

96109
public PolicyDecision Evaluate(IMutation<PolicyBenchmarkState> mutation, PolicyBenchmarkState state)
97-
=> PolicyDecision.Allow(Name, "Synchronous benchmark policy allowed the mutation.");
110+
=> _decision;
111+
112+
public Task<PolicyDecision> EvaluateAsync(
113+
IMutation<PolicyBenchmarkState> mutation,
114+
PolicyBenchmarkState state,
115+
CancellationToken cancellationToken = default)
116+
=> _decisionTask;
98117
}
99118

100119
/// <summary>
101120
/// Asynchronous allow policy used in benchmark scenarios.
102121
/// </summary>
103122
public sealed class AsyncAllowBenchmarkPolicy : IMutationPolicy<PolicyBenchmarkState>
104123
{
105-
public AsyncAllowBenchmarkPolicy(int priority) => Priority = priority;
124+
private readonly PolicyDecision _decision;
125+
private readonly Task<PolicyDecision> _decisionTask;
126+
127+
public AsyncAllowBenchmarkPolicy(int priority)
128+
{
129+
Priority = priority;
130+
_decision = PolicyDecision.Allow(Name, "Asynchronous benchmark policy allowed the mutation.");
131+
_decisionTask = Task.FromResult(_decision);
132+
}
106133

107134
public string Name => $"{nameof(AsyncAllowBenchmarkPolicy)}_{Priority}";
108135

109136
public int Priority { get; }
110137

111138
public string? Description => "Asynchronous allow policy for benchmark measurements.";
112139

113-
public async Task<PolicyDecision> EvaluateAsync(
140+
public Task<PolicyDecision> EvaluateAsync(
114141
IMutation<PolicyBenchmarkState> mutation,
115142
PolicyBenchmarkState state,
116143
CancellationToken cancellationToken = default)
117144
{
118-
await Task.CompletedTask;
119145
cancellationToken.ThrowIfCancellationRequested();
120-
121-
return PolicyDecision.Allow(Name, "Asynchronous benchmark policy allowed the mutation.");
146+
return _decisionTask;
122147
}
123148
}
124149
}

src/Abstractions/Policies/PolicyDecision.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace ModularityKit.Mutator.Abstractions.Policies;
22

33
/// <summary>
4-
/// Represents the decision of a policy regarding a mutation.
4+
/// Represents the decision of policy regarding mutation.
55
/// Contains approval, denial, modification instructions, and metadata.
66
/// </summary>
77
public sealed class PolicyDecision
@@ -46,22 +46,28 @@ public sealed class PolicyDecision
4646
/// </summary>
4747
public DateTimeOffset Timestamp { get; init; } = DateTimeOffset.UtcNow;
4848

49+
private static readonly PolicyDecision _allow = new() { IsAllowed = true };
50+
4951
/// <summary>
5052
/// Creates an allow decision.
5153
/// </summary>
5254
/// <param name="policyName">Optional policy name.</param>
5355
/// <param name="reason">Optional human-readable reason.</param>
5456
/// <returns>A policy decision that allows the mutation.</returns>
5557
public static PolicyDecision Allow(string? policyName = null, string? reason = null)
56-
=> new()
58+
{
59+
if (policyName is null && reason is null)
60+
return _allow;
61+
return new PolicyDecision
5762
{
5863
IsAllowed = true,
5964
PolicyName = policyName,
6065
Reason = reason
6166
};
67+
}
6268

6369
/// <summary>
64-
/// Creates a deny decision with standard error severity.
70+
/// Creates deny decision with standard error severity.
6571
/// </summary>
6672
/// <param name="reason">Reason for denial.</param>
6773
/// <param name="policyName">Optional policy name.</param>
@@ -76,7 +82,7 @@ public static PolicyDecision Deny(string reason, string? policyName = null)
7682
};
7783

7884
/// <summary>
79-
/// Creates a deny decision with critical severity.
85+
/// Creates deny decision with critical severity.
8086
/// </summary>
8187
/// <param name="reason">Reason for denial.</param>
8288
/// <param name="policyName">Optional policy name.</param>
@@ -91,7 +97,7 @@ public static PolicyDecision DenyCritical(string reason, string? policyName = nu
9197
};
9298

9399
/// <summary>
94-
/// Creates a modification decision that adjusts mutation values.
100+
/// Creates modification decision that adjusts mutation values.
95101
/// </summary>
96102
/// <param name="modifications">Dictionary of modifications to apply.</param>
97103
/// <param name="policyName">Optional policy name.</param>
@@ -107,7 +113,7 @@ public static PolicyDecision Modify(
107113
};
108114

109115
/// <summary>
110-
/// Creates a decision that requires additional approval before proceeding.
116+
/// Creates decision that requires additional approval before proceeding.
111117
/// </summary>
112118
/// <param name="requirement">The requirement that must be fulfilled.</param>
113119
/// <param name="policyName">Optional policy name.</param>

0 commit comments

Comments
 (0)