From 5dfc09c394c42d2e575ba97f9d8d5fb1f9877ef8 Mon Sep 17 00:00:00 2001 From: mrxrsd Date: Sat, 21 Aug 2021 09:23:33 -0300 Subject: [PATCH] (aviita) Fix performance issue with CaseSensitivity enabled #76 --- Jace.Benchmark/BenchMarkOperation.cs | 3 ++- Jace.Benchmark/Program.cs | 23 ++++++++++++++------- Jace.Tests/DecimalCalculationEngineTests.cs | 16 ++++++++++++++ Jace.Tests/DoubleCalculationEngineTests.cs | 16 ++++++++++++++ Jace/GenericCalculationEngine.cs | 7 ++++--- 5 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Jace.Benchmark/BenchMarkOperation.cs b/Jace.Benchmark/BenchMarkOperation.cs index 7d7e50e..021035b 100644 --- a/Jace.Benchmark/BenchMarkOperation.cs +++ b/Jace.Benchmark/BenchMarkOperation.cs @@ -10,6 +10,7 @@ public class BenchMarkOperation { public string Formula { get; set; } public BenchmarkMode Mode { get; set; } - public Func, string, TimeSpan> BenchMarkDelegate { get; set; } + public Func, string, Dictionary, TimeSpan> BenchMarkDelegate { get; set; } + public Dictionary VariableDict { get; set; } } } diff --git a/Jace.Benchmark/Program.cs b/Jace.Benchmark/Program.cs index d6ee91e..474188d 100644 --- a/Jace.Benchmark/Program.cs +++ b/Jace.Benchmark/Program.cs @@ -71,6 +71,8 @@ private static DataTable Benchmark(BenchmarkMode mode, CaseSensitivity caseSensi BenchMarkOperation[] benchmarks = { new BenchMarkOperation() { Formula = "2+3*7", Mode = BenchmarkMode.Static, BenchMarkDelegate = BenchMarkCalculationEngine }, + new BenchMarkOperation() { Formula = "something2 - (var1 + var2 * 3)/(2+3)", Mode = BenchmarkMode.Simple, BenchMarkDelegate = BenchMarkCalculationEngine, + VariableDict = new Dictionary(){{"var1", 4.5642}, {"var2", 845.4235}, { "something2", 25038.66 } } }, new BenchMarkOperation() { Formula = "logn(var1, (2+3) * 500)", Mode = BenchmarkMode.SimpleFunction , BenchMarkDelegate = BenchMarkCalculationEngineFunctionBuild }, new BenchMarkOperation() { Formula = "(var1 + var2 * 3)/(2+3) - something", Mode = BenchmarkMode.Simple , BenchMarkDelegate = BenchMarkCalculationEngineFunctionBuild }, }; @@ -89,25 +91,25 @@ private static DataTable Benchmark(BenchmarkMode mode, CaseSensitivity caseSensi { if (caseSensitivity == CaseSensitivity.All || caseSensitivity == CaseSensitivity.CaseInSensitive) { - duration = benchmark.BenchMarkDelegate(interpretedEngine, benchmark.Formula); + duration = benchmark.BenchMarkDelegate(interpretedEngine, benchmark.Formula, benchmark.VariableDict); table.AddBenchmarkRecord("Interpreted", false, benchmark.Formula, null, NumberOfTests, duration); } if (caseSensitivity == CaseSensitivity.All || caseSensitivity == CaseSensitivity.CaseSensitive) { - duration = benchmark.BenchMarkDelegate(interpretedEngineCaseSensitive, benchmark.Formula); + duration = benchmark.BenchMarkDelegate(interpretedEngineCaseSensitive, benchmark.Formula, benchmark.VariableDict); table.AddBenchmarkRecord("Interpreted", true, benchmark.Formula, null, NumberOfTests, duration); } if (caseSensitivity == CaseSensitivity.All || caseSensitivity == CaseSensitivity.CaseInSensitive) { - duration = benchmark.BenchMarkDelegate(compiledEngine, benchmark.Formula); + duration = benchmark.BenchMarkDelegate(compiledEngine, benchmark.Formula, benchmark.VariableDict); table.AddBenchmarkRecord("Compiled", false, benchmark.Formula, null, NumberOfTests, duration); } if (caseSensitivity == CaseSensitivity.All || caseSensitivity == CaseSensitivity.CaseSensitive) { - duration = benchmark.BenchMarkDelegate(compiledEngineCaseSensitive, benchmark.Formula); + duration = benchmark.BenchMarkDelegate(compiledEngineCaseSensitive, benchmark.Formula, benchmark.VariableDict); table.AddBenchmarkRecord("Compiled", true, benchmark.Formula, null, NumberOfTests, duration); } } @@ -153,13 +155,20 @@ private static DataTable Benchmark(BenchmarkMode mode, CaseSensitivity caseSensi return table; } - private static TimeSpan BenchMarkCalculationEngine(ICalculationEngine engine, string functionText) + private static TimeSpan BenchMarkCalculationEngine(ICalculationEngine engine, string functionText, Dictionary variableDict) { DateTime start = DateTime.Now; for (int i = 0; i < NumberOfTests; i++) { - engine.Calculate(functionText); + if (variableDict == null) + { + engine.Calculate(functionText); + } + else + { + engine.Calculate(functionText, new Dictionary(variableDict)); + } } DateTime end = DateTime.Now; @@ -167,7 +176,7 @@ private static TimeSpan BenchMarkCalculationEngine(ICalculationEngine en return end - start; } - private static TimeSpan BenchMarkCalculationEngineFunctionBuild(ICalculationEngine engine, string functionText) + private static TimeSpan BenchMarkCalculationEngineFunctionBuild(ICalculationEngine engine, string functionText, Dictionary variableDict) { DateTime start = DateTime.Now; diff --git a/Jace.Tests/DecimalCalculationEngineTests.cs b/Jace.Tests/DecimalCalculationEngineTests.cs index 24854b8..9e02321 100644 --- a/Jace.Tests/DecimalCalculationEngineTests.cs +++ b/Jace.Tests/DecimalCalculationEngineTests.cs @@ -120,6 +120,22 @@ public void TestCalculateFormulaWithCaseSensitiveVariables1Compiled() Assert.AreEqual(8.5m, result); } + [TestMethod] + public void TestCalculateFormulaWithCaseSensitiveThrows() + { + Dictionary variables = new Dictionary(); + variables.Add("var1", 1); + variables.Add("var2", 1); + + var engine = CalculationEngine.New(new JaceOptions + { + CaseSensitive = true + }); + + var ex = AssertExtensions.ThrowsException(() => engine.Calculate("VaR1*vAr2", variables)); + Assert.AreEqual("The variable \"VaR1\" used is not defined.", ex.Message); + } + [TestMethod] public void TestCalculateFormulaWithCaseSensitiveVariables1Interpreted() { diff --git a/Jace.Tests/DoubleCalculationEngineTests.cs b/Jace.Tests/DoubleCalculationEngineTests.cs index 23ce464..18096d3 100644 --- a/Jace.Tests/DoubleCalculationEngineTests.cs +++ b/Jace.Tests/DoubleCalculationEngineTests.cs @@ -120,6 +120,22 @@ public void TestCalculateFormulaWithCaseSensitiveVariables1Compiled() Assert.AreEqual(8.5, result); } + [TestMethod] + public void TestCalculateFormulaWithCaseSensitiveThrows() + { + Dictionary variables = new Dictionary(); + variables.Add("var1", 1); + variables.Add("var2", 1); + + var engine = CalculationEngine.New(new JaceOptions + { + CaseSensitive = true + }); + + var ex = AssertExtensions.ThrowsException(() => engine.Calculate("VaR1*vAr2", variables)); + Assert.AreEqual("The variable \"VaR1\" used is not defined.", ex.Message); + } + [TestMethod] public void TestCalculateFormulaWithCaseSensitiveVariables1Interpreted() { diff --git a/Jace/GenericCalculationEngine.cs b/Jace/GenericCalculationEngine.cs index f9022bb..0a24afa 100644 --- a/Jace/GenericCalculationEngine.cs +++ b/Jace/GenericCalculationEngine.cs @@ -35,13 +35,14 @@ public abstract class GenericCalculationEngine : IInternalCalculationEngineThe to configure the behaviour of the engine. internal GenericCalculationEngine(JaceOptions options) { + this.caseSensitive = options.CaseSensitive; this.executionFormulaCache = new MemoryCache, T>>(options.CacheMaximumSize, options.CacheReductionSize); - this.FunctionRegistry = new FunctionRegistry(false); - this.ConstantRegistry = new ConstantRegistry(false); + this.FunctionRegistry = new FunctionRegistry(caseSensitive); + this.ConstantRegistry = new ConstantRegistry(caseSensitive); this.cultureInfo = options.CultureInfo; this.cacheEnabled = options.CacheEnabled; this.optimizerEnabled = options.OptimizerEnabled; - this.caseSensitive = options.CaseSensitive; + this.random = new Random();