Open epsitec opened 7 years ago
@mattwar @tmat I'm also getting ~5 seconds while executing the expression for the first time and subsequent calls take milliseconds. We are having multiple scripts to execute, so we combined them now to reduce the execution time. But still, the first execution is not acceptable for us. Is there any way to reduce the first execution time?
@mattwar @tmat Every time we load a new script ( dynamic logic) the penalty is around 4-5s. Earlier I anticipated that this time is just a one time of assembly loading and linking. It seems like there is a penalty whenever you call CSharpScript.Create for even a small change in the script. definitely looking for some workaround or optimization or some help here
Seems to get better with .NET Core, I'm getting roughly ~1.8 sec there. Not good enough yet, but not as horrible as it used to be.
I am facing the samw issue, Whenever I am loading a new script, It is taking 4-5 seconds, Please let me know if anyone has got a workaround for this ? The following line takes 4 seconds on my machine:
var script2 = CSharpScript.Create<object>("System.Console.WriteLine(\"Something\");");
script2.Compile();
Any help will be appreciated
For cold startup in a net472 console app, that snippet takes 2155.4ms on my machine. Subsequent executions take 5.4ms.
@jnm2 I am not sure what is the issue, This is my new test console App with zero code, I am just testing the performance, Following is the installed API version information.
For cold startup in a net472 console app, that snippet takes 2155.4ms on my machine. Subsequent executions take 5.4ms.
2+ seconds is still not a fair result, considering the amount of code it is compiling. Please let me know if there is any way-out of this
I would like to know what the bottleneck is, too. Best thing would be to profile the console application running the snippet exactly once, and looking at that 2 seconds of data to see where the time is going. Maybe DLL loading from the file system or jitting, for instance?
I'm having two problems, too: 1) As reported by this issue, compile time is rather slow. On my X1 Carbon, it takes 3.5s on the first run and 160 on successive runs 2) High memory utilization as reported in #22219
While there are things that need improvement, are there any general guidelines to improve performance of using the scripting API?
Create method takes 10 seconds. Is this normal?? Didn't find any solution in this thread.
` hostScriptingAPI = new ScriptingAPI(this);
var loadedScript = "DebugMessage(\"debug\")";
var options = ScriptOptions.Default;
script = CSharpScript.Create(loadedScript, options, hostScriptingAPI.GetType());
script.RunAsync(hostScriptingAPI);`
I have the same slowness problem here. Following the tip of the user mattwar (extending from an empty standard script), I managed to greatly improve the performance, but it is still not satisfactory.
What am I doing:
I abandoned the practice of passing the object with data to be validated, in the "globals" parameter. This practice is definitely not feasible at the moment. Instead, I am simplifying the expression before sending it out for validation.
This simplification consists of using the values of the attributes of the object that I intend to validate, converted to primary types of C #, instead of sending the object itself in the "globals" parameter to the "CSharpScript.EvaluateAsync ()" method. I will certainly have a little more code, but this practice has dramatically improved performance here:
1 - Approach with low performance:
/*
sampleVM =
{
"Value1": 15,
"Value2": 40,
"ValueA": "ABC",
"ValueB": "DEF"
}
*/
private readonly string DynamicRule = "(Value1 > 10 && Value2 < 50 && ValueA == 'ABC' && ValueB == 'DEF')";
string expression = DynamicRule.Replace("\'", "\"");
var resultScript = CSharpScript.EvaluateAsync(expression, globals: sampleVM).Result;
2 - Approach with the expression simplifier:
In this approach, I am using the "ContinueWith ()" method (user tip "mattwar") from a previously instantiated expression, embedded in my controllers.
private readonly string DynamicRule = "(Value1 > 10 && Value2 < 50 && ValueA == 'ABC' && ValueB == 'DEF')";
string expression = SimplifyExpression(sampleVM);
var resultScript = _DynamicExpressions.CreateScript().ContinueWith(expression).RunAsync().GetResult().ReturnValue;
private string SimplifyExpression(SampleVM sampleVM)
{
string expression = DynamicRule.Replace("\'", "\"");
expression = expression.Replace("Value1", sampleVM.Value1.ToString());
expression = expression.Replace("Value2", sampleVM.Value2.ToString());
expression = expression.Replace("ValueA", "\"" + sampleVM.ValueA.ToString() + "\"");
expression = expression.Replace("ValueB", "\"" + sampleVM.ValueB.ToString() + "\"");
return expression;
}
Comparing approach number 2 with number 1, I had a performance gain of 265%.
Here is the demo project with all the implementation details I’m using.
Note: Although, in addition to performance in the first run is poor. In the approach I'm using, performance deteriorates with each new request. Hinting that something is being accumulated in the context of script execution.
If the need is "just" to validate dynamic expressions at run time, I strongly advise you to take a look at this library here, it worked like a glove for me. It does the same thing that I was doing with "CSharp.Scripting" a thousandth of the time.
If the need is "just" to validate dynamic expressions at run time, I strongly advise you to take a look at this library here, it worked like a glove for me. It does the same thing that I was doing with "CSharp.Scripting" a thousandth of the time.
I tried that NuGet package, and I can verify that the performance seems excellent (especially when you keep an instance of the context alive across calls). This is the performance I was expecting from Roslyn. It runs simple formula snippets in about 0.1ms per snippet, resulting in running the same expression 3000 times in ~350ms. It handles strings, conditional expressions and accepts function imports for e.g. Math and custom classes as well.
Maybe the Roslyn script engine can use some assumptions to improve performance, like running code snippets in the current application context using the current set of references. If more complicated actions are needed, you could define some kind of isolated environment before running scripts on it. For simple code snippets à la NCalc, Roslyn is still very slow.
please note this https://weblog.west-wind.com/posts/2022/Jun/07/Runtime-CSharp-Code-Compilation-Revisited-for-Roslyn
Roslyn also includes a Scripting API that is meant to simplify this process by letting you run just a snippet of code or an individual expression rather than having to first create a class manually.
Behind the scenes the code still produces a class and assembly, but with the Scripting API this is hidden so you just pass in a code snippet.
Although the scripting code is significantly simpler than the full compilation code, it's important to understand that behind the scenes the library still creates and compiles a C# type and produces an in-memory assembly.
By default scripts create new assemblies on every script run which can bloat a processes memory usage very quickly.
If the need is "just" to validate dynamic expressions at run time, I strongly advise you to take a look at this library here, it worked like a glove for me. It does the same thing that I was doing with "CSharp.Scripting" a thousandth of the time.
I also found DynamicExpresso helped me similarly.
I am experimenting with version
2.0.0-rc3
, running on Windows 7 x64 with .NET Framework 4.6.2.When running very simple scripts such as these two:
or
I consistently get execution times which lie between 70 and 100 milliseconds. I understand there is a cost to compile the script, however I cannot use
CSharpScript
with such execution times, as I need to evaluate hundreds of little code snippets.What is the recommended approach if I need to evaluate lots of small pieces of C# - I'd like to pay the startup time only once, and be able to then execute my snippets one after the other, getting back the results as fast as possible.