From f7eaebc1d66109760a4891876071fbb70002c977 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 19:56:01 -0400 Subject: [PATCH 01/13] Clean up algorithm for hot path... Remove linq in DEP_Handler --- src/Engine/ProtoCore/AssociativeGraph.cs | 45 ++++++++++++++++++------ 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/Engine/ProtoCore/AssociativeGraph.cs b/src/Engine/ProtoCore/AssociativeGraph.cs index 201ec7698ca..08be1a8ef96 100644 --- a/src/Engine/ProtoCore/AssociativeGraph.cs +++ b/src/Engine/ProtoCore/AssociativeGraph.cs @@ -17,7 +17,17 @@ public class Utils public static AssociativeGraph.GraphNode GetGraphNodeAtPC(int pc, List graphNodesInScope) { Validity.Assert(graphNodesInScope != null); - return graphNodesInScope.FirstOrDefault(g => g.isActive && g.isDirty && g.updateBlock.startpc == pc); + + for (int i = 0; i < graphNodesInScope.Count; i++) + { + var g = graphNodesInScope[i]; + if (g.isActive && g.isDirty && g.updateBlock.startpc == pc) + { + return g; + } + } + + return null; } /// @@ -29,7 +39,17 @@ public static AssociativeGraph.GraphNode GetGraphNodeAtPC(int pc, List graphNodesInScope) { Validity.Assert(graphNodesInScope != null); - return graphNodesInScope.FirstOrDefault(g => g.isActive && g.isDirty && g.updateBlock.startpc >= pc); + + for (int i = 0; i < graphNodesInScope.Count; i++) + { + var g = graphNodesInScope[i]; + if (g.isActive && g.isDirty && g.updateBlock.startpc >= pc) + { + return g; + } + } + + return null; } /// @@ -659,23 +679,28 @@ public static bool IsGraphNodeRedefined(AssociativeGraph.GraphNode gnode, Associ List redefinedNodes = new List(); if (executingGraphNode != null) { - // Remove this condition when full SSA is enabled - bool isssa = (!executingGraphNode.IsSSANode() && executingGraphNode.DependsOnTempSSA()); - + bool isssa; if (runtimeCore.Options.ExecuteSSA) { isssa = executingGraphNode.IsSSANode(); } + else + { + // Remove this condition when full SSA is enabled + isssa = (!executingGraphNode.IsSSANode() && executingGraphNode.DependsOnTempSSA()); + } + if (!isssa) { + + SymbolNode symbol = executingGraphNode.updateNodeRefList[0].nodeList[0].symbol; + bool isMember = symbol.classScope != Constants.kInvalidIndex + && symbol.functionIndex == Constants.kInvalidIndex; + foreach (AssociativeGraph.GraphNode graphNode in nodesInScope) { bool allowRedefine = true; - SymbolNode symbol = executingGraphNode.updateNodeRefList[0].nodeList[0].symbol; - bool isMember = symbol.classScope != Constants.kInvalidIndex - && symbol.functionIndex == Constants.kInvalidIndex; - if (isMember) { // For member vars, do not allow if not in the same scope @@ -1475,7 +1500,7 @@ public bool IsSSANode() return false; } - var firstNode = updateNodeRefList.First().nodeList.FirstOrDefault(); + var firstNode = updateNodeRefList[0].nodeList[0]; return firstNode != null && firstNode.nodeType == UpdateNodeType.Symbol && firstNode.symbol.isSSATemp; } } From e7639258aa174f7f63847f5c44d7878de3df0bb9 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:00:35 -0400 Subject: [PATCH 02/13] Combine get and update with a single call in PopTo --- src/Engine/ProtoCore/DSASM/Executive.cs | 13 ++++++++----- src/Engine/ProtoCore/RuntimeMemory.cs | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Engine/ProtoCore/DSASM/Executive.cs b/src/Engine/ProtoCore/DSASM/Executive.cs index ecc0a89a0f2..f24831056db 100644 --- a/src/Engine/ProtoCore/DSASM/Executive.cs +++ b/src/Engine/ProtoCore/DSASM/Executive.cs @@ -2438,8 +2438,9 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal case AddressType.MemVarIndex: SymbolNode symbol = GetSymbolNode(blockId, op2.ClassIndex, op1.SymbolIndex); - opPrev = rmem.GetSymbolValue(symbol); - rmem.SetSymbolValue(symbol, opVal); + opPrev = rmem.SetSymbolValueAndGetPreviousValue(symbol, opVal); + +#if DEBUG exe.UpdatedSymbols.Add(symbol); if (IsDebugRun()) @@ -2452,14 +2453,15 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal { logWatchWindow(blockId, op1.SymbolIndex); } - +#endif RecordExecutedGraphNode(); break; case AddressType.StaticMemVarIndex: var staticMember = GetSymbolNode(blockId, Constants.kGlobalScope, op1.StaticVariableIndex); - opPrev = rmem.GetSymbolValue(staticMember); - rmem.SetSymbolValue(staticMember, opVal); + opPrev = rmem.SetSymbolValueAndGetPreviousValue(staticMember, opVal); + +#if DEBUG exe.UpdatedSymbols.Add(staticMember); if (IsDebugRun()) @@ -2469,6 +2471,7 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal } logWatchWindow(blockId, op1.StaticVariableIndex); +#endif break; case AddressType.Register: { diff --git a/src/Engine/ProtoCore/RuntimeMemory.cs b/src/Engine/ProtoCore/RuntimeMemory.cs index dd3b508bc24..05c7e6f1dbb 100644 --- a/src/Engine/ProtoCore/RuntimeMemory.cs +++ b/src/Engine/ProtoCore/RuntimeMemory.cs @@ -186,6 +186,20 @@ public void SetSymbolValue(SymbolNode symbol, StackValue data) Stack[index] = data; } + /// + /// Set the value for symbol on current stack frame and get the previous item. + /// + /// + /// + /// + public StackValue SetSymbolValueAndGetPreviousValue(SymbolNode symbol, StackValue data) + { + int index = GetStackIndex(symbol, FramePointer); + var prevVal = Stack[index]; + Stack[index] = data; + return prevVal; + } + // TO BE DELETED public int GetStackIndex(int offset) { From 45ca5f46143d4fba386013e05aeb98e88b0a2043 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:03:12 -0400 Subject: [PATCH 03/13] Improve DSObject allocation for pointer in Marshaling --- src/Engine/ProtoCore/DSASM/DSObject.cs | 8 +++++++- src/Engine/ProtoCore/DSASM/Heap.cs | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Engine/ProtoCore/DSASM/DSObject.cs b/src/Engine/ProtoCore/DSASM/DSObject.cs index 5ccab6bb174..dfb22217501 100644 --- a/src/Engine/ProtoCore/DSASM/DSObject.cs +++ b/src/Engine/ProtoCore/DSASM/DSObject.cs @@ -1,4 +1,4 @@ -using System; +using System; using ProtoCore.Properties; using ProtoCore.Runtime; @@ -12,6 +12,12 @@ public DSObject(int size, Heap heap) MetaData = new MetaData { type = (int)PrimitiveType.Pointer }; } + public DSObject(int size, Heap heap, MetaData metaData) + : base(size, heap) + { + MetaData = metaData; + } + public DSObject(StackValue[] values, Heap heap) : base(values, heap) { diff --git a/src/Engine/ProtoCore/DSASM/Heap.cs b/src/Engine/ProtoCore/DSASM/Heap.cs index f825cf35808..c3cbd736586 100644 --- a/src/Engine/ProtoCore/DSASM/Heap.cs +++ b/src/Engine/ProtoCore/DSASM/Heap.cs @@ -402,9 +402,7 @@ public StackValue AllocatePointer(int size, MetaData metadata) { try { - int index = AllocateInternal(size, PrimitiveType.Pointer); - var hpe = heapElements[index]; - hpe.MetaData = metadata; + int index = AllocatePointerInternal(size, metadata); return StackValue.BuildPointer(index, metadata); } catch (OutOfMemoryException) @@ -583,6 +581,11 @@ private int AllocateInternal(int size, PrimitiveType type) return AddHeapElement(hpe); } + private int AllocatePointerInternal(int size, MetaData metaData) + { + return AddHeapElement(new DSObject(size, this, metaData)); + } + private int AllocateInternal(StackValue[] values, PrimitiveType type) { HeapElement hpe = null; From e0f3e46114bd905a42cae18d3549afb1d7102c2d Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:05:27 -0400 Subject: [PATCH 04/13] Cache last symbol lookup (PopTo optimization) stays in scope with Executive --- src/Engine/ProtoCore/DSASM/Executive.cs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Engine/ProtoCore/DSASM/Executive.cs b/src/Engine/ProtoCore/DSASM/Executive.cs index f24831056db..a6858b54f01 100644 --- a/src/Engine/ProtoCore/DSASM/Executive.cs +++ b/src/Engine/ProtoCore/DSASM/Executive.cs @@ -2308,15 +2308,35 @@ private void Execute(int exeblock, int entry, Language language = Language.NotSp } } + private int cacheBlockID = -1; + private int cacheClassIndex = -1; + private int cachesymbolIndex = -1; + private SymbolNode cachedSymbol = null; + protected SymbolNode GetSymbolNode(int blockId, int classIndex, int symbolIndex) { + if (blockId == cacheBlockID && classIndex == cacheClassIndex && symbolIndex == cachesymbolIndex && cachedSymbol != null) + { + return cachedSymbol; + } + if (Constants.kGlobalScope == classIndex) { - return exe.runtimeSymbols[blockId].symbolList[symbolIndex]; + var symbol = exe.runtimeSymbols[blockId].symbolList[symbolIndex]; + cacheBlockID = blockId; + cacheClassIndex = classIndex; + cachesymbolIndex = symbolIndex; + cachedSymbol = symbol; + return symbol; } else { - return exe.classTable.ClassNodes[classIndex].Symbols.symbolList[symbolIndex]; + var symbol = exe.classTable.ClassNodes[classIndex].Symbols.symbolList[symbolIndex]; + cacheBlockID = blockId; + cacheClassIndex = classIndex; + cachesymbolIndex = symbolIndex; + cachedSymbol = symbol; + return symbol; } } From 9218369354aa72e97bebe206e9c67aff4b3195e6 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:07:41 -0400 Subject: [PATCH 05/13] Cache interpretor object for lifetime of JIL function endpoint --- src/Engine/ProtoCore/DSASM/Executive.cs | 11 +++++++ .../ProtoCore/Lang/JILFunctionEndPoint.cs | 31 ++++++++++++++----- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/Engine/ProtoCore/DSASM/Executive.cs b/src/Engine/ProtoCore/DSASM/Executive.cs index a6858b54f01..cd061866c72 100644 --- a/src/Engine/ProtoCore/DSASM/Executive.cs +++ b/src/Engine/ProtoCore/DSASM/Executive.cs @@ -111,6 +111,17 @@ public Executive(RuntimeCore runtimeCore, bool isFep = false) deferedGraphNodes = new List(); } + public void Reset(bool isFep = false) + { + pc = Constants.kInvalidIndex; + executingBlock = Constants.kInvalidIndex; + RX = StackValue.BuildInvalid(); + TX = StackValue.BuildInvalid(); + fepRun = isFep; + terminate = false; + graphNodesInProgramScope = null; + } + /// /// Cache the graphnodes in scope /// diff --git a/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs b/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs index b9eae6bc9cd..7a0deddaf22 100644 --- a/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs +++ b/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs @@ -1,4 +1,4 @@ - + using System.Collections.Generic; using ProtoCore.DSASM; using ProtoCore.Lang.Replication; @@ -19,6 +19,8 @@ public class JILActivationRecord public class JILFunctionEndPoint : FunctionEndPoint { private readonly JILActivationRecord activation; + private Interpreter mInterpreter; + public JILFunctionEndPoint() { activation = new JILActivationRecord(); @@ -35,14 +37,29 @@ public override bool DoesPredicateMatch(ProtoCore.Runtime.Context c, List formalParameters, ProtoCore.DSASM.StackFrame stackFrame, RuntimeCore runtimeCore) { - ProtoCore.DSASM.Interpreter interpreter = new ProtoCore.DSASM.Interpreter(runtimeCore, true); + if (mInterpreter == null) + { + Init(runtimeCore); + } + else + { + mInterpreter.runtime.Reset(true); + } + ProtoCore.DSASM.Executive oldDSASMExec = null; if (runtimeCore.CurrentExecutive != null) { oldDSASMExec = runtimeCore.CurrentExecutive.CurrentDSASMExec; - runtimeCore.CurrentExecutive.CurrentDSASMExec = interpreter.runtime; + runtimeCore.CurrentExecutive.CurrentDSASMExec = mInterpreter.runtime; } try @@ -60,14 +77,14 @@ public override StackValue Execute(ProtoCore.Runtime.Context c, List for (int n = execStateSize - 1; n >= 0; --n) { AssociativeGraph.GraphNode gnode = procedureNode.GraphNodeList[n]; - interpreter.Push(StackValue.BuildBoolean(gnode.isDirty)); + mInterpreter.Push(StackValue.BuildBoolean(gnode.isDirty)); } // Push Params formalParameters.Reverse(); for (int i = 0; i < formalParameters.Count; i++) { - interpreter.Push(formalParameters[i]); + mInterpreter.Push(formalParameters[i]); } StackValue svThisPtr = stackFrame.ThisPtr; @@ -108,7 +125,7 @@ public override StackValue Execute(ProtoCore.Runtime.Context c, List } stackFrame.TX = svCallConvention; - interpreter.runtime.TX = svCallConvention; + mInterpreter.runtime.TX = svCallConvention; // Set SX register stackFrame.BlockIndex = svBlockDecl; @@ -138,7 +155,7 @@ public override StackValue Execute(ProtoCore.Runtime.Context c, List } else { - svRet = interpreter.Run(runtimeCore.RunningBlock, activation.pc, Language.NotSpecified, runtimeCore.Breakpoints); + svRet = mInterpreter.Run(runtimeCore.RunningBlock, activation.pc, Language.NotSpecified, runtimeCore.Breakpoints); runtimeCore.RunningBlock = origRunningBlock; } From a1867af0c301f9e4c2154ebc6487e14b5efe15f4 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:11:27 -0400 Subject: [PATCH 06/13] reuse parameters array & referencePrameter list, cache marshaller & paraminfo in CLR Function Pointer --- .../ProtoCore/FFI/CLRFFIFunctionPointer.cs | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs b/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs index 8f0728f6d4e..95a717eb899 100644 --- a/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs +++ b/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs @@ -500,14 +500,30 @@ private string ErrorString(System.Exception ex) return string.Format(Resources.OperationFailType2, ReflectionInfo.DeclaringType.Name, ReflectionInfo.Name, msg); } + private object[] parameters = null; + private FFIObjectMarshaler marshaller; + private FFIParameterInfo[] paraminfos; + private List referencedParameters; + private object missing = Type.Missing; + public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, List s) { - var parameters = new List(); + if (parameters == null) + { + parameters = new object[mArgTypes.Length]; + marshaller = Module.GetMarshaler(dsi.runtime.RuntimeCore); + paraminfos = ReflectionInfo.GetParameters(); + referencedParameters = new List(); + } + else + { + referencedParameters.Clear(); + } + if (s == null) s = dsi.runtime.rmem.Stack; object thisObject = null; - FFIObjectMarshaler marshaller = Module.GetMarshaler(dsi.runtime.RuntimeCore); if (!ReflectionInfo.IsStatic) { try @@ -525,9 +541,6 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis return null; //Can't call a method on null object. } - FFIParameterInfo[] paraminfos = ReflectionInfo.GetParameters(); - List referencedParameters = new List(); - for (int i = 0; i < mArgTypes.Length; ++i) { var opArg = s[i]; @@ -536,7 +549,7 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis Type paramType = paraminfos[i].Info.ParameterType; object param = null; if (opArg.IsDefaultArgument) - param = Type.Missing; + param = missing; else param = marshaller.UnMarshal(opArg, c, dsi, paramType); @@ -559,7 +572,7 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis } - parameters.Add(param); + parameters[i] = param; } catch (System.InvalidCastException ex) { @@ -574,7 +587,7 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis } } - var ret = InvokeFunctionPointerNoThrow(c, dsi, thisObject, parameters.Count > 0 ? parameters.ToArray() : null); + var ret = InvokeFunctionPointerNoThrow(c, dsi, thisObject, parameters); if (ReflectionInfo.KeepReferenceThis && thisObject != null) { referencedParameters.Add(s.Last()); From 98f38f68d51c49fd2349d004524acf32450e1d45 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:12:40 -0400 Subject: [PATCH 07/13] Optimize check methods in CLR marshaller --- .../ProtoCore/FFI/CLRObjectMarshaler.cs | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs b/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs index dc4d87e0f04..d9bc0b883a2 100644 --- a/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs +++ b/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs @@ -504,12 +504,27 @@ public override object UnMarshal(StackValue dsObject, ProtoCore.Runtime.Context internal static bool IsAssignableFromDictionary(Type expectedCLRType) { - return expectedCLRType == typeof(IDictionary) || - expectedCLRType.GetInterfaces() - .Where(i => i.IsGenericType) - .Select(i => i.GetGenericTypeDefinition()) - .Contains(typeof(IDictionary<,>)) || - expectedCLRType.IsAssignableFrom(typeof(DesignScript.Builtin.Dictionary)); + if (expectedCLRType == typeof(IDictionary)) + return true; + + // Fast path for common generic IDictionary<,> + if (expectedCLRType.IsGenericType && expectedCLRType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + + // Check interfaces without LINQ + var interfaces = expectedCLRType.GetInterfaces(); + for (int i = 0; i < interfaces.Length; i++) + { + var iface = interfaces[i]; + if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + } + + // Check assignability from DesignScript.Builtin.Dictionary + if (expectedCLRType.IsAssignableFrom(typeof(DesignScript.Builtin.Dictionary))) + return true; + + return false; } private object ToIDictionary(StackValue dsObject, ProtoCore.Runtime.Context context, Interpreter dsi, System.Type expectedType) @@ -709,9 +724,9 @@ public override StackValue Marshal(object obj, ProtoCore.Runtime.Context context return retVal; //5. If it is a StackValue, simply return it. - if (obj is StackValue) + if (obj is StackValue sv) { - return (StackValue)obj; + return sv; } //6. Seems like a new object create a new DS object and bind it. From acef308db79e9301bad9e70381503d12846234d9 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:13:41 -0400 Subject: [PATCH 08/13] Use spans to avoid extra allocation and preallocated list sizes in Replication --- src/Engine/ProtoCore/Lang/CallSite.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Engine/ProtoCore/Lang/CallSite.cs b/src/Engine/ProtoCore/Lang/CallSite.cs index db237faa6c3..beaa2340f1c 100644 --- a/src/Engine/ProtoCore/Lang/CallSite.cs +++ b/src/Engine/ProtoCore/Lang/CallSite.cs @@ -5,6 +5,7 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using DynamoServices; using Newtonsoft.Json; @@ -1421,8 +1422,10 @@ private StackValue Execute( } else //replicated call { + ReadOnlySpan replicationInstructionsSpan = CollectionsMarshal.AsSpan(replicationInstructions); + c.IsReplicating = true; - ret = ExecWithRISlowPath(functionEndPoints, c, formalParameters, replicationInstructions, stackFrame, runtimeCore, singleRunTraceData, newTraceData); + ret = ExecWithRISlowPath(functionEndPoints, c, formalParameters, replicationInstructionsSpan, stackFrame, runtimeCore, singleRunTraceData, newTraceData); } //Do a trace save here @@ -1456,7 +1459,7 @@ private StackValue ExecWithRISlowPath( List functionEndPoints, Context c, List formalParameters, - List replicationInstructions, + ReadOnlySpan replicationInstructions, StackFrame stackFrame, RuntimeCore runtimeCore, SingleRunTraceData previousTraceData, @@ -1467,7 +1470,7 @@ private StackValue ExecWithRISlowPath( throw new NotImplementedException("Parallel mode disabled: {BF417AD5-9EA9-4292-ABBC-3526FC5A149E}"); //Recursion base case - if (replicationInstructions.Count == 0) + if (replicationInstructions.Length == 0) { return ExecWithZeroRI(functionEndPoints, c, formalParameters, stackFrame, runtimeCore, previousTraceData, newTraceData, finalFunctionEndPoint); } @@ -1490,7 +1493,7 @@ private StackValue ExecWithRISlowPath( List repIndecies = ri.ZipIndecies; //this will hold the heap elements for all the arrays that are going to be replicated over - List parameters = new List(); + List parameters = new List(repIndecies.Count); int retSize; switch (algorithm) @@ -1545,7 +1548,7 @@ private StackValue ExecWithRISlowPath( StackValue[] retSVs = new StackValue[retSize]; SingleRunTraceData retTrace = newTraceData; - retTrace.NestedData = new List(); //this will shadow the SVs as they are created + retTrace.NestedData = new List(retSize); //this will shadow the SVs as they are created //Populate out the size of the list with default values //@TODO:Luke perf optimisation here @@ -1595,7 +1598,7 @@ private StackValue ExecWithRISlowPath( } } - List newRIs = replicationInstructions.GetRange(1, replicationInstructions.Count - 1); + ReadOnlySpan newRIs = replicationInstructions[1..]; SingleRunTraceData cleanRetTrace = new SingleRunTraceData(); @@ -1658,7 +1661,7 @@ private StackValue ExecWithRISlowPath( if (supressArray) { - List newRIs = replicationInstructions.GetRange(1, replicationInstructions.Count - 1); + ReadOnlySpan newRIs = replicationInstructions[1..]; return ExecWithRISlowPath(functionEndPoints, c, newFormalParams, newRIs, stackFrame, runtimeCore, previousTraceData, newTraceData, finalFunctionEndPoint); } @@ -1669,7 +1672,7 @@ private StackValue ExecWithRISlowPath( //It was an array pack the arg with the current value newFormalParams[cartIndex] = array.GetValueFromIndex(i, runtimeCore); - List newRIs = replicationInstructions.GetRange(1, replicationInstructions.Count - 1); + ReadOnlySpan newRIs = replicationInstructions[1..]; SingleRunTraceData lastExecTrace; From c5922d79599dc340e42caf20b93903da497b97eb Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:18:21 -0400 Subject: [PATCH 09/13] Improve Data access speed in MirrorData for tesselation and other lookups. Pre allocate marshaller and interpreter --- src/Engine/ProtoCore/Reflection/MirrorData.cs | 71 ++++++++++++++++--- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/src/Engine/ProtoCore/Reflection/MirrorData.cs b/src/Engine/ProtoCore/Reflection/MirrorData.cs index 541d9165f95..4785c428aef 100644 --- a/src/Engine/ProtoCore/Reflection/MirrorData.cs +++ b/src/Engine/ProtoCore/Reflection/MirrorData.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Linq; using Autodesk.DesignScript.Interfaces; +using ProtoCore.AST.AssociativeAST; using ProtoCore.DSASM; using ProtoCore.Utils; @@ -39,11 +40,8 @@ public bool IsFunction // private ProtoCore.Core core; private ProtoCore.RuntimeCore runtimeCore; - - /// - /// - /// - private static GraphicDataProvider dataProvider = new GraphicDataProvider(); + private ProtoCore.DSASM.Interpreter interpreter; + private ProtoFFI.FFIObjectMarshaler marshaler; /// /// Experimental constructor that takes in a core object @@ -70,6 +68,21 @@ public MirrorData(ProtoCore.Core core, ProtoCore.RuntimeCore runtimeCore, StackV svData = sv; } + /// + /// Takes a runtime core object to read runtime data + /// + /// + /// + /// + public MirrorData(ProtoCore.Core core, ProtoCore.RuntimeCore runtimeCore, StackValue sv, Interpreter i, ProtoFFI.FFIObjectMarshaler m) + { + this.core = core; + this.runtimeCore = runtimeCore; + svData = sv; + interpreter = i; + marshaler = m; + } + /// /// Recursively finds all Pointers from the stack value @@ -145,7 +158,20 @@ public IEnumerable GetElements() return null; var array = runtimeCore.Heap.ToHeapObject(svData); - return array.Values.Select(x => new MirrorData(this.core, this.runtimeCore, x)); + + ProtoCore.DSASM.Interpreter interpreter = new ProtoCore.DSASM.Interpreter(runtimeCore, false); + var helper = ProtoFFI.DLLFFIHandler.GetModuleHelper(ProtoFFI.FFILanguage.CSharp); + var marshaler = helper.GetMarshaler(runtimeCore); + var elements = new List(); + + foreach(var item in array.Values) + { + elements.Add(item.IsPointer + ? new MirrorData(this.core, this.runtimeCore, item, interpreter, marshaler) + : new MirrorData(this.core, this.runtimeCore, item)); + } + + return elements; } /// @@ -162,7 +188,7 @@ public IEnumerable GetElements() /// StackValue /// ProtoCore.Core /// System.Object - internal static object GetData(StackValue sv, RuntimeCore runtimeCore) + internal object GetData(StackValue sv, RuntimeCore runtimeCore) { switch (sv.optype) { @@ -177,13 +203,42 @@ internal static object GetData(StackValue sv, RuntimeCore runtimeCore) case AddressType.String: return StringUtils.GetStringValue(sv, runtimeCore); case AddressType.Pointer: - return dataProvider.GetCLRObject(sv, runtimeCore); + return GetCLRObject(sv, runtimeCore); default: break; } return null; } + internal object GetCLRObject(StackValue svData, RuntimeCore runtimeCore) + { + if (null == runtimeCore.DSExecutable.classTable) + return null; + + IList classNodes = runtimeCore.DSExecutable.classTable.ClassNodes; + if (null == classNodes || (classNodes.Count <= 0)) + return null; + + ClassNode classnode = runtimeCore.DSExecutable.classTable.ClassNodes[svData.metaData.type]; + if (!classnode.IsImportedClass) //TODO: look at properties to see if it contains any FFI objects. + return null; + + try + { + if (this.marshaler == null) + { + interpreter = new ProtoCore.DSASM.Interpreter(runtimeCore, false); + var helper = ProtoFFI.DLLFFIHandler.GetModuleHelper(ProtoFFI.FFILanguage.CSharp); + marshaler = helper.GetMarshaler(runtimeCore); + } + return marshaler.UnMarshal(svData, null, interpreter, typeof(object)); + } + catch (System.Exception) + { + return null; + } + } + internal static string PrecisionFormat { get; set; } = "f3"; /// From 42dccdcd398f51b9a14090cd0a1261c9aa6166e8 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Wed, 11 Jun 2025 20:19:59 -0400 Subject: [PATCH 10/13] pre allocate initial heapElement list size --- src/Engine/ProtoCore/DSASM/Heap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Engine/ProtoCore/DSASM/Heap.cs b/src/Engine/ProtoCore/DSASM/Heap.cs index c3cbd736586..07165d3fa1c 100644 --- a/src/Engine/ProtoCore/DSASM/Heap.cs +++ b/src/Engine/ProtoCore/DSASM/Heap.cs @@ -297,8 +297,8 @@ public enum GCMark Black // Objects that are in use (are not candidates for garbage collection) and have had all their references traced. } - private readonly List freeList = new List(); - private readonly List heapElements = new List(); + private readonly List freeList = new List(GC_THRESHOLD); + private readonly List heapElements = new List(GC_THRESHOLD); private HashSet fixedHeapElements = new HashSet(); private readonly StringTable stringTable = new StringTable(); // The expected lifetime of the sweepSet is start of GC to end of GC From 58e08f3ba1cee8d425b4333910abddfd0480d906 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Mon, 23 Jun 2025 23:46:28 -0400 Subject: [PATCH 11/13] Fix test failures (cherry picked from commit 9f5e99050e6a5dfd732a0e641cc4e987a2d50c32) --- src/Engine/ProtoCore/DSASM/Executive.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Engine/ProtoCore/DSASM/Executive.cs b/src/Engine/ProtoCore/DSASM/Executive.cs index cd061866c72..dd9d4603b58 100644 --- a/src/Engine/ProtoCore/DSASM/Executive.cs +++ b/src/Engine/ProtoCore/DSASM/Executive.cs @@ -2471,9 +2471,9 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal SymbolNode symbol = GetSymbolNode(blockId, op2.ClassIndex, op1.SymbolIndex); opPrev = rmem.SetSymbolValueAndGetPreviousValue(symbol, opVal); -#if DEBUG - exe.UpdatedSymbols.Add(symbol); + exe.UpdatedSymbols.Add(symbol); +#if DEBUG if (IsDebugRun()) { logWatchWindow(blockId, op1.SymbolIndex); @@ -2492,9 +2492,9 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal var staticMember = GetSymbolNode(blockId, Constants.kGlobalScope, op1.StaticVariableIndex); opPrev = rmem.SetSymbolValueAndGetPreviousValue(staticMember, opVal); -#if DEBUG - exe.UpdatedSymbols.Add(staticMember); + exe.UpdatedSymbols.Add(staticMember); +#if DEBUG if (IsDebugRun()) { logWatchWindow(blockId, op1.StaticVariableIndex); From c565687b3b1168ed2124f30e77a7cab14e3d2b32 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Tue, 24 Jun 2025 00:07:40 -0400 Subject: [PATCH 12/13] Fix test failures in FunctionPointer (cherry picked from commit 1a42ed02775abe7823a3995a05080aaf184baef9) --- src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs b/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs index 95a717eb899..a56efe7306b 100644 --- a/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs +++ b/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs @@ -501,7 +501,6 @@ private string ErrorString(System.Exception ex) } private object[] parameters = null; - private FFIObjectMarshaler marshaller; private FFIParameterInfo[] paraminfos; private List referencedParameters; private object missing = Type.Missing; @@ -511,7 +510,6 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis if (parameters == null) { parameters = new object[mArgTypes.Length]; - marshaller = Module.GetMarshaler(dsi.runtime.RuntimeCore); paraminfos = ReflectionInfo.GetParameters(); referencedParameters = new List(); } @@ -524,6 +522,7 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis s = dsi.runtime.rmem.Stack; object thisObject = null; + FFIObjectMarshaler marshaller = Module.GetMarshaler(dsi.runtime.RuntimeCore); if (!ReflectionInfo.IsStatic) { try From 222feabc7712a8dfd0ab3ee1277670324dc3577c Mon Sep 17 00:00:00 2001 From: Craig Long Date: Tue, 24 Jun 2025 13:22:28 -0400 Subject: [PATCH 13/13] Fix recursive calls with interpreter --- src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs b/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs index 7a0deddaf22..3518871e106 100644 --- a/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs +++ b/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs @@ -39,14 +39,14 @@ public override bool DoesPredicateMatch(ProtoCore.Runtime.Context c, List formalParameters, ProtoCore.DSASM.StackFrame stackFrame, RuntimeCore runtimeCore) { - if (mInterpreter == null) + if (mInterpreter == null || c.IsImplicitCall) { Init(runtimeCore); }