Skip to content

Commit

Permalink
Support profiling and analysis development with more determinism
Browse files Browse the repository at this point in the history
Strengthen the reliability of comparing different versions of profiling, code analysis, and compiler by comparing the emitted C# code: Mitigate the accidental introduction of randomness at various stages of the compilation pipeline, ensuring that multiple invocations of the profile-guided optimization consistently yield the same result.
+ Expand interfaces to allow integrating code to choose whether or not to enable the (non-deterministic) cache.
+ Erase randomness introduced by .NET Dictionary implementations (hashing randomization) with additional explicit ordering using stable properties.
  • Loading branch information
Viir committed May 30, 2024
1 parent 082753f commit b576a7d
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 24 deletions.
11 changes: 7 additions & 4 deletions implement/pine/ElmInteractive/InteractiveSessionPine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,14 +282,16 @@ void IDisposable.Dispose()
TreeNodeWithStringPath compileElmProgramCodeFiles,
IReadOnlyList<TestElmInteractive.Scenario> scenarios,
Pine.CompilePineToDotNet.SyntaxContainerConfig syntaxContainerConfig,
int limitNumber)
int limitNumber,
bool enableEvalExprCache)
{
var expressionsProfiles =
scenarios
.Select(scenario =>
CollectExpressionsToOptimizeFromScenario(
compileElmProgramCodeFiles: compileElmProgramCodeFiles,
scenario: scenario))
scenario: scenario,
enableEvalExprCache: enableEvalExprCache))
.ToImmutableArray();

var aggregateExpressionsProfiles =
Expand All @@ -309,14 +311,15 @@ void IDisposable.Dispose()

public static IReadOnlyDictionary<ExpressionUsageAnalysis, ExpressionUsageProfile> CollectExpressionsToOptimizeFromScenario(
TreeNodeWithStringPath compileElmProgramCodeFiles,
TestElmInteractive.Scenario scenario)
TestElmInteractive.Scenario scenario,
bool enableEvalExprCache)
{
var cache = new PineVMCache();

var profilingVM =
new ProfilingPineVM(
overrideParseExpression: cache.BuildParseExprDelegate,
overrideEvaluateExpression: cache.BuildEvalExprDelegate);
overrideEvaluateExpression: enableEvalExprCache ? cache.BuildEvalExprDelegate : null);

var profilingSession = new InteractiveSessionPine(
compileElmProgramCodeFiles: compileElmProgramCodeFiles,
Expand Down
27 changes: 18 additions & 9 deletions implement/pine/Pine/CompilePineToDotNet/CompileToCSharp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,14 @@ MethodDeclarationSyntax memberDeclarationSyntaxForExpression(
}

static Result<string, IReadOnlyDictionary<ExpressionUsageAnalysis, CompiledExpressionFunction>> CompileExpressionFunctions(
IReadOnlyCollection<ExpressionUsageAnalysis> expressionsUsages)
IReadOnlyCollection<ExpressionUsageAnalysis> expressionsUsagesBeforeOrdering)
{
var expressionsUsages =
expressionsUsagesBeforeOrdering
.OrderBy(eu => eu.CompiledExpressionId.ExpressionHashBase16)
.ThenBy(eu => eu.EnvId?.HashBase16)
.ToImmutableArray();

var dictionary = new Dictionary<ExpressionUsageAnalysis, CompiledExpressionFunction>();

var expressions =
Expand Down Expand Up @@ -201,10 +207,6 @@ availableSpecialized.Count is 0
if (dictionary.ContainsKey(expressionUsage))
continue;

var withEnvDerivedId =
CompiledExpressionId(expressionUsage.Expression)
.Extract(err => throw new Exception(err));

var functionInterface =
compilationUnitEnv.GetInterfaceForExprUsage(
expression,
Expand All @@ -221,10 +223,12 @@ availableSpecialized.Count is 0
expressionUsage,
branchesConstrainedEnvIds: supportedConstrainedEnvironments,
functionEnv)
.MapError(err => "Failed to compile expression " + withEnvDerivedId.ExpressionHashBase16[..10] + ": " + err)
.MapError(err =>
"Failed to compile expression " +
expressionUsage.CompiledExpressionId.ExpressionHashBase16[..10] + ": " + err)
.Map(ok =>
new CompiledExpressionFunction(
withEnvDerivedId,
expressionUsage.CompiledExpressionId,
ok.blockSyntax,
ok.dependencies,
functionEnv));
Expand All @@ -240,7 +244,9 @@ availableSpecialized.Count is 0
{
return
result
.MapError(err => "Failed to compile expression " + withEnvDerivedId + ": " + err)
.MapError(err =>
"Failed to compile expression " + expressionUsage.CompiledExpressionId.ExpressionHashBase16[..10] +
": " + err)
.Map(_ => (IReadOnlyDictionary<ExpressionUsageAnalysis, CompiledExpressionFunction>)
ImmutableDictionary<ExpressionUsageAnalysis, CompiledExpressionFunction>.Empty);
}
Expand All @@ -256,7 +262,8 @@ availableSpecialized.Count is 0
.AndThen(compiledExpressionsBeforeOrdering =>
{
var compiledExpressions =
compiledExpressionsBeforeOrdering.OrderBy(ce => ce.Value.Identifier.ExpressionHashBase16).ToImmutableArray();
compiledExpressionsBeforeOrdering
.OrderBy(ce => ce.Key.CompiledExpressionId.ExpressionHashBase16).ToImmutableArray();

var aggregateDependencies =
CompiledExpressionDependencies.Union(compiledExpressions.Select(er => er.Value.Dependencies));
Expand Down Expand Up @@ -540,6 +547,8 @@ .. compiledExpressionsMemberDeclarations.Cast<MemberDeclarationSyntax>()

var branchesForSpecializedRepr =
branchesEnvIds
// Order by number of items, to prioritize the more specialized branches.
.OrderByDescending(envId => envId.ParsedEnvItems.Count)
.Select(envId =>
{
return
Expand Down
7 changes: 4 additions & 3 deletions implement/pine/Pine/PineVM/DynamicPGOShare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ private static Compilation GetOrCreateCompilationForProfiles(
.Select(expressionAndProfile => expressionAndProfile.Key)
.ToImmutableHashSet();

if (previousCompilations.FirstOrDefault(c => expressionsToCompile.SetEquals(c.CompiledExpressions)) is { } matchigPrevious)
return matchigPrevious;
if (previousCompilations.FirstOrDefault(c => expressionsToCompile.SetEquals(c.CompiledExpressions)) is { } matchingPrevious)
return matchingPrevious;

var selectExpressionsDuration = totalStopwatch.Elapsed;

Expand Down Expand Up @@ -317,7 +317,8 @@ public static IEnumerable<KeyValuePair<ExpressionUsageAnalysis, ExpressionUsageP
aggregateExpressionsProfiles
.Where(expressionProfile =>
4 < expressionProfile.Value.UsageCount && ShouldIncludeExpressionInCompilation(expressionProfile.Key.Expression))
.OrderByDescending(expressionAndProfile => expressionAndProfile.Value.UsageCount);
.OrderByDescending(expressionAndProfile => expressionAndProfile.Value.UsageCount)
.ThenBy(expressionAndProfile => expressionAndProfile.Key.CompiledExpressionId.ExpressionHashBase16);

public static bool ShouldIncludeExpressionInCompilation(Expression expression) =>
expression switch
Expand Down
26 changes: 19 additions & 7 deletions implement/pine/Pine/PineVM/ProfilingPineVM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,24 @@
namespace Pine.PineVM;


public record ExpressionUsageAnalysis(
Expression Expression,
EnvConstraintId? EnvId)
public record ExpressionUsageAnalysis
{
public Expression Expression { get; init; }

public CompilePineToDotNet.CompiledExpressionId CompiledExpressionId { init; get; }

public EnvConstraintId? EnvId { get; init; }

public ExpressionUsageAnalysis(Expression expression, EnvConstraintId? envId)
{
Expression = expression;
EnvId = envId;

CompiledExpressionId =
CompilePineToDotNet.CompileToCSharp.CompiledExpressionId(expression)
.Extract(err => throw new System.Exception(err));
}

public override int GetHashCode()
{
return Expression.GetHashCode();
Expand All @@ -23,10 +37,8 @@ public virtual bool Equals(ExpressionUsageAnalysis? other)
if (other is null)
return false;

if (!Expression.Equals(other.Expression))
{
if (CompiledExpressionId.ExpressionHashBase16 != other.CompiledExpressionId.ExpressionHashBase16)
return false;
}

return EnvConstraintId.Equal(EnvId, other.EnvId);
}
Expand All @@ -42,7 +54,7 @@ public record ExpressionUsageProfile(int UsageCount);

public class ProfilingPineVM
{
public static readonly ConcurrentQueue<System.TimeSpan> computeExpressionUsageTimes = new();
public readonly ConcurrentQueue<System.TimeSpan> computeExpressionUsageTimes = new();

public IPineVM PineVM { init; get; }

Expand Down
3 changes: 2 additions & 1 deletion implement/pine/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,8 @@ private static CommandLineApplication AddInteractiveCommand(
compileElmProgramCodeFiles: compileElmProgramCodeFiles,
profilingScenarios,
syntaxContainerConfig: syntaxContainerConfig,
limitNumber: 60);
limitNumber: 60,
enableEvalExprCache: false);

var compileToFileResult =
compileResult
Expand Down

0 comments on commit b576a7d

Please sign in to comment.