Skip to content

Commit

Permalink
use concurrent dictionary to replace lock
Browse files Browse the repository at this point in the history
  • Loading branch information
WAcry committed Jul 6, 2024
1 parent 9788cc7 commit c285fa9
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 10 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ By default `LambdaParser` uses `ValueComparer` for values comparison. You can pr
var valComparer = new ValueComparer() { NullComparison = ValueComparer.NullComparisonMode.Sql };
var lambdaParser = new LambdaParser(valComparer);
```
### Caching Expressions

The `UseCache` property determines whether the `LambdaParser` should cache parsed expressions. By default, `UseCache` is set to `true`, meaning expressions are cached to improve performance for repeated evaluations of the same expression.

Therefore, using a singleton instance of `LambdaParser` is recommended, rather than creating a new instance each time.

You can disable caching by setting UseCache to false if you want to save memory, especially when evaluating a large number of unique expressions.

```csharp
var lambdaParser = new LambdaParser();
lambdaParser.UseCache = false;
```

## Who is using this?
NReco.LambdaParser is in production use at [SeekTable.com](https://www.seektable.com/) and [PivotData microservice](https://www.nrecosite.com/pivotdata_service.aspx) (used for user-defined calculated cube members: formulas, custom formatting).
Expand Down
16 changes: 6 additions & 10 deletions src/NReco.LambdaParser/Linq/LambdaParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#endregion

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
Expand All @@ -36,9 +37,8 @@ public class LambdaParser {
static readonly string[] mulOps = new[] {"*", "/", "%" };
static readonly string[] addOps = new[] { "+", "-" };
static readonly string[] eqOps = new[] { "==", "!=", "<", ">", "<=", ">=" };

readonly IDictionary<string, CompiledExpression> CachedExpressions = new Dictionary<string, CompiledExpression>();
readonly object _lock = new object();

readonly IDictionary<string, CompiledExpression> CachedExpressions = new ConcurrentDictionary<string, CompiledExpression>();

/// <summary>
/// Gets or sets whether LambdaParser should use the cache for parsed expressions.
Expand Down Expand Up @@ -124,9 +124,7 @@ public object Eval(string expr, IDictionary<string, object> vars) {
public object Eval(string expr, Func<string,object> getVarValue) {
CompiledExpression compiledExpr = null;
if (UseCache) {
lock (_lock) {
CachedExpressions.TryGetValue(expr, out compiledExpr);
}
CachedExpressions.TryGetValue(expr, out compiledExpr);
}

if (compiledExpr == null) {
Expand All @@ -136,11 +134,9 @@ public object Eval(string expr, Func<string,object> getVarValue) {
};
var lambdaExpr = Expression.Lambda(linqExpr, compiledExpr.Parameters);
compiledExpr.Lambda = lambdaExpr.Compile();

if (UseCache)
lock (_lock) {
CachedExpressions[expr] = compiledExpr;
}
CachedExpressions[expr] = compiledExpr;
}

var valuesList = new List<object>();
Expand Down

0 comments on commit c285fa9

Please sign in to comment.