add a little readme, factor things properly
This commit is contained in:
parent
b8c9d55258
commit
335a9cc99a
159
Program.cs
159
Program.cs
|
|
@ -330,7 +330,23 @@ internal static class Parsing
|
|||
|
||||
public static EvaluationInstructions Parse(string filename)
|
||||
{
|
||||
var identifiers = new Dictionary<string, Operand>();
|
||||
// parse all identifiers first, so we know which are directly associated with constant values
|
||||
Dictionary<string, Operand> identifiers = ParseIdentifiers(filename);
|
||||
|
||||
// now parse instructions, correctly tagging operands that operate directly on constants
|
||||
List<Instruction> instructions = ParseInstructions(filename, identifiers);
|
||||
|
||||
// eliminate any constants from the tape, folding them into the instruction operands instead
|
||||
(int totalNumberOfInstructions, int numberOfRemovedInstructions) = EliminateConstants(instructions);
|
||||
Console.WriteLine($"Sharpero eliminated {numberOfRemovedInstructions} constants from tape, {(float)numberOfRemovedInstructions / totalNumberOfInstructions * 100.0f:0.0} % of total");
|
||||
|
||||
return new EvaluationInstructions(instructions.ToArray());
|
||||
}
|
||||
|
||||
private static Dictionary<string, Operand> ParseIdentifiers(string filename)
|
||||
{
|
||||
Dictionary<string, Operand> identifiers = [];
|
||||
|
||||
foreach (string line in File.ReadAllLines(filename))
|
||||
{
|
||||
if (line.StartsWith('#'))
|
||||
|
|
@ -358,7 +374,13 @@ internal static class Parsing
|
|||
}
|
||||
}
|
||||
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
private static List<Instruction> ParseInstructions(string filename, Dictionary<string, Operand> identifiers)
|
||||
{
|
||||
List<Instruction> instructions = [];
|
||||
|
||||
foreach (string line in File.ReadAllLines(filename))
|
||||
{
|
||||
if (line.StartsWith('#'))
|
||||
|
|
@ -370,7 +392,7 @@ internal static class Parsing
|
|||
{
|
||||
[{ } @out, "var-x"] => new Instruction(identifiers[@out], OpCode.VarX),
|
||||
[{ } @out, "var-y"] => new Instruction(identifiers[@out], OpCode.VarY),
|
||||
[{ } @out, "const", { } v]=> new Instruction(identifiers[@out], OpCode.Const, C: float.Parse(v, CultureInfo.InvariantCulture)),
|
||||
[{ } @out, "const", { } v] => new Instruction(identifiers[@out], OpCode.Const, C: float.Parse(v, CultureInfo.InvariantCulture)),
|
||||
[{ } @out, "add", { } a, { } b] => new Instruction(identifiers[@out], OpCode.Add, identifiers[a], identifiers[b]),
|
||||
[{ } @out, "sub", { } a, { } b] => new Instruction(identifiers[@out], OpCode.Sub, identifiers[a], identifiers[b]),
|
||||
[{ } @out, "mul", { } a, { } b] => new Instruction(identifiers[@out], OpCode.Mul, identifiers[a], identifiers[b]),
|
||||
|
|
@ -388,80 +410,77 @@ internal static class Parsing
|
|||
}
|
||||
}
|
||||
|
||||
bool shouldEliminateConstants = true;
|
||||
return instructions;
|
||||
}
|
||||
|
||||
if (shouldEliminateConstants)
|
||||
private static (int TotalInstructions, int NumberOfRemovedInstructions) EliminateConstants(List<Instruction> instructions)
|
||||
{
|
||||
// handle constant propagation, eliminating all constants from the instruction stream and merging them
|
||||
foreach (ref Instruction instruction in CollectionsMarshal.AsSpan(instructions))
|
||||
{
|
||||
// handle constant propagation, eliminating all constants from the instruction stream and merging them
|
||||
foreach (ref Instruction instruction in CollectionsMarshal.AsSpan(instructions))
|
||||
switch (instruction.OpCode)
|
||||
{
|
||||
switch (instruction.OpCode)
|
||||
{
|
||||
case (OpCode.Add or OpCode.Sub or OpCode.Mul) when instruction.A.IsConstant || instruction.B.IsConstant:
|
||||
switch (instruction)
|
||||
{
|
||||
case { A: { IsConstant: true } a, B.IsConstant: false }:
|
||||
instruction = instruction with { C = instructions[a].C };
|
||||
break;
|
||||
case { A.IsConstant: false, B: { IsConstant: true } b }:
|
||||
instruction = instruction with { C = instructions[b].C };
|
||||
break;
|
||||
case { OpCode: var opCode, A: { IsConstant: true } a, B: { IsConstant: true } b }:
|
||||
instruction = instruction with { C = EvaluateExpression(opCode, instructions[a].C, instructions[b].C) };
|
||||
break;
|
||||
case { A.IsConstant: false, B.IsConstant: false }:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode.Add or OpCode.Sub or OpCode.Mul when instruction.A.IsConstant || instruction.B.IsConstant:
|
||||
switch (instruction)
|
||||
{
|
||||
case { A: { IsConstant: true } a, B.IsConstant: false }:
|
||||
instruction = instruction with { C = instructions[a].C };
|
||||
break;
|
||||
case { A.IsConstant: false, B: { IsConstant: true } b }:
|
||||
instruction = instruction with { C = instructions[b].C };
|
||||
break;
|
||||
case { OpCode: var opCode, A: { IsConstant: true } a, B: { IsConstant: true } b }:
|
||||
instruction = instruction with { C = EvaluateExpression(opCode, instructions[a].C, instructions[b].C) };
|
||||
break;
|
||||
case { A.IsConstant: false, B.IsConstant: false }:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// relocate all offsets, now that we've nuked all the constants
|
||||
foreach (ref Instruction instruction in CollectionsMarshal.AsSpan(instructions))
|
||||
{
|
||||
switch (instruction.OpCode)
|
||||
{
|
||||
case OpCode.Const:
|
||||
int offset = instruction.Out;
|
||||
foreach (ref Instruction otherInstruction in CollectionsMarshal.AsSpan(instructions))
|
||||
{
|
||||
int newOperandOut = otherInstruction.Out >= offset
|
||||
? otherInstruction.Out - 1
|
||||
: otherInstruction.Out;
|
||||
|
||||
int newOperandA = otherInstruction.A >= offset
|
||||
? otherInstruction.A - 1
|
||||
: otherInstruction.A;
|
||||
|
||||
int newOperandB = otherInstruction.B >= offset
|
||||
? otherInstruction.B - 1
|
||||
: otherInstruction.B;
|
||||
|
||||
if (newOperandOut == otherInstruction.Out
|
||||
&& newOperandA == otherInstruction.A
|
||||
&& newOperandB == otherInstruction.B)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
otherInstruction = otherInstruction with
|
||||
{
|
||||
Out = new Operand(newOperandOut),
|
||||
A = new Operand(newOperandA, isConstant: otherInstruction.A.IsConstant),
|
||||
B = new Operand(newOperandB, isConstant: otherInstruction.B.IsConstant),
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// eliminate all constants now :)
|
||||
int totalNumberOfInstructions = instructions.Count;
|
||||
int numberOfRemovedInstructions = instructions.RemoveAll(i => i.OpCode == OpCode.Const);
|
||||
Console.WriteLine($"Sharpero eliminated {numberOfRemovedInstructions} constants from tape, {(float)numberOfRemovedInstructions / totalNumberOfInstructions * 100.0f:0.0} % of total");
|
||||
}
|
||||
|
||||
return new EvaluationInstructions(instructions.ToArray());
|
||||
// relocate all offsets, now that we've nuked all the constants
|
||||
foreach (ref Instruction instruction in CollectionsMarshal.AsSpan(instructions))
|
||||
{
|
||||
switch (instruction.OpCode)
|
||||
{
|
||||
case OpCode.Const:
|
||||
int offset = instruction.Out;
|
||||
foreach (ref Instruction otherInstruction in CollectionsMarshal.AsSpan(instructions))
|
||||
{
|
||||
int newOperandOut = otherInstruction.Out >= offset
|
||||
? otherInstruction.Out - 1
|
||||
: otherInstruction.Out;
|
||||
|
||||
int newOperandA = otherInstruction.A >= offset
|
||||
? otherInstruction.A - 1
|
||||
: otherInstruction.A;
|
||||
|
||||
int newOperandB = otherInstruction.B >= offset
|
||||
? otherInstruction.B - 1
|
||||
: otherInstruction.B;
|
||||
|
||||
if (newOperandOut == otherInstruction.Out
|
||||
&& newOperandA == otherInstruction.A
|
||||
&& newOperandB == otherInstruction.B)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
otherInstruction = otherInstruction with
|
||||
{
|
||||
Out = new Operand(newOperandOut),
|
||||
A = new Operand(newOperandA, isConstant: otherInstruction.A.IsConstant),
|
||||
B = new Operand(newOperandB, isConstant: otherInstruction.B.IsConstant),
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int totalNumberOfInstructions = instructions.Count;
|
||||
int numberOfRemovedInstructions = instructions.RemoveAll(i => i.OpCode == OpCode.Const);
|
||||
return (TotalInstructions: totalNumberOfInstructions, NumberOfRemovedInstructions: numberOfRemovedInstructions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
Sharpero
|
||||
----------
|
||||
This is an implementation of an Interpreter/Compiler for the [Prospero](https://www.mattkeeter.com/projects/prospero/) challenge, implementing some basic optimizations including removing constants from the instruction tape, vectorization, parallelization but also optionally compiling the loop of the evaluator to [CIL](https://en.wikipedia.org/wiki/Common_Intermediate_Language) prior to invocation.
|
||||
|
||||
It doesn't perform any sophisticated interval-arithmetic based optimizations (... yet), it's essentially a brute-force approach.
|
||||
|
||||
This program is also an interactive visualizer of the rendering process, allowing you to see exactly how it's writing the image data out, and toggle vectorization, parallel execution, compilation on/off and observe the effects on runtime.
|
||||
|
||||
On my own machine (CPU: Ryzen 7 4800HS), the results tabulate roughly as follows.
|
||||
|
||||
# (Crude) Benchmark Results
|
||||
| Compilation | Parallelism | Vectorization | Evaluation Time | Compilation Time |
|
||||
|-------------|-------------|---------------|-----------------|------------------|
|
||||
| enabled | enabled | enabled | 0.2s | 0.2s |
|
||||
| enabled | enabled | disabled | 1.3s | 0.2s |
|
||||
| enabled | disabled | enabled | 1.7s | 0.2s |
|
||||
| enabled | disabled | disabled | 10s | 0.2s |
|
||||
| disabled | enabled | enabled | 0.7s | N/A |
|
||||
| disabled | enabled | disabled | 5.0s | N/A |
|
||||
| disabled | disabled | disabled | 48s | N/A |
|
||||
Loading…
Reference in New Issue