Skip to content

Instantly share code, notes, and snippets.

@WhiteBlackGoose
Last active August 26, 2020 10:40
Show Gist options
  • Save WhiteBlackGoose/538d932cbeec7e7bfffd45eee007c951 to your computer and use it in GitHub Desktop.
Save WhiteBlackGoose/538d932cbeec7e7bfffd45eee007c951 to your computer and use it in GitHub Desktop.
Simple code for generating loops in linq expressions
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
public static class NestedLoopBuilder
{
public static Expression CreateLoop(ParameterExpression var, Expression until, Expression onIter)
{
var label = Expression.Label();
var loop = Expression.Loop(
Expression.Block(
Expression.IfThenElse(
Expression.LessThan(var, until),
Expression.Block(
onIter
),
Expression.Break(label)
),
Expression.PostIncrementAssign(var)
),
label);
return Expression.Block(
Expression.Assign(var, Expression.Constant(0)),
loop
);
}
public static Expression CompileNestedLoops(Expression[] shapes, Func<ParameterExpression[], Expression> onIter)
{
var acts = new List<Expression>();
var locals = new ParameterExpression[shapes.Length];
for (int i = 0; i < shapes.Length; i++)
locals[i] = Expression.Parameter(typeof(int), "x_" + i);
var localShapes = new ParameterExpression[shapes.Length];
for (int i = 0; i < shapes.Length; i++)
localShapes[i] = Expression.Parameter(typeof(int), "shape_" + i);
var localShapesAssigned = new Expression[shapes.Length];
for (int i = 0; i < shapes.Length; i++)
localShapesAssigned[i] = Expression.Assign(localShapes[i], shapes[i]);
Expression currExpr = onIter(locals);
for (int i = shapes.Length - 1; i >= 0; i--)
{
currExpr = CreateLoop(locals[i], localShapes[i], currExpr);
}
acts.AddRange(localShapesAssigned);
acts.Add(currExpr);
var localVariables = new List<ParameterExpression>();
localVariables.AddRange(locals);
localVariables.AddRange(localShapes);
return Expression.Block(localVariables, Expression.Block(acts));
}
public static Expression CompileNestedLoops(Expression[] shapes, Func<ParameterExpression[], Expression> onIter,
bool parallel, ParameterExpression[] localsToPass)
{
if (!parallel)
return CompileNestedLoops(shapes, onIter);
else
{
var x = Expression.Parameter(typeof(int), "outerLoopVar");
Func<ParameterExpression[], Expression> newOnIter = exprs =>
{
var arr = new ParameterExpression[exprs.Length + 1];
arr[0] = x;
for (int i = 0; i < exprs.Length; i++)
arr[i + 1] = exprs[i];
return onIter(arr);
};
var shape0 = shapes[0];
var others = new ArraySegment<Expression>(shapes, 1, shapes.Length - 1).ToArray();
var compiledLoops = CompileNestedLoops(others, newOnIter);
var newArgs = localsToPass.ToList().Append(x).ToArray();
var del = Expression.Lambda(compiledLoops, newArgs);
var actions = new List<Expression>();
var localDelCall = Expression.Invoke(del, newArgs);
var finalLambda = Expression.Lambda(
localDelCall,
x
);
var enu = typeof(Parallel)
.GetMethods()
.Where(mi => mi.Name == nameof(Parallel.For))
.Where(mi => mi.GetParameters().Length == 3)
.Where(mi => mi.GetParameters()[2].ParameterType == typeof(Action<int>)).ToArray();
if (enu.Count() != 1)
throw new InvalidEnumArgumentException();
var mi = enu.FirstOrDefault();
var call = Expression.Call(null, mi, Expression.Constant(0), shape0, finalLambda);
return call;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment