This project is read-only.

JSON parsing performance

Jun 20, 2013 at 10:52 PM
Hi James,

I want to use your library, sounds fantastic. I replied to your comment on my blog.

Could you send me the simplest possible complete block code that will parse JSON and return an object via your V8 library?

For instance, for javascriptdotnet the code would be as follows:

const string scriptTemplate =
"var json = '{0}';" +
"var parsedResult = JSON.parse(json);";
const string json = "{\"myProperty\":true,\"myNumber\":3}";

string script = string.Format(scriptTemplate, json);

var jsEngine = new JavascriptContext();
jsEngine.Run(script);

var result = jsEngine.GetParameter("parsedResult");

I'll do the rest!

Thanks,

Tim.
Jun 24, 2013 at 8:34 PM
Edited Jun 24, 2013 at 8:34 PM
Sorry for the delay, I never seem to get notifications of postings! :(

I'll get this to you asap! :)
Jun 24, 2013 at 8:40 PM
Edited Jun 24, 2013 at 11:04 PM
Try the following:
const string scriptTemplate =
 "var json = '{0}';" +
 "var parsedResult = JSON.parse(json);";

const string json = "{\"myProperty\":true,\"myNumber\":3}"; 

string script = string.Format(scriptTemplate, json); 

var jsEngine = new V8Engine(); 
jsEngine.WithIsolateScope = ()=>
{
    /*var result =*/ jsEngine.Execute(script);  // OR jsEngine.ConsoleExecute(script);
   // (Note: "Execute()" returns the result of the executed script, but not when "var" is used in the JavaScript. You can skip the line following this comment if you place "parsedResult;" as the last line in the script)
    using(var result = (InternalHandle)jsEngine.DynamicGlobalObject.parsedResult)
    {
    // (... do something with "result" here [result is a handle, which you can try to cast to a string, or read the string value, if any] ...)
    // Note: You can also use "jsEngine.GlobalObject.GetProperty()" which is more direct and thus much faster.
    } // ("using" disposes of the handle, or call result.Dispose() when no longer needed)
};
Jun 24, 2013 at 11:16 PM
Edited Jun 24, 2013 at 11:19 PM
I just realized that "parsedResult" is an object (I was thinking a string for some reason). Anyhow, you do not NEED a managed object instance to to reference it (a simple handle will do) but you will need to wrap the handle with a managed object in order to operate on it like so:
var resultingObject = jsEngine.GetObject(result);
var someProp = ((DynamicObject)resultingObject).someProp;
You can then use the methods on "resultingObject" to access properties on the native object as well.

This actually makes me think of something ... since the managed object is simply a wrapper, I may consider caching those and reusing them to reduce GC interference, or modify the object wrappers to allow setting them with new handles without needing to create objects all the time.
Jun 25, 2013 at 11:07 AM
Edited Jun 25, 2013 at 11:13 AM
Thanks, that's very useful!

I'm still trying to get my app working because new V8Engine() throws an exception: assembly not found for V8.Net.Proxy.Interface Version=1.0.0.0 -- despite this DLL being added as a reference and all DLLs being in the bin folder.

Interesting point about the managed object wrapper which doesn't change -- sounds like caching could be an improvement!
Jun 25, 2013 at 12:28 PM
Edited Jun 25, 2013 at 2:58 PM
Don't add that one - it needs to be loaded by v8.net.dll automatically. You only need to reference v8.net.dll only (see docs) and possibly v8.net.sharedtypes.dll as well. -jw (sent via iPhone)
Jun 25, 2013 at 1:08 PM
If I create a new Console application in VS2010 with .NET 4.0, add references to V8.Net.dll and the shared library, new JSEngine() throws that exception -- it's unable to find the assembly. I tried putting all of the binaries in the same folder and they're all in the bin folder.
Jun 25, 2013 at 3:04 PM
I think I compiled the release with .NET 4.5 (it was just the default for VS2012). I only need 4.0 for this (for DynamicObject), so I can make a new release to support 4.0. I think I can do this today at some point - I have a new release with some minor bug fixes.
Jun 25, 2013 at 3:14 PM
Ah yes please, thanks! I'll try it as soon as the DLLs are available.



Jun 25, 2013 at 3:29 PM
You'll know this is the issue if the console exe runs fine, but v8.net.dll doesn't load in your own project. ;)
Jun 26, 2013 at 6:03 AM
Ok, DLLs are posted. :)
Jun 26, 2013 at 10:08 AM
Thanks! I still have the same problem.

I downloaded the binaries and ran your own console exe, and that threw the same exception -- after this message:

=> Message: An attempt was made to load an assembly from a network location...
Jun 26, 2013 at 1:45 PM
How is your system setup? (Windows version, VS2010, etc...?)

-jw
(sent via iPhone)

Jun 26, 2013 at 1:49 PM
Thanks for your help.

It's a fairly typical setup:

- Windows 7 Enterprise SP1 64-bit
- VS2010 (which shouldn't affect your console exe)



Jun 26, 2013 at 2:58 PM
Did you unblock the zip file before extracting the files? I can reproduce the error if I don't. Just right click the zip file and select "Properties", then click on "unblock".

That said, I see an index error now for some reason in the console when loaded on a different machine, but the DLLs may load fine.
Jun 26, 2013 at 3:26 PM
Thanks -- that does seem to be part of the problem.

Now I just get this DllNotFound exception on new JSEngine():

Unable to load DLL 'V8_Net_Proxy_x86': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

This happens whether or not all your DLLs are in the bin folder of my console app, and whether or not I add a project reference to the proxy DLL. I also tried playing with the project platform target to see if x86 vs Any CPU made any difference.



Jun 26, 2013 at 3:37 PM
Odd... Can you zip your project and send it so I can take a closer look?

Jun 26, 2013 at 3:38 PM
Done -- I sent it to the email address you gave when you first commented on my website.


Jun 26, 2013 at 3:42 PM
Ok thanks, I'll take a look.

Thanks also for helping to iron out these issues. :)
Jun 26, 2013 at 3:43 PM
It's me who owes you thanks -- I appreciate the collaboration. :)


Jun 26, 2013 at 4:30 PM
Edited Jun 26, 2013 at 4:44 PM
You don't have all the required DLLs in the bin\debug folder. All DLLs need to be copied there. I have a section in the documentation on the files required.

I got passed your error by including all DLLs - but there seems to be an index error when running on 32-bit systems (works fine on my laptop).
Jun 26, 2013 at 4:44 PM
I had all DLLs (in fact the whole contents of your Zip file) in the bin folder for both the console app and the project that referenced it. I only deleted them from the Zip file I sent you to save space. Having all the DLLs there does not solve the problem for me.


Jun 26, 2013 at 4:53 PM
I would usually see this exception only when the DLL does not match the build platform type -- in my case x86 for both projects.



Jun 26, 2013 at 5:19 PM
Edited Jun 26, 2013 at 5:21 PM
Oh, and I also removed the reference to "V8.Net.Proxy.Interface.x86.dll",try that as well.

Make sure to unblock the zip and re-copy all the DLLs, then only add a reference to v8.net.dll and v8.net.sharedtypes.dll only.
Jun 26, 2013 at 5:23 PM
The build platform doesn't matter - v8.net.dll will detect and load the proper DLLs.
Jun 26, 2013 at 5:28 PM
Same issue:

Could not load file or assembly 'V8.Net.Proxy.Interface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.



Jun 26, 2013 at 5:28 PM
Edited Jun 26, 2013 at 5:28 PM
timacheson wrote:
Unable to load DLL 'V8_Net_Proxy_x86': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
That usually means "v8-ia32.dll" could not be found (the "module" used by "'V8_Net_Proxy_x86.dll') - or loaded if blocked by windows (check the file properties to make sure it's not blocked).
Jun 26, 2013 at 5:29 PM
Correction, this is the issue, as before:

Unable to load DLL 'V8_Net_Proxy_x86': The specified module could not be found. (Exception from HRESULT: 0x8007007E)



Jun 26, 2013 at 5:31 PM
I get the same error message when running your console.exe (it says x64 though of course).



Jun 26, 2013 at 5:37 PM
Ok, let's confirm the files on the directory (your project loads the DLLs fine on my laptop).

In the folder "bin\debug" I have these:

JsonParserBenchmarks.exe
TestFixtures.dll
V8.Net.dll
V8.Net.Proxy.Interface.x64.dll
V8.Net.Proxy.Interface.x86.dll
V8.Net.SharedTypes.dll
V8_Net_Proxy_x64.dll
V8_Net_Proxy_x86.dll
v8-ia32.dll
v8-x64.dll
  1. Right click each of the V8*.* DLLs and make sure none of them are blocked.
  2. Try running "JsonParserBenchmarks.exe" manually by double clicking it within this folder.
Jun 26, 2013 at 6:29 PM
The only way I can reproduce the error is by hidding/renaming the "v8-ia32.dll" file (or restricting access to it).
Jun 26, 2013 at 6:44 PM
We can investigate this with your own application if that helps, because I get exactly the same error with your console as I do with mine.

In my console app app all of the DLLs are in bin/debug and none are blocked. Any other console app can access DLLs in that folder. My user account has Administrator access level.

The problems is obviously when your library tries to load that DLL and cannot access it. I'm wondering whether it's running under an unexpected user account for some reason. I'll try some experiments with permissions.

Have you tried it on different PC?


Jun 26, 2013 at 8:35 PM
Edited Jun 26, 2013 at 8:46 PM
Hi, yes, the console runs find on my Windows 7 laptop (64 bit - I always test this before releasing anything), and also loads the DLL fine on my other 32-bit windows 7 laptop (though an index error shows in the console - probably a marshaling bug on 32-bit systems). In both cases the DLL all loaded, except if I renamed or restricted access to one of the DLLs.

If the console exe in the V8.NET library is also experiencing the exact same issues with no blocked DLLs, that's really odd. I can't reproduce the error any other way, but I'll try to test on another system as soon as I can.
Jun 26, 2013 at 8:38 PM
Edited Jun 26, 2013 at 8:47 PM
I do have one thought: The "IntPtr.Size" is checked to determine the correct "interface" DLL to load (which is linked to a proxy DLL). If some how it detected the wrong DLL for the platform, it might cause a complication, but I'm not sure how that scenario would be possible.
Jun 27, 2013 at 6:22 PM
Ok, I think have have the solution. Apparently the MSVC++ libraries are required (http://www.microsoft.com/en-au/download/details.aspx?id=30679). I'm going to see if I can either include the required DLLs, or see if there's a way to compile the C++ code without that requirement.
Jun 27, 2013 at 8:52 PM
Sounds promising. Let me know what you come you with. I'll try some experiments also.



Jun 30, 2013 at 9:06 PM
Edited Jun 30, 2013 at 9:06 PM
Please try testing with this most recent compile of the libraries. The arrangement of these libraries is completely new, and seems to work better.
https://dl.dropboxusercontent.com/u/17275929/Temp/V8.Net%20%28Testing%20Only%29.zip

*** Make sure to right click and unblock the ZIP file before extracting :) ***
Jul 1, 2013 at 8:07 AM
Ok, the new release should fix all these issues. :)
Jul 1, 2013 at 11:02 AM
Thanks -- I now get this error at new V8Engine.Execute(script):

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."


Jul 1, 2013 at 7:24 PM
Did you execute it in a context? (i.e. "V8Engine.WithContextScope")

If so, send me your current source and I'll look into it right away.

-jw
(sent via iPhone)

Jul 8, 2013 at 10:09 AM
Edited Jul 8, 2013 at 10:16 AM
Thanks -- context scope worked, I was using isolated scope.

But, I still can't get to the "var obj" that was declared and assigned in the executed JavaScript. I sense we're almost there, this must be the last obstacle!

The parsed result is not what I expected, or at least I cannot cast it to the type I want or access the properties I want.

The following code throws an exception because the property "count" cannot be found.
jsEngine.WithContextScope = () => {
 jsEngine.Execute(javaScriptShortJSON);
 result = jsEngine.DynamicGlobalObject.obj;
 var count (int)result.count
 };
Jul 8, 2013 at 12:13 PM
Hi Tim,

"obj" is still a handle. V8.NET works always with handles to speed things up. It never a involves object wrappers unless you wanted them, so you will need to do this:

var obj = jsEngine.GetObject(result);
var count (int)obj.count
Hope that helps! :)
-jw
(sent via iPhone)

Jul 8, 2013 at 12:48 PM
Edited Jul 8, 2013 at 12:51 PM
Thanks.
This code:
var resultHandle = jsEngine.DynamicGlobalObject.obj;
var result = jsEngine.GetObject(resultHandle);
Throws a RuntimeBinderException:

"The best overloaded method match for 'V8.Net.V8Engine.GetObject(V8.Net.InternalHandle, bool, bool)' has some invalid arguments"
Jul 8, 2013 at 1:12 PM
That's odd - is it in a context scope? Also, can you post the whole method code? Thanks.

Jul 8, 2013 at 1:23 PM
Edited Jul 8, 2013 at 1:24 PM
const string ShortJSON = "{\"result\":true,\"count\":3}";

const string JavaScriptTemplate = 
    "var json = '{0}';" +
    "var obj = JSON.parse(json);";

var javaScriptShortJSON = string.Format(JavaScriptTemplate, ShortJSON);

var jsEngine = new V8Engine();

jsEngine.WithContextScope = () =>
    {
        jsEngine .Execute(javaScriptShortJSON);
        var resultHandle = jsEngine.DynamicGlobalObject.obj;
        result = jsEngine .GetObject(resultHandle);
    };
Jul 8, 2013 at 3:21 PM
Edited Jul 8, 2013 at 3:27 PM
Congratulations! You found a bug. Too bad there are no prizes to award, except the awesome feeling of being a part of something great. LOL. ;)

Ok, apparently "DynamicGlobalObject.obj;" is returning a string and not a handle (this was proper behavior before some things changed a few versions ago). I'll have to make sure to add it to my testing routines. Try this to see what I mean:
InternalHandle resultHandle = jsEngine.DynamicGlobalObject.obj;
That will produce an invalid cast exception. For now the work around is simply to call the GetProperty() method:
var resultHandle = jsEngine.GlobalObject.GetProperty("obj");
I'll make a new release with this fixed asap - I also plan to implement better streamlined access to properties and nested objects as well.

I guess using dynamic properties is "infectious" in that it causes var to be dynamic, and thus also causes GetObject() to become dynamic also! Very odd, but makes sense. ;)

Thanks. 8)
Jul 8, 2013 at 3:52 PM
Thanks -- I'll try the suggested workaround for now, and look forward to trying the new version when it's ready! :)
Jul 8, 2013 at 4:04 PM
FYI I tried the work around but had the same issue for the object returned by GetProperty(). I'll try again with the next version.


Jul 8, 2013 at 4:34 PM
That's odd, I tested it and it works on my end. Ok, I'll let you know once the new version is ready.
Aug 26, 2013 at 7:47 AM
Edited Aug 26, 2013 at 7:49 AM
Sorry, forgot to mention it was fixed in the previous release, but now there's another release, and this one supports binding script to CLR types! :) Includes a tone of bug fixes and more optimizations as well.