Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ stylecop.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
BenchmarkDotNet.Artifacts/

# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
Expand Down
39 changes: 39 additions & 0 deletions DynamicExpresso.sln
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,63 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{4088369E-A
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicExpresso.Core", "src\DynamicExpresso.Core\DynamicExpresso.Core.csproj", "{C6B7C0D2-B84A-4307-9C61-D95613DB564D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmark", "benchmark", "{09EED85C-BE3C-7566-DC0E-2E8E43466740}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicExpresso.Benchmarks", "benchmark\DynamicExpresso.Benchmarks\DynamicExpresso.Benchmarks.csproj", "{394496C4-B878-4F0C-8471-2417F3205FC8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Debug|x64.ActiveCfg = Debug|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Debug|x64.Build.0 = Debug|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Debug|x86.ActiveCfg = Debug|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Debug|x86.Build.0 = Debug|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Release|Any CPU.Build.0 = Release|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Release|x64.ActiveCfg = Release|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Release|x64.Build.0 = Release|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Release|x86.ActiveCfg = Release|Any CPU
{33157A92-C6B2-4A51-8262-1FEBFD6558BE}.Release|x86.Build.0 = Release|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Debug|x64.ActiveCfg = Debug|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Debug|x64.Build.0 = Debug|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Debug|x86.ActiveCfg = Debug|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Debug|x86.Build.0 = Debug|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Release|Any CPU.Build.0 = Release|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Release|x64.ActiveCfg = Release|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Release|x64.Build.0 = Release|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Release|x86.ActiveCfg = Release|Any CPU
{C6B7C0D2-B84A-4307-9C61-D95613DB564D}.Release|x86.Build.0 = Release|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Debug|x64.ActiveCfg = Debug|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Debug|x64.Build.0 = Debug|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Debug|x86.ActiveCfg = Debug|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Debug|x86.Build.0 = Debug|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Release|Any CPU.Build.0 = Release|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Release|x64.ActiveCfg = Release|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Release|x64.Build.0 = Release|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Release|x86.ActiveCfg = Release|Any CPU
{394496C4-B878-4F0C-8471-2417F3205FC8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{394496C4-B878-4F0C-8471-2417F3205FC8} = {09EED85C-BE3C-7566-DC0E-2E8E43466740}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A36C3463-448E-4051-AE87-A2994E36C1EC}
EndGlobalSection
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ or run unit tests for a specific project with a specific framework:

Add `--logger:trx` to generate test results for VSTS.

## Benchmarks

This repository includes a BenchmarkDotNet project under `benchmark/DynamicExpresso.Benchmarks` to measure interpreter hot-paths.

dotnet run -c Release --project benchmark/DynamicExpresso.Benchmarks

## Release notes

See [releases page](https://github.com/dynamicexpresso/DynamicExpresso/releases).
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<ProjectReference Include="..\..\src\DynamicExpresso.Core\DynamicExpresso.Core.csproj" />
</ItemGroup>
</Project>
79 changes: 79 additions & 0 deletions benchmark/DynamicExpresso.Benchmarks/LambdaBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using BenchmarkDotNet.Attributes;

namespace DynamicExpresso.Benchmarks;

[MemoryDiagnoser]
public class LambdaBenchmarks
{
private Interpreter _interpreter = null!;

private Lambda _lambda = null!;

private object[] _args = null!;
private Parameter[] _declared = null!;
private Parameter[] _parameterValues = null!;

private const int A = 1;
private const int B = 2;
private const int C = 3;
private const int D = 4;
private const int E = 5;
private const int F = 6;

[GlobalSetup]
public void Setup()
{
_interpreter = new Interpreter();

_declared = new[]
{
new Parameter("a", typeof(int)),
new Parameter("b", typeof(int)),
new Parameter("c", typeof(int)),
new Parameter("d", typeof(int)),
new Parameter("e", typeof(int)),
new Parameter("f", typeof(int))
};

_lambda = _interpreter.Parse(
"((a + b) * c - (double)d / (e + f + 1)) + Math.Max(a, b)",
typeof(double),
_declared);

_args = new object[] { A, B, C, D, E, F };

_parameterValues = new[]
{
new Parameter("a", typeof(int), A),
new Parameter("b", typeof(int), B),
new Parameter("c", typeof(int), C),
new Parameter("d", typeof(int), D),
new Parameter("e", typeof(int), E),
new Parameter("f", typeof(int), F)
};
}

[Benchmark(Description = "Invoke cached lambda (object[])")]
public double Invoke_ObjectArray()
{
double sum = 0;
for (var i = 0; i < 100_000; i++)
{
sum += (double)_lambda.Invoke(_args);
}

return sum;
}

[Benchmark(Description = "Invoke cached lambda (IEnumerable<Parameter>)")]
public double Invoke_ParametersEnumerable()
{
double sum = 0;
for (var i = 0; i < 100_000; i++)
{
sum += (double)_lambda.Invoke(_parameterValues);
}

return sum;
}
}
3 changes: 3 additions & 0 deletions benchmark/DynamicExpresso.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using BenchmarkDotNet.Running;

BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
8 changes: 5 additions & 3 deletions src/DynamicExpresso.Core/Interpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,9 @@ public T Eval<T>(string expressionText, params Parameter[] parameters)
/// <returns></returns>
public object Eval(string expressionText, Type expressionType, params Parameter[] parameters)
{
return Parse(expressionText, expressionType, parameters).Invoke(parameters);
// Eval is intended for one-off expressions: prefer interpretation to avoid IL generation cost.
var lambda = ParseAsLambda(expressionText, expressionType, parameters, preferInterpretation: true);
return lambda.Invoke(parameters);
}

#endregion
Expand All @@ -548,7 +550,7 @@ public IdentifiersInfo DetectIdentifiers(string expression, DetectorOptions opti

#region Private methods

private Lambda ParseAsLambda(string expressionText, Type expressionType, Parameter[] parameters)
private Lambda ParseAsLambda(string expressionText, Type expressionType, Parameter[] parameters, bool preferInterpretation = false)
{
var arguments = new ParserArguments(
expressionText,
Expand All @@ -561,7 +563,7 @@ private Lambda ParseAsLambda(string expressionText, Type expressionType, Paramet
foreach (var visitor in Visitors)
expression = visitor.Visit(expression);

var lambda = new Lambda(expression, arguments);
var lambda = new Lambda(expression, arguments, preferInterpretation);

#if TEST_DetectIdentifiers
AssertDetectIdentifiers(lambda);
Expand Down
Loading
Loading