README.md
This tool compiles C# code by first rewriting the syntax trees of LINQ expressions using plain procedural code, minimizing allocations and dynamic dispatch.
Example input code
publicint Method1()
{var arr = new[] { 1, 2, 3, 4 };var q = 2;return arr.Where(x => x > q).Select(x => x + 3).Sum();
}
Allocations: input array, array enumerator, closure for q
, Where
delegate, Select
delegate, Where
enumerator, Select
enumerator.
Decompiled output code
publicint Method1()
{int[] arr = new[] { 1, 2, 3, 4 };int q = 2;returnthis.Method1_ProceduralLinq1(arr, q);
}privateint Method1_ProceduralLinq1(int[] _linqitems, int q)
{if (_linqitems == null) thrownew ArgumentNullException();int num = 0;for (int i = 0; i < _linqitems.Length; i++)
{if (_linqitems[i] > q)
num += num2 + 3;
}return num;
}
Allocations: input array.
Supported LINQ methods
Select
,Where
,Reverse
,Cast
,OfType
First
,FirstOrDefault
,Single
,SingleOrDefault
,Last
,LastOrDefault
ToList
,ToArray
,ToDictionary
Count
,LongCount
,Any
,All
ElementAt
,ElementAtOrDefault
Contains
,ForEach
Usage
- Add the following to your
project.json
:
"tools": {"dotnet-compile-csc-linq-rewrite": {"version": "1.0.1.9","imports": "portable-net45+win8+wp8+wpa81"
}
}
- In the
buildOptions
of yourproject.json
, specify the custom compiler:
"buildOptions": {"compilerName": "csc-linq-rewrite"
}
- Compile your project with
dotnet restore
anddotnet build
. - If you need to exclude a specific method, apply a
[NoLinqRewrite]
attribute to that method:
namespaceShaman.Runtime
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method)]publicclassNoLinqRewriteAttribute : Attribute
{
}
}
Development
Shaman.FastLinq
To further reduce allocations, install Shaman.FastLinq
or Shaman.FastLinq.Sources
. These packages include LINQ methods specific for T[]
and List<>
(not all method calls are optimized by LINQ rewrite, like individual, non-chained .First()
or .Last()
calls).
Comparison to LinqOptimizer
- Code is optimized at build time (as opposed to run time)
- Uses existing LINQ syntax, no need for
AsQueryExpr().Run()
- No allocations for Expression<> trees and enumerator boxing
- Parallel LINQ is not supported (i.e. left intact)
- No support for F#