iNITIal commit, simd accelerated interpreter
This commit is contained in:
commit
84a1ad7330
|
|
@ -0,0 +1,5 @@
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/modules.xml
|
||||||
|
/contentModel.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/.idea.Sharpero.iml
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Sharpero
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
int currentImageSize = 1024;
|
||||||
|
(float[] result, double timeTakenSecondsEvaluate) = BenchmarkFunction(() =>
|
||||||
|
{
|
||||||
|
Instruction[] instructions = Parsing.Parse("Programs/prospero.vm");
|
||||||
|
return Interpreter.Evaluate(instructions, imageSize: currentImageSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine($"Sharpero took: {timeTakenSecondsEvaluate} seconds to evaluate {currentImageSize}x{currentImageSize} image!");
|
||||||
|
|
||||||
|
(bool success, double timeTakenSecondsOutput) = BenchmarkFunction(() =>
|
||||||
|
{
|
||||||
|
CreateOutputImage(currentImageSize, result, "prospero.jpg");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine($"Sharpero took: {timeTakenSecondsOutput} seconds to write out {currentImageSize}x{currentImageSize} image!");
|
||||||
|
|
||||||
|
(T, double) BenchmarkFunction<T>(Func<T> benchmarkedFunction)
|
||||||
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
T benchmarkedResult = benchmarkedFunction();
|
||||||
|
return (benchmarkedResult, sw.Elapsed.TotalSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateOutputImage(int imageSize, float[] imageData, string imageOutputPath)
|
||||||
|
{
|
||||||
|
// #TODO: remove bitmap usage as this draws through a canvas every time, can we just set the pixel data directly?
|
||||||
|
using SKBitmap bitmap = new SKBitmap(imageSize, imageSize, isOpaque: true);
|
||||||
|
for (int x = 0; x < imageSize; ++x)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < imageSize; ++y)
|
||||||
|
{
|
||||||
|
byte v = imageData[Parsing.CoordToIndex(x, y, width: imageSize)] < 0 ? (byte)255 : (byte)0;
|
||||||
|
SKColor c = new SKColor(v, v, v, 255);
|
||||||
|
bitmap.SetPixel(x, y, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using var image = SKImage.FromBitmap(bitmap);
|
||||||
|
using var data = image.Encode(SKEncodedImageFormat.Jpeg, 100);
|
||||||
|
using var stream = File.OpenWrite(imageOutputPath);
|
||||||
|
data.SaveTo(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OpCode
|
||||||
|
{
|
||||||
|
VarX,
|
||||||
|
VarY,
|
||||||
|
Const,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Max,
|
||||||
|
Min,
|
||||||
|
Neg,
|
||||||
|
Square,
|
||||||
|
Sqrt
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly record struct Instruction(int Out, OpCode OpCode, int A = -1, int B = -1, float V = float.MaxValue);
|
||||||
|
|
||||||
|
static class Parsing
|
||||||
|
{
|
||||||
|
|
||||||
|
// 1D <-> 2D coordinate helpers for square grids
|
||||||
|
public static (int x, int y) IndexToCoord(int idx, int width) => (idx % width, idx / width);
|
||||||
|
public static int CoordToIndex(int x, int y, int width) => x + (y * width);
|
||||||
|
|
||||||
|
// the identifiers are hexadecimal, stripping off the leading _ is enough :)
|
||||||
|
public static int ParseIdentifier(string id) => Convert.ToInt32(id[1..], 16);
|
||||||
|
|
||||||
|
public static Instruction[] Parse(string filename)
|
||||||
|
{
|
||||||
|
List<Instruction> instructions = [];
|
||||||
|
|
||||||
|
foreach (string line in File.ReadAllLines(filename))
|
||||||
|
{
|
||||||
|
Instruction? parsedInstruction = line.Split(" ") switch
|
||||||
|
{
|
||||||
|
[{ } @out, "var-x"] => new Instruction(ParseIdentifier(@out), OpCode.VarX),
|
||||||
|
[{ } @out, "var-y"] => new Instruction(ParseIdentifier(@out), OpCode.VarY),
|
||||||
|
[{ } @out, "const", {} v] => new Instruction(ParseIdentifier(@out), OpCode.Const, V: float.Parse(v, CultureInfo.InvariantCulture)),
|
||||||
|
[{ } @out, "add", { } a, { } b] => new Instruction(ParseIdentifier(@out), OpCode.Add, ParseIdentifier(a), ParseIdentifier(b)),
|
||||||
|
[{ } @out, "sub", { } a, { } b] => new Instruction(ParseIdentifier(@out), OpCode.Sub, ParseIdentifier(a), ParseIdentifier(b)),
|
||||||
|
[{ } @out, "mul", { } a, { } b] => new Instruction(ParseIdentifier(@out), OpCode.Mul, ParseIdentifier(a), ParseIdentifier(b)),
|
||||||
|
[{ } @out, "max", { } a, { } b] => new Instruction(ParseIdentifier(@out), OpCode.Max, ParseIdentifier(a), ParseIdentifier(b)),
|
||||||
|
[{ } @out, "min", { } a, { } b] => new Instruction(ParseIdentifier(@out), OpCode.Min, ParseIdentifier(a), ParseIdentifier(b)),
|
||||||
|
[{ } @out, "neg", { } a] => new Instruction(ParseIdentifier(@out), OpCode.Neg, ParseIdentifier(a)),
|
||||||
|
[{ } @out, "square", { } a] => new Instruction(ParseIdentifier(@out), OpCode.Square, ParseIdentifier(a)),
|
||||||
|
[{ } @out, "sqrt", { } a] => new Instruction(ParseIdentifier(@out), OpCode.Sqrt, ParseIdentifier(a)),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parsedInstruction is { } instruction)
|
||||||
|
{
|
||||||
|
instructions.Add(instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class Compiler
|
||||||
|
{
|
||||||
|
static (AssemblyBuilder, MethodBuilder, TypeBuilder) CreateDynamicAssemblyWithMethodBuilder(string assemblyName,
|
||||||
|
string moduleName, string typeName, string methodName)
|
||||||
|
{
|
||||||
|
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run);
|
||||||
|
var moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
|
||||||
|
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
|
||||||
|
var methodBuilder =
|
||||||
|
typeBuilder.DefineMethod(
|
||||||
|
methodName,
|
||||||
|
MethodAttributes.Public | MethodAttributes.Static,
|
||||||
|
typeof(float),
|
||||||
|
[typeof(float), typeof(float)]);
|
||||||
|
|
||||||
|
// #TODO: how do this?
|
||||||
|
// assemblyBuilder.EntryPoint = methodBuilder;
|
||||||
|
|
||||||
|
return (assemblyBuilder, methodBuilder, typeBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BuildProgramToAssembly(Instruction[] instructions)
|
||||||
|
{
|
||||||
|
var (assemblyBuilder, methodBuilder, typeBuilder) = CreateDynamicAssemblyWithMethodBuilder(
|
||||||
|
assemblyName: "SharperoAssembly",
|
||||||
|
moduleName: "SharperoModule",
|
||||||
|
typeName: "SharperoMain",
|
||||||
|
methodName: "Evaluate");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ExecuteProgramInAssembly()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float[] Evaluate(Instruction[] instructions, int imageSize)
|
||||||
|
{
|
||||||
|
return Array.Empty<float>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Interpreter
|
||||||
|
{
|
||||||
|
public static float[] Evaluate(Instruction[] instructions, int imageSize)
|
||||||
|
{
|
||||||
|
float[] result = new float[imageSize * imageSize];
|
||||||
|
|
||||||
|
int chunkSize = Vector<float>.Count;
|
||||||
|
Parallel.For(0, (imageSize * imageSize) / chunkSize, chunkIdx =>
|
||||||
|
{
|
||||||
|
Span<float> xs = stackalloc float[chunkSize];
|
||||||
|
Span<float> ys = stackalloc float[chunkSize];
|
||||||
|
for (int idx = 0; idx < chunkSize; ++idx)
|
||||||
|
{
|
||||||
|
int currentIdx = chunkIdx * chunkSize + idx;
|
||||||
|
(int x, int y) = Parsing.IndexToCoord(currentIdx, width: imageSize);
|
||||||
|
|
||||||
|
// fix up the coordinate space, our space is actually more like [-imageSize * 0.5f, imageSize * 0.5f],
|
||||||
|
// ... rather than [0, imageSize] in x/y, so this gives us the expected result
|
||||||
|
float vx = (x / (imageSize * 0.5f)) - 1.0f;
|
||||||
|
float vy = 1.0f - (y / (imageSize * 0.5f));
|
||||||
|
|
||||||
|
(xs[idx], ys[idx]) = (vx, vy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<float> results = Evaluate(instructions, new Vector<float>(xs), new Vector<float>(ys));
|
||||||
|
for (int idx = 0; idx < chunkSize; ++idx)
|
||||||
|
{
|
||||||
|
int currentIdx = chunkIdx * chunkSize + idx;
|
||||||
|
(int x, int y) = Parsing.IndexToCoord(currentIdx, width: imageSize);
|
||||||
|
result[Parsing.CoordToIndex(x, y, width: imageSize)] = results[idx];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector<float> Evaluate(Instruction[] instructions, Vector<float> xs, Vector<float> ys)
|
||||||
|
{
|
||||||
|
Span<Vector<float>> variables = stackalloc Vector<float>[instructions.Length];
|
||||||
|
|
||||||
|
foreach (ref Instruction instruction in instructions.AsSpan())
|
||||||
|
{
|
||||||
|
variables[instruction.Out] = instruction switch
|
||||||
|
{
|
||||||
|
{ OpCode: OpCode.VarX } => xs,
|
||||||
|
{ OpCode: OpCode.VarY } => ys,
|
||||||
|
{ OpCode: OpCode.Add, A: var a, B: var b } => variables[a] + variables[b],
|
||||||
|
{ OpCode: OpCode.Sub, A: var a, B: var b } => variables[a] - variables[b],
|
||||||
|
{ OpCode: OpCode.Mul, A: var a, B: var b } => variables[a] * variables[b],
|
||||||
|
{ OpCode: OpCode.Max, A: var a, B: var b } => Vector.Max(variables[a], variables[b]),
|
||||||
|
{ OpCode: OpCode.Min, A: var a, B: var b } => Vector.Min(variables[a], variables[b]),
|
||||||
|
{ OpCode: OpCode.Neg, A: var a } => -variables[a],
|
||||||
|
{ OpCode: OpCode.Sqrt, A: var a } => Vector.SquareRoot(variables[a]),
|
||||||
|
{ OpCode: OpCode.Square, A: var a } => variables[a] * variables[a],
|
||||||
|
{ OpCode: OpCode.Const, V: var v } => Vector<float>.One * v,
|
||||||
|
_ => variables[instruction.Out]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return variables[instructions.Length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,14 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Lokad.ILPack" Version="0.3.1" />
|
||||||
|
<PackageReference Include="SkiaSharp" Version="3.119.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sharpero", "Sharpero.csproj", "{7CF4F126-DD5F-42D1-806A-A204031AD967}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7CF4F126-DD5F-42D1-806A-A204031AD967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CF4F126-DD5F-42D1-806A-A204031AD967}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7CF4F126-DD5F-42D1-806A-A204031AD967}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7CF4F126-DD5F-42D1-806A-A204031AD967}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArrayPool_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc8beccd33913594a240a8bed807798632452943eba5864a26bfec56b2e617_003FArrayPool_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AColor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6a9fabe8949749a5835bf490db09a4cc272a00_003F66_003F1010c222_003FColor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANumber_002EParsing_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fde8a243f75215d958afa80cf80a41b4981a44efea7db93bffcdaf7bd6ba378c0_003FNumber_002EParsing_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APersistedAssemblyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F479daf628d169411ac8526d497865727d69e6e7f4832a1ba6e65e18abf4f90_003FPersistedAssemblyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeAssemblyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F89f78f55baa267d67190b18dceab4e687d397e0573226d8f9d32ecc2271863e_003FRuntimeAssemblyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKCanvas_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F40c61ebf4349bc39224b986147986231e7ffd8b3b69ac47f370bd607e7a2_003FSKCanvas_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKPaint_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fb4d6b3f01617f13d8e24965ceb91a3b4bff76e3d61e49788aa782f68597af6a_003FSKPaint_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"sdk": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"rollForward": "latestMinor",
|
||||||
|
"allowPrerelease": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 926 KiB |
Loading…
Reference in New Issue