-
-
Notifications
You must be signed in to change notification settings - Fork 91
Open
Labels
Description
Imagine having a compact and serializable Expression representation by using a SOA (structure of arrays):
public struct Expr
{
public int RootLambdaIndex;
// using ImTools.SmallList4 will keep the first 4 items on stack
public SmallList4<LambdaItem> Lambdas; // the root lambda is referred by index
public SmallList4<object> Constants;
public SmallList4<ParamOrVarEntry> ParamsAndVars;
public SmallList4<MethodLikeItem> CtorsMethodsInvokes;
public SmallList4<BinaryEntry> Binaries;
public SmallList4<UnaryEntry> Unaries;
// ... the rest non-conformists
}
// The lambda is repsented as a slice of `LambdaItem`, 0 element in slice denoting the ParamCount.
// The next ParamCount items refer to `ParamsAndVars` by index in `ParamCountOrParamIndex`,
// the `ReturnTypeOrNullForParam` being null (@perf how to optimize it even more?)
public struct LambdaItem
{
public int ParamCountOrParamIndex;
public Type ReturnTypeOrNullForParam;
}
public struct ParamOrVarEntry
{
public Type ParamType;
public bool IsByRef;
// For now Name is a string, but it may be even a slice referencing to the external continuos text of all strings,
// including all Param and Var names.
public string NameMaybe;
}
public struct MethodLikeItem
{
public MethodBase Method; // ConstructorInfo, MethodInfo, etc.
// Instead of enum and tranlastion to the corresponing list Expr, it may be the offest inside Expr struct, or both the enum with the offset value
public ExprKind ParamKind;
public int ParamIndex;
}
// Can be constructed like this:
Expr expr = default;
expr.Lambda<Func<int, Foo>>(
expr.New(typeof(Foo).GetConstructors()[0],
Constant("hey, "),
expr.Parameter(typeof(int), "n", same: 0),
Constant("sistah", putIntoClosure: true)),
expr.Parameter(same: 0)
);
Lambda<int, Foo> lambda = expr.CompileFastToLambda();
var fooSis = lambda.Invoke(31);
lambda.Closure.SetConstant(0, "brotha");
var fooBro = lambda.Invoke(42);
// Where the Lambda might be
public struct Lambda<T1, R>
{
public Func<ArrayClosure, T1, R> Func;
public FriendlyClosure Closure;
public R Invoke<T1>(T1 t1) => Func(FriendlyClosure.ArrayClosure, t1);
}The benefits:
- Less memory consumed overall.
- Small expressions are fully on stack.
- Using the indexes to refer to the children expressions makes it transferrable and persistent over the network, benefits the De/Serialization making it almost no-op.
- Data is grouped and serialized by the
ExpressionTypesimplifies the processing of the specificExpressionType, e.g. collecting the Constants does not require the full expression traversal, nor the collecting of the nested Lambdas or the nested Blocks.
The cons:
- Not compatible with System Expression API and requires the conversion.
Update - 2025.04.22
- Expression forest as a single struct of "array"
- Use precise capacity for "arrays" for the expression with all known sub-expressions, number of constants, calls, etc.
ovska