Skip to content

Commit

Permalink
Improve runtime efficiency by caching common derivations of expressions
Browse files Browse the repository at this point in the history
To reduce computational expenses for frequently used aggregate properties derived from expressions and their descendant trees, store these derivations directly in each instance's representation in memory.
  • Loading branch information
Viir committed Sep 11, 2024
1 parent 9628cd3 commit 319cff7
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 87 deletions.
251 changes: 206 additions & 45 deletions implement/Pine.Core/PineVM/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ namespace Pine.PineVM;
[JsonConverter(typeof(JsonConverterForChoiceType))]
public abstract record Expression
{
public abstract int SubexpressionCount { get; }

/// <summary>
/// True if the expression itself or any its subexpressions is of type <see cref="Environment"/>.
/// </summary>
public abstract bool ReferencesEnvironment { get; }

public static readonly Expression EnvironmentInstance = new Environment();

public static readonly Literal LiteralEmptyListInstance =
Expand Down Expand Up @@ -76,13 +83,22 @@ public static Conditional ConditionalInstance(

public record Literal(
PineValue Value)
: Expression;
: Expression
{
public override int SubexpressionCount { get; } = 0;

public override bool ReferencesEnvironment { get; } = false;
}

public record List
: Expression
{
private readonly int slimHashCode;

public override int SubexpressionCount { get; }

public override bool ReferencesEnvironment { get; } = false;

public IReadOnlyList<Expression> items { get; }

internal List(IReadOnlyList<Expression> items)
Expand All @@ -96,6 +112,16 @@ internal List(ListStruct listKey)
items = listKey.Items;

slimHashCode = listKey.slimHashCode;

SubexpressionCount = items.Count;

for (int i = 0; i < items.Count; ++i)
{
SubexpressionCount += items[i].SubexpressionCount;

if (items[i].ReferencesEnvironment)
ReferencesEnvironment = true;
}
}

public virtual bool Equals(List? other)
Expand Down Expand Up @@ -156,16 +182,54 @@ public static int ComputeHashCode(IReadOnlyList<Expression> items)
}
}

public record ParseAndEval(
Expression encoded,
Expression environment)
: Expression;
public record ParseAndEval
: Expression
{
public Expression encoded { get; }

public Expression environment { get; }

public override int SubexpressionCount { get; }

public override bool ReferencesEnvironment { get; }

public ParseAndEval(
Expression encoded,
Expression environment)
{
this.encoded = encoded;
this.environment = environment;

SubexpressionCount =
encoded.SubexpressionCount + environment.SubexpressionCount + 2;

ReferencesEnvironment =
encoded.ReferencesEnvironment || environment.ReferencesEnvironment;
}
}

public record KernelApplication(
string function,
Expression input)
public record KernelApplication
: Expression
{
public string function { get; }

public Expression input { get; }

public override int SubexpressionCount { get; }

public override bool ReferencesEnvironment { get; }

public KernelApplication(
string function,
Expression input)
{
this.function = function;
this.input = input;

SubexpressionCount = input.SubexpressionCount + 1;
ReferencesEnvironment = input.ReferencesEnvironment;
}

public virtual bool Equals(KernelApplication? other)
{
if (other is not { } notNull)
Expand Down Expand Up @@ -198,6 +262,10 @@ public record Conditional

public Expression trueBranch { get; }

public override int SubexpressionCount { get; }

public override bool ReferencesEnvironment { get; }

internal Conditional(
Expression condition,
Expression falseBranch,
Expand All @@ -215,6 +283,17 @@ internal Conditional(
trueBranch = conditionalStruct.TrueBranch;

slimHashCode = conditionalStruct.slimHashCode;

SubexpressionCount =
condition.SubexpressionCount +
falseBranch.SubexpressionCount +
trueBranch.SubexpressionCount +
3;

ReferencesEnvironment =
condition.ReferencesEnvironment ||
falseBranch.ReferencesEnvironment ||
trueBranch.ReferencesEnvironment;
}

public virtual bool Equals(Conditional? other)
Expand Down Expand Up @@ -279,15 +358,64 @@ public static int ComputeHashCode(
}
}

public record Environment : Expression;
public record Environment : Expression
{
public override int SubexpressionCount { get; } = 0;

public override bool ReferencesEnvironment { get; } = true;
}

public record StringTag
: Expression
{
public string tag { get; }

public Expression tagged { get; }

public record StringTag(
string tag,
Expression tagged)
: Expression;
public override int SubexpressionCount { get; }

public override bool ReferencesEnvironment { get; }

public readonly int slimHashCode;

public StringTag(
string tag,
Expression tagged)
{
this.tag = tag;
this.tagged = tagged;

SubexpressionCount = tagged.SubexpressionCount + 1;
ReferencesEnvironment = tagged.ReferencesEnvironment;

slimHashCode = HashCode.Combine(tag, tagged);
}

public override int GetHashCode() =>
slimHashCode;

public virtual bool Equals(StringTag? other)
{
if (ReferenceEquals(other, this))
return true;

if (other is null)
return false;

return
other.slimHashCode == slimHashCode &&
other.tag == tag &&
other.tagged == tagged;
}
}

public record StackReferenceExpression(int offset)
: Expression;
: Expression
{
public override int SubexpressionCount { get; } = 0;

public override bool ReferencesEnvironment { get; } = false;
}

/// <summary>
/// Fusion of the two applications of the kernel functions 'skip' and 'head',
Expand All @@ -296,8 +424,14 @@ public record StackReferenceExpression(int offset)
public record KernelApplications_Skip_Head_Path(
ReadOnlyMemory<int> SkipCounts,
Expression Argument)
: Expression
: Expression
{
public override int SubexpressionCount { get; } =
Argument.SubexpressionCount + 1;

public override bool ReferencesEnvironment { get; } =
Argument.ReferencesEnvironment;

public virtual bool Equals(KernelApplications_Skip_Head_Path? other)
{
if (other is null)
Expand Down Expand Up @@ -328,47 +462,73 @@ public override int GetHashCode()
public record KernelApplication_Equal_Two(
Expression left,
Expression right)
: Expression;
: Expression
{
public override int SubexpressionCount { get; } =
left.SubexpressionCount + right.SubexpressionCount + 2;

/// <summary>
/// Returns true if the expression is independent of the environment, that is none of the expressions in the tree contain an <see cref="Environment"/>.
/// </summary>
public static bool IsIndependent(Expression expression) =>
expression switch
public override bool ReferencesEnvironment { get; } =
left.ReferencesEnvironment || right.ReferencesEnvironment;
}

public static IReadOnlySet<Expression> CollectAllComponentsFromRoots(
IEnumerable<Expression> roots)
{
var components = new HashSet<Expression>();

var stack = new Stack<Expression>(roots);

while (stack.TryPop(out var expression))
{
Environment =>
false,
if (components.Contains(expression))
continue;

components.Add(expression);

Literal =>
true,
IReadOnlyList<Expression> childItems =
expression switch
{
List listExpression =>
listExpression.items,

List list =>
list.items.All(IsIndependent),
Conditional conditionalExpression =>
[
conditionalExpression.condition,
conditionalExpression.falseBranch,
conditionalExpression.trueBranch
],

ParseAndEval decodeAndEvaluate =>
IsIndependent(decodeAndEvaluate.encoded) && IsIndependent(decodeAndEvaluate.environment),
KernelApplication kernelApplicationExpression =>
[kernelApplicationExpression.input],

KernelApplication kernelApplication =>
IsIndependent(kernelApplication.input),
ParseAndEval parseAndEval =>
[parseAndEval.environment, parseAndEval.encoded],

Conditional conditional =>
IsIndependent(conditional.condition) && IsIndependent(conditional.trueBranch) && IsIndependent(conditional.falseBranch),
StringTag stringTagExpression =>
[stringTagExpression.tagged],

StringTag stringTag =>
IsIndependent(stringTag.tagged),
Literal =>
[],

StackReferenceExpression =>
false,
Environment =>
[],

KernelApplications_Skip_Head_Path fused =>
IsIndependent(fused.Argument),
StackReferenceExpression =>
[],

KernelApplication_Equal_Two fused =>
IsIndependent(fused.left) && IsIndependent(fused.right),
_ =>
throw new NotImplementedException(
"Unexpected expression type: " + expression.GetType())
};

_ =>
throw new NotImplementedException(),
};
foreach (var item in childItems)
{
stack.Push(item);
}
}

return components;
}

public static IEnumerable<Expression> EnumerateSelfAndDescendants(
Expression expression) =>
Expand Down Expand Up @@ -444,7 +604,8 @@ public static IEnumerable<Expression> EnumerateSelfAndDescendants(
break;

default:
throw new NotImplementedException("Unknown expression type: " + expression.GetType().FullName);
throw new NotImplementedException(
"Unknown expression type: " + expression.GetType().FullName);
}
}
}
Expand Down
21 changes: 4 additions & 17 deletions implement/Pine.Core/ReusedInstances.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ public record ReusedInstances(
IEnumerable<Expression> expressionRootsSource)
{
public static readonly ReusedInstances Instance =
new(
expressionRootsSource: ExpressionsSource());
new(expressionRootsSource: ExpressionsSource());

public FrozenDictionary<PineValue.ListValue.ListValueStruct, PineValue.ListValue>? ListValues { private set; get; }

Expand Down Expand Up @@ -155,24 +154,12 @@ listItem is PineValue.ListValue oldItemAsList
var expressionsRoots =
expressionRootsSource.ToList();


var allExpressionDescendants = new HashSet<Expression>();

/*
* TODO: Optimize collecting all components and sorting by size.
* */

foreach (var expressionRoot in expressionsRoots)
{
foreach (var descendant in Expression.EnumerateSelfAndDescendants(expressionRoot))
{
allExpressionDescendants.Add(descendant);
}
}
var allExpressionDescendants =
Expression.CollectAllComponentsFromRoots(expressionsRoots);

var allDescendantsOrdered =
allExpressionDescendants
.OrderBy(expression => Expression.EnumerateSelfAndDescendants(expression).Count())
.OrderBy(expression => expression.SubexpressionCount)
.ToList();

var reusedInstancesInConstruction = new Dictionary<Expression, Expression>();
Expand Down
Loading

0 comments on commit 319cff7

Please sign in to comment.