Self-executing function changing dramatically the execution time
Hi,
the first thing that I want to say is congratulations for your excellent work on Jurassic!
I've got an interesting case that I don't know if it is the normal behavior, a bug or if I'm doing something wrong.
Take the following code as an example:
Line 0001: (function () {
Line 0002: var a = 0;
Line 0003:
Line 0004: a++;
Line 0005: a++;
.
.
.
Line 2002: a++;
Line 2003: a++;
Line 2004: })();
The above code runs in approximately 80ms but if I remove the self-executing function (lines 1 and 2004), it runs in something about 900ms.
If the "a++;" instruction is repeated 10k times instead of 2k, with the self-executing function it takes 120ms and without this function it gives "An unhandled exception of type 'System.StackOverflowException' occurred in Jurassic.dll"
Before running this Javascript code I do some other stuffs in the Jurassic engine but I don't think it's relevant. I can provide further details if necessary.
That's very strange. I can't take a look right now, sorry, I'm on holiday :-) But I'll take a look when I get back.
I tried it, but both the self-executing and non-self-executing function took about the same amount of time. Can you clarify exactly what code you were using when you experienced the slow execution and/or the stack overflow exception?
Hi @paulbartrum,
I can reproduce the StackOverflowException and the long run time just by creating a new ScriptEngine and executing the code from @rodrigoroma without the self-executing function expression, using Jurassic from the current master branch.
I have created a repro with commit https://github.com/kpreisser/jurassic/commit/49dda065691f54f63805abbbb5e5a20ef69b16dc. When I run this project on a 64-Bit machine (using VS 2015), I'm getting a StackOverflowException.
Thanks!
Hi @paulbartrum and @kpreisser,
I've also created a simple program to reproduce this issue (it's attached).
My environment is Visual Studio Community 2015 in Windows 10 Pro (64 bits version) using the latest version of Jurassic available in NuGet (I've just created a new Console Application and ran Install-Package Jurassic).
My guess is that the long time has something to do with global variables because if I put the declaration of "a" out of the self-executing function (that is, in the global scope), it also takes a long time.
Line 0001: var a = 0;
Line 0002:
Line 0003: (function () {
Line 0004: a++;
Line 0005: a++;
.
.
.
Line 2002: a++;
Line 2003: a++;
Line 2004: })();
Thanks!
Accessing global variables is quite a bit slower than accessing local variables. But I'm not sure that's the problem here. Separating out the compilation and the execution:
ScriptEngine engine = new ScriptEngine();
var compiledCode = engine.Compile(new StringScriptSource(generateCode(false)));
compiledCode.Execute();
~~99% of the elapsed time is in the compilation, not the execution. I'll keep looking.~~ EDIT: I got mixed up, the majority of the time is in reading or writing the global variables.
Ok, thanks again for your attention!
Oh no, I'm wrong, the elapsed time is in the execution.
------------------------------------
Without the self executing function
Compile time: 154, execution time: 1086 (b=2000)
Compile time: 54, execution time: 1071 (b=2000)
Compile time: 56, execution time: 1067 (b=2000)
Compile time: 62, execution time: 1065 (b=2000)
Compile time: 56, execution time: 1073 (b=2000)
------------------------------------
With the self executing function
Compile time: 27, execution time: 44 (b=2000)
Compile time: 24, execution time: 42 (b=2000)
Compile time: 15, execution time: 41 (b=2000)
Compile time: 11, execution time: 41 (b=2000)
Compile time: 13, execution time: 42 (b=2000)
That means it's likely to be a problem with the codegen for reading or writing a global variable (or both).
I made some small changes to the Jurassic code, I hope @paulbartrum understanding。
In class CompiledScript I use EvalMethodGenerator instead of GlobalMethodGenerator so that CompiledScript.Compile can be compiled with the return value of a function (global functions are written into (function (args) {body}), the compilation of these functions results in a filename as a key The static Dictionary <string,CompiledScript>, can then be used in a multithreaded environment, I use a ReaderWriterLock to deal with concurrency
Now very quickly, because it no longer needs to be repeated Parse