Skip to content

Commit

Permalink
Merge pull request #38 from RobertBaruch/push_pop_types
Browse files Browse the repository at this point in the history
Push pop types
  • Loading branch information
RobertBaruch authored Feb 4, 2024
2 parents d301457 + e1e8f13 commit 7382388
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 77 deletions.
61 changes: 34 additions & 27 deletions dergwasm_mod/Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using FrooxEngine;
using static Derg.ResoniteEnv;
using Dergwasm.Runtime;
using System.Reflection;

namespace DergwasmTests
{
Expand Down Expand Up @@ -136,19 +137,20 @@ public void Nop()
}
}

// BenchmarkDotNet v0.13.10, Windows 10 (10.0.19045.3693/22H2/2022Update)
// Intel Core i7-7660U CPU 2.50GHz(Kaby Lake), 1 CPU, 4 logical and 2 physical cores
// [Host] : .NET Framework 4.8.1 (4.8.9195.0), X64 RyuJIT VectorSize=256 [AttachedDebugger]
// .NET Framework 4.7.2 : .NET Framework 4.8.1 (4.8.9195.0), X64 RyuJIT VectorSize=256
// BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.3007/22H2/2022Update/SunValley2)
// 11th Gen Intel Core i7-11700K 3.60GHz, 1 CPU, 16 logical and 8 physical cores
// [Host] : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256 [AttachedDebugger]
// .NET Framework 4.7.2 : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256
//
// Job=.NET Framework 4.7.2 Runtime=.NET Framework 4.7.2
//
// | Method | N | Mean | Error | StdDev | Ratio | RatioSD |
// |------------------- |---- |---------:|----------:|----------:|------:|--------:|
// | BaselinePushPopInt | 100 | 1.223 us | 0.0092 us | 0.0082 us | 1.00 | 0.00 |
// | PopAsInt | 100 | 1.506 us | 0.0191 us | 0.0169 us | 1.23 | 0.02 |
// | PushOverloadInt | 100 | 1.240 us | 0.0225 us | 0.0350 us | 1.03 | 0.04 |
// | PushGenericInt | 100 | 1.485 us | 0.0120 us | 0.0106 us | 1.21 | 0.01 |
// | Method | N | Mean | Error | StdDev | Ratio |
// |------------------- |---- |---------:|----------:|----------:|------:|
// | BaselinePushPopInt | 100 | 1.111 us | 0.0015 us | 0.0014 us | 1.00 |
// | PopAsInt | 100 | 1.357 us | 0.0012 us | 0.0010 us | 1.22 |
// | PopOutInt | 100 | 1.122 us | 0.0022 us | 0.0021 us | 1.01 |
// | PushOverloadInt | 100 | 1.121 us | 0.0012 us | 0.0011 us | 1.01 |
// | PushGenericInt | 100 | 1.280 us | 0.0013 us | 0.0011 us | 1.15 |
[SimpleJob(RuntimeMoniker.Net472, baseline: true)]
public class PushPop : TestMachine
{
Expand All @@ -163,16 +165,6 @@ public void Setup()
frame = CreateFrame();
}

// BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.2428/22H2/2022Update/SunValley2)
// 11th Gen Intel Core i7-11700K 3.60GHz, 1 CPU, 16 logical and 8 physical cores
// [Host] : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256
// .NET Framework 4.7.2 : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256
//
// Job=.NET Framework 4.7.2 Runtime=.NET Framework 4.7.2
//
// | Method | Mean | Error | StdDev | Median | Ratio |
// |----------- |---------:|---------:|---------:|---------:|------:|
// | PushPopInt | 22.30 ns | 0.414 ns | 0.991 ns | 21.96 ns | 1.00 |
[Benchmark(Baseline = true)]
public int BaselinePushPopInt()
{
Expand All @@ -197,6 +189,20 @@ public int PopAsInt()
return x;
}

[Benchmark]
public int PopOutInt()
{
int x = 0;
int y;
for (int i = 0; i < N; i++)
{
frame.Push(new Value { s32 = 1 });
frame.Pop(out y);
x += y;
}
return x;
}

[Benchmark]
public int PushOverloadInt()
{
Expand All @@ -222,17 +228,18 @@ public int PushGenericInt()
}
}

// BenchmarkDotNet v0.13.10, Windows 10 (10.0.19045.3930/22H2/2022Update)
// Intel Core i7-7660U CPU 2.50GHz(Kaby Lake), 1 CPU, 4 logical and 2 physical cores
// [Host] : .NET Framework 4.8.1 (4.8.9195.0), X64 RyuJIT VectorSize=256 [AttachedDebugger]
// .NET Framework 4.7.2 : .NET Framework 4.8.1 (4.8.9195.0), X64 RyuJIT VectorSize=256
// BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.3007/22H2/2022Update/SunValley2)
// 11th Gen Intel Core i7-11700K 3.60GHz, 1 CPU, 16 logical and 8 physical cores
// [Host] : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256 [AttachedDebugger]
// .NET Framework 4.7.2 : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256
//
// Job=.NET Framework 4.7.2 Runtime=.NET Framework 4.7.2
//
// | Method | N | Mean | Error | StdDev | Ratio |
// |------------- |----- |---------:|--------:|--------:|------:|
// | HostFuncCall | 1000 | 314.0 us | 4.39 us | 4.11 us | 1.00 |
// | Method | N | Mean | Error | StdDev | Ratio | Allocated | Alloc Ratio |
// |------------- |----- |---------:|--------:|--------:|------:|----------:|------------:|
// | HostFuncCall | 1000 | 151.0 us | 0.18 us | 0.17 us | 1.00 | - | NA |
[SimpleJob(RuntimeMoniker.Net472, baseline: true)]
[MemoryDiagnoser]
public class HostFuncCallBenchmark : InstructionTestFixture
{
[Params(1000)]
Expand Down
82 changes: 50 additions & 32 deletions dergwasm_mod/Dergwasm/Modules/ModuleReflector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,49 +157,63 @@ ApiFunc funcData
var machine = Expression.Parameter(typeof(Machine), "machine");
var frame = Expression.Parameter(typeof(Frame), "frame");

// Parameter Processing
var argsCount = 0;
var parameters = new List<Expression>();
var poppers = new List<Expression>();
var callParams = new List<ParameterExpression>();
var outVars = new List<ParameterExpression>();
foreach (var param in method.GetParameters())
{
if (param.ParameterType == typeof(Machine))
{
parameters.Add(machine);
callParams.Add(machine);
continue;
}
else if (param.ParameterType == typeof(Frame))
{
parameters.Add(frame);
callParams.Add(frame);
continue;
}
else
{
parameters.Add(
Expression.Call(
Expression.ArrayIndex(
Expression.PropertyOrField(frame, nameof(Frame.Locals)),
Expression.Constant(argsCount)
),
typeof(Value)
.GetMethod(nameof(Value.As))
.MakeGenericMethod(param.ParameterType)
)
);

funcData.Parameters.Add(
new Parameter
{
Name = param.Name,
Type = ValueTypeFor(param.ParameterType),
CSType = param.ParameterType.GetNiceName()
}
);
funcData.ParameterValueTypes.Add(ValueTypeFor(param.ParameterType));
// Declare a local var for the popped value.
ParameterExpression poppedValue = Expression.Variable(
param.ParameterType,
param.Name
);
callParams.Add(poppedValue);
outVars.Add(poppedValue);

argsCount++;
// Call the appropriate pop method for the type.
string refTypeName = param.ParameterType.MakeByRefType().Name;
MethodInfo popper = typeof(Frame)
.GetMethods()
.Where(m => m.Name == "Pop")
.Where(m => m.GetParameters().Length == 1)
.Where(m => m.GetParameters()[0].IsOut)
.Where(m => m.GetParameters()[0].ParameterType.Name == refTypeName)
.First();
if (param.ParameterType.IsGenericType)
{
popper = popper.MakeGenericMethod(param.ParameterType.GetGenericArguments());
}
MethodCallExpression popperCaller = Expression.Call(frame, popper, poppedValue);
poppers.Add(popperCaller);

funcData.Parameters.Add(
new Parameter
{
Name = param.Name,
Type = ValueTypeFor(param.ParameterType),
CSType = param.ParameterType.GetNiceName()
}
);
funcData.ParameterValueTypes.Add(ValueTypeFor(param.ParameterType));
}

// The actual inner call.
var result = Expression.Call(method.IsStatic ? null : context, method, parameters);
// The poppers need to be reversed. The first value pushed is the first call arg,
// which means that the first value popped is the *last* call arg.
poppers.Reverse();

// The actual inner call to the host func.
var result = Expression.Call(method.IsStatic ? null : context, method, callParams);

var returnsCount = 0;
if (method.ReturnType != typeof(void))
Expand All @@ -226,9 +240,13 @@ ApiFunc funcData

// TODO: Add the ability to process value tuple based return values.
}
poppers.Add(result);

BlockExpression block = Expression.Block(outVars.ToArray(), poppers);

// This is the invoked callsite. To improve per-call performance, optimize the expression structure going into this compilation.
var callImpl = Expression.Lambda<HostProxy>(result, machine, frame);
// This is the invoked callsite. To improve per-call performance, optimize the expression
// structure going into this compilation.
var callImpl = Expression.Lambda<HostProxy>(block, machine, frame);

var funcCtor = Expression.New(
typeof(HostFunc).GetConstructors().First(),
Expand Down
92 changes: 74 additions & 18 deletions dergwasm_mod/Dergwasm/Runtime/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using Derg.Instructions;
using Derg.Resonite;
using Derg.Wasm;
using Dergwasm.Runtime;
using FrooxEngine;

namespace Derg.Runtime
{
Expand Down Expand Up @@ -99,6 +102,50 @@ public List<Instruction> Code

public T Pop<T>() => Pop().As<T>();

public void Pop(out bool val) => val = (Pop().s32 != 0 ? true : false);

public void Pop(out int val) => val = Pop().s32;

public void Pop(out uint val) => val = Pop().u32;

public void Pop(out long val) => val = Pop().s64;

public void Pop(out ulong val) => val = Pop().u64;

public void Pop(out float val) => val = Pop().f32;

public void Pop(out double val) => val = Pop().f64;

public void Pop(out ResoniteError val) => val = (ResoniteError)Pop().s32;

public void Pop(out ResoniteEnv.ResoniteType val) =>
val = (ResoniteEnv.ResoniteType)Pop().s32;

public void Pop<T>(out WasmRefID<T> refId)
where T : class, IWorldElement => refId = new WasmRefID<T>(Pop().u64);

public void Pop<T>(out Ptr<T> ptr)
where T : struct => ptr = new Ptr<T>(Pop().s32);

public void Pop(out NullTerminatedString ptr) => ptr = new NullTerminatedString(Pop().s32);

public void Pop<T>(out Output<T> ptr)
where T : struct => ptr = new Output<T>(Pop().s32);

public void Pop<T>(out WasmArray<T> ptr)
where T : struct => ptr = new WasmArray<T>(Pop().s32);

public void Pop<T>(out Buff<T> buff)
where T : struct
{
// The first value pushed is the first call arg.
// The first value popped is the last call arg.
// A buff argument is (data, len), so in pop order, it's (len, data).
int len = Pop().s32;
int data = Pop().s32;
buff = new Buff<T>(len, data);
}

public void Push<T>(in T value) => value_stack.Push(Value.From(value));

public void Push(Value val) => value_stack.Push(val);
Expand All @@ -117,6 +164,32 @@ public List<Instruction> Code

public void Push(double val) => Push(new Value { f64 = val });

public void Push(ResoniteError val) => Push((int)val);

public void Push(ResoniteEnv.ResoniteType val) => Push((int)val);

public void Push<T>(WasmRefID<T> val)
where T : class, IWorldElement => Push(val.Id);

public void Push<T>(Ptr<T> val)
where T : struct => Push(val.Addr);

public void Push(NullTerminatedString val) => Push(val.Data.Addr);

public void Push<T>(Output<T> val)
where T : struct => Push(val.Ptr.Addr);

public void Push<T>(WasmArray<T> val)
where T : struct => Push(val.Data.Addr);

public void Push<T>(Buff<T> val)
where T : struct
{
// A buff argument is (data, len).
Push(val.Ptr.Addr);
Push(val.Length);
}

public int StackLevel() => value_stack.Count;

public Label PopLabel() => label_stack.Pop();
Expand Down Expand Up @@ -155,24 +228,7 @@ void InvokeHostFunc(Machine machine, HostFunc f)
if (machine.Debug)
Console.WriteLine($"Invoking host func {f.ModuleName}.{f.Name}");

int arity = f.Signature.returns.Length;
int args = f.Signature.args.Length;

Frame next_frame = new HostFrame(f, Module, this);

// The first value pushed is the first local.
// The first value popped is the first local.
for (int i = args - 1; i >= 0; --i)
{
next_frame.Locals[i] = value_stack.Pop();
}

// For consistency, we also stick a label in.
next_frame.Label = new Label(arity, 0);

f.Proxy.Invoke(machine, next_frame);

next_frame.EndFrame();
f.Proxy.Invoke(machine, this);
}

// Executes a module function call. This sets up a new frame, pops the args off the current frame and
Expand Down
Loading

0 comments on commit 7382388

Please sign in to comment.