From 29a587e33e97ee391831a75e5abb28f6158b865b Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Thu, 14 Nov 2024 23:52:12 -0800 Subject: [PATCH 1/6] Memory optimization pass Implementing frame/label reuse Stopping allocations where possible --- Wacs.Console/CommandLineOptions.cs | 3 + Wacs.Console/Program.cs | 10 +- Wacs.Core/Instructions/Control.cs | 10 +- Wacs.Core/Modules/Sections/FunctionSection.cs | 1 - Wacs.Core/Modules/Sections/StackRenderer.cs | 14 +- Wacs.Core/Runtime/Delegates.cs | 2 +- Wacs.Core/Runtime/ExecContext.cs | 81 ++++---- Wacs.Core/Runtime/Frame.cs | 44 +++-- Wacs.Core/Runtime/InstructionPointer.cs | 19 +- Wacs.Core/Runtime/InvokerOptions.cs | 46 +++++ Wacs.Core/Runtime/Label.cs | 41 +++-- Wacs.Core/Runtime/OpStack.cs | 14 +- Wacs.Core/Runtime/RuntimeAttributes.cs | 36 ++++ Wacs.Core/Runtime/RuntimeOptions.cs | 24 +++ Wacs.Core/Runtime/WasmRuntime.cs | 111 ++++------- Wacs.Core/Types/Expression.cs | 6 +- Wacs.Core/Types/IndexSpace.cs | 23 ++- Wacs.Core/Utilities/IReusable.cs | 25 +++ .../Utilities/InstructionLoggingExtension.cs | 26 +++ Wacs.Core/Utilities/ReusableStack.cs | 174 ++++++++++++++++++ Wacs.Core/Validation/WasmValidationContext.cs | 7 +- 21 files changed, 539 insertions(+), 178 deletions(-) create mode 100644 Wacs.Core/Runtime/InvokerOptions.cs create mode 100644 Wacs.Core/Runtime/RuntimeAttributes.cs create mode 100644 Wacs.Core/Runtime/RuntimeOptions.cs create mode 100644 Wacs.Core/Utilities/IReusable.cs create mode 100644 Wacs.Core/Utilities/InstructionLoggingExtension.cs create mode 100644 Wacs.Core/Utilities/ReusableStack.cs diff --git a/Wacs.Console/CommandLineOptions.cs b/Wacs.Console/CommandLineOptions.cs index e885713..1e337b3 100644 --- a/Wacs.Console/CommandLineOptions.cs +++ b/Wacs.Console/CommandLineOptions.cs @@ -40,6 +40,9 @@ public class CommandLineOptions [Option('g', "log_gas", HelpText = "Print total instructions executed.", Default = false)] public bool LogGas { get; set; } + + [Option('y', "limit_gas", HelpText = "Limit dispatched instructions.", Default = 0)] + public int LimitGas { get; set; } [Option('n',"log_progress", HelpText = "Print a . every n instructions.", Default = -1)] public int LogProgressEvery { get; set; } diff --git a/Wacs.Console/Program.cs b/Wacs.Console/Program.cs index 9e9f563..6530867 100644 --- a/Wacs.Console/Program.cs +++ b/Wacs.Console/Program.cs @@ -211,6 +211,7 @@ static int RunWithOptions(CommandLineOptions opts) var callOptions = new InvokerOptions { LogGas = opts.LogGas, + GasLimit = opts.LimitGas, LogProgressEvery = opts.LogProgressEvery, LogInstructionExecution = opts.LogInstructionExecution, CalculateLineNumbers = opts.CalculateLineNumbers, @@ -292,14 +293,15 @@ static int RunWithOptions(CommandLineOptions opts) return 1; } - var pVals = new List(); + var pVals = new Value[provided.Count]; for (int i = 0; i < provided.Count; i++) { - pVals.Add(new Value(type.ParameterTypes.Types[i], provided[i])); + pVals[i] = new Value(type.ParameterTypes.Types[i], provided[i]); } - var result = caller(pVals.ToArray()); + + Value [] result = caller(pVals); - System.Console.WriteLine($"Result:[{string.Join(" ",result)}]"); + System.Console.WriteLine($"Result:[{string.Join(" ", result)}]"); } catch (TrapException exc) { diff --git a/Wacs.Core/Instructions/Control.cs b/Wacs.Core/Instructions/Control.cs index eb982fe..9d7d9c5 100644 --- a/Wacs.Core/Instructions/Control.cs +++ b/Wacs.Core/Instructions/Control.cs @@ -120,7 +120,7 @@ public static void ExecuteInstruction(ExecContext context, Block block, ByteCode //3. context.Assert(funcType, $"Invalid BlockType: {block.Type}"); //4. - var label = new Label(funcType.ResultType, context.GetPointer(), inst); + // var label = new Label(funcType.ResultType, context.GetPointer(), inst); //5. context.Assert(context.OpStack.Count >= funcType.ParameterTypes.Length, $"Instruction block failed. Operand Stack underflow."); @@ -129,7 +129,7 @@ public static void ExecuteInstruction(ExecContext context, Block block, ByteCode "Shared temporary stack had values left in it."); context.OpStack.PopResults(funcType.ParameterTypes, ref _asideVals); //7. - context.EnterBlock(label, block, _asideVals); + context.EnterBlock(block, funcType.ResultType, inst, _asideVals); } catch (IndexOutOfRangeException exc) { @@ -209,7 +209,7 @@ public override void Execute(ExecContext context) //3. context.Assert(funcType, $"Invalid BlockType: {Block.Type}"); //4. - var label = new Label(funcType.ParameterTypes, context.GetPointer(), OpCode.Loop); + // var label = new Label(funcType.ParameterTypes, context.GetPointer(), OpCode.Loop); //5. context.Assert( context.OpStack.Count >= funcType.ParameterTypes.Length, $"Instruction loop failed. Operand Stack underflow."); @@ -218,7 +218,7 @@ public override void Execute(ExecContext context) "Shared temporary stack had values left in it."); context.OpStack.PopResults(funcType.ParameterTypes, ref _asideVals); //7. - context.EnterBlock(label, Block, _asideVals); + context.EnterBlock(Block, funcType.ParameterTypes, OpCode.Loop, _asideVals); } catch (IndexOutOfRangeException exc) { @@ -485,7 +485,7 @@ public static void ExecuteInstruction(ExecContext context, LabelIdx labelIndex) //5. context.Assert(_asideVals.Count == 0, "Shared temporary stack had values left in it."); - context.OpStack.PopResults(label.Type, ref _asideVals); + context.OpStack.PopResults(label.Arity, ref _asideVals); //6. while (context.OpStack.Count > label.StackHeight) { diff --git a/Wacs.Core/Modules/Sections/FunctionSection.cs b/Wacs.Core/Modules/Sections/FunctionSection.cs index 29a09ab..073887f 100644 --- a/Wacs.Core/Modules/Sections/FunctionSection.cs +++ b/Wacs.Core/Modules/Sections/FunctionSection.cs @@ -199,7 +199,6 @@ public Validator() var funcType = types[func.TypeIndex]; vContext.FunctionIndex = func.Index; vContext.SetExecFrame(funcType, func.Locals); - // vContext.Unreachable = false; //*Expression Validator also validates result types var exprValidator = new Expression.Validator(funcType.ResultType); diff --git a/Wacs.Core/Modules/Sections/StackRenderer.cs b/Wacs.Core/Modules/Sections/StackRenderer.cs index e385500..488b68b 100644 --- a/Wacs.Core/Modules/Sections/StackRenderer.cs +++ b/Wacs.Core/Modules/Sections/StackRenderer.cs @@ -141,14 +141,8 @@ public FakeContext(Module module, Module.Function func) var funcType = Types[func.TypeIndex]; var fakeType = new FunctionType(ResultType.Empty, funcType.ResultType); - var locals = new LocalsSpace(funcType.ParameterTypes.Types, func.Locals); - var execFrame = new Frame(ModuleInst, fakeType) - { - Locals = locals, - Index = func.Index - }; - DummyContext = BuildDummyContext(module, ModuleInst, execFrame); + DummyContext = BuildDummyContext(module, ModuleInst, func); ReturnType = funcType.ResultType; PushControlFrame(OpCode.Block, fakeType); @@ -226,7 +220,7 @@ public void SetUnreachable() public ElementsSpace Elements { get; set; } public DataValidationSpace Datas { get; set; } - private ExecContext BuildDummyContext(Module module, ModuleInstance moduleInst, Frame execFrame) + private ExecContext BuildDummyContext(Module module, ModuleInstance moduleInst, Module.Function modFunc) { var store = new Store(); store.OpenTransaction(); @@ -266,7 +260,11 @@ private ExecContext BuildDummyContext(Module module, ModuleInstance moduleInst, } } + var funcType = Types[modFunc.TypeIndex]; var dummyContext = new ExecContext(store, new RuntimeAttributes { Live = false } ); + var execFrame = dummyContext.ReserveFrame(ModuleInst, new FunctionType(ResultType.Empty, funcType.ResultType), modFunc.Index); + execFrame.Locals = new LocalsSpace(funcType.ParameterTypes.Types, modFunc.Locals); + dummyContext.PushFrame(execFrame); return dummyContext; } diff --git a/Wacs.Core/Runtime/Delegates.cs b/Wacs.Core/Runtime/Delegates.cs index 11e21f0..c8ce967 100644 --- a/Wacs.Core/Runtime/Delegates.cs +++ b/Wacs.Core/Runtime/Delegates.cs @@ -26,7 +26,7 @@ public static class Delegates { public delegate object GenericFunc(params object[] args); - public delegate object[] GenericFuncs(params object[] args); + public delegate Value[] GenericFuncs(params object[] args); public delegate Value[] StackFunc(Value[] parameters); diff --git a/Wacs.Core/Runtime/ExecContext.cs b/Wacs.Core/Runtime/ExecContext.cs index fc1ed74..9fff7d7 100644 --- a/Wacs.Core/Runtime/ExecContext.cs +++ b/Wacs.Core/Runtime/ExecContext.cs @@ -24,21 +24,10 @@ using Wacs.Core.Runtime.Exceptions; using Wacs.Core.Runtime.Types; using Wacs.Core.Types; +using Wacs.Core.Utilities; namespace Wacs.Core.Runtime { - public class RuntimeAttributes - { - public bool Configure_RefTypes = false; - public bool Live = true; - - public int MaxCallStack = 1024; - public int MaxFunctionLocals = 2048; - - public int MaxOpStack = 1024; - public IInstructionFactory InstructionFactory { get; set; } = SpecFactory.Factory; - } - public struct ExecStat { public long duration; @@ -61,13 +50,16 @@ public ExecContext(Store store, RuntimeAttributes? attributes = default) { Store = store; Attributes = attributes ?? new RuntimeAttributes(); + FrameStack = new(Attributes.InitialCallStack, Attributes.GrowCallStack); + LabelStack = new(Attributes.InitialCallStack * 2, Attributes.GrowCallStack); + CallStack = FrameStack.GetSubStack(); _hostReturnSequence = new InstructionSequence( InstructionFactory.CreateInstruction(OpCode.Func) ); _currentSequence = _hostReturnSequence; _sequenceIndex = -1; - OpStack = new(Attributes.MaxCallStack); + OpStack = new(Attributes.MaxOpStack); } public Stopwatch ProcessTimer { get; set; } = new(); @@ -77,7 +69,10 @@ public ExecContext(Store store, RuntimeAttributes? attributes = default) public Store Store { get; } public OpStack OpStack { get; } - private Stack CallStack { get; } = new(); + + private ReusableStack FrameStack { get; } + private SubStack CallStack { get; } + private ReusableStack