sloisel / numeric

Numerical analysis in Javascript
http://www.numericjs.com/
Other
1.42k stars 177 forks source link

nodejs support #1

Closed ssbanerje closed 12 years ago

ssbanerje commented 12 years ago

Hello, Is there any support for node.js in the current release. If not is it in the project roadmap?

Cheers, Subho.

sloisel commented 12 years ago

Dear Subho,

Thanks for your question. I just tried it in node. You are correct, it does not currently work in node. If I try numeric.clone([1,2,3]), it complains about how there's no object numeric.

From my previous (limited) experience with node, I suspect that the problem is as follows. Node uses v8 contexts a lot. Each module is loaded into its own javascript context. See http://stackoverflow.com/questions/5875193/node-javascript-contexts-sharing-built-in-prototypes for some examples of this.

As a result, the dynamically generated function numeric.clone (as well as many other dynamically generated functions) are having a hard time locating the numeric functions.

Not quite sure how to fix this yet. If you have a patch, I'll accept it.

Thanks,

Sébastien Loisel

sloisel commented 12 years ago

I have confirmed this is a known feature of node.js. On the irc channel for node, user insin related a similar problem in their project. The solution they found is:

<insin> ZedPepsi: I ended up defining everything I needed from the containing scope as an argument and passing it in. Not sure if there are better workarounds

That particular solution sounds like a major rewrite. Not sure where to go from here.

Edit: for posterity, here's the problem: http://pastebin.com/XJF3EiG2

afri commented 12 years ago

There is a relatively easy hack, but it's not elegant or pretty. The idea would be to reflect the "numeric" object into the global scope, say as variable "__numeric", so that it's unlikely to clash with anything. To get to the global scope independent of whether you're running in the browser or on nodejs, you can use code like this:

try { // attempt to access global scope via 'window' (browser) if (!window.numeric) window.numeric = numeric; // now see if we can reach numeric; i.e. if window really is the global scope: try { numeric } catch (e) { delete window.numeric; throw "nope"; } } catch (e) { try { // attempt to access global scope via 'global' (nodejs) if (!global.numeric) global.numeric = numeric; // see if we can reach it: try { numeric } catch (e) { delete global.__numeric; throw "nope"; } } catch (e) { throw new Error("numeric: Don't know how to access global scope"); } }

(This code also tries to catch various cases where - say - someone has put a global variable 'window' into nodejs's global scope.)

Then you just replace all references to "numeric" in numeric.js with "__numeric", and everything will work fine under both nodejs or the browser (and it will also fix browser file loaders with which numeric doesn't work atm).

A cleaner alternative would be to use eval() instead of Function() and wrap a closure around your functions in that way, but it is slightly larger surgery...

sloisel commented 12 years ago

Dear Alexander,

Sorry for the slight delay in answering, I'm trying to catch up on my email from last week and it just keeps getting worse.

I tried your tip (setting global.numeric = numeric) and that seems to fix the immediate problem. I haven't run the full unit test suite in node -- I imagine that'll unearth yet more problems but at least this one problem will be fixed. For what it's worth, this is at the node.js prompt:

numeric.add([1,2],[3,4]) [ 4, 6 ]

So that at least works.

sloisel commented 12 years ago

A quick note for posterity: creating functions using eval() was worse than Function() for me because the function created by eval() is deeply nested and this seemed to have some substantial performance impact. The reason why I use Function() is because it creates a function that is outside all the scopes and as a result does not incur any of the scoping performance costs.

afri commented 12 years ago

Whether eval has a performance impact depends on how exactly you use it to define functions. Firstly, a (somewhat) little known fact is that there are actually two different versions of eval. If you call it like this:

eval(.)

then it will indeed capture the current closure, which can lead to a performance impact. The other form is:

window.eval(.) (or global.eval(.) on nodejs)

This will create a function that doesn't capture the closure (i.e. it can be used similarly to "Function(.)").

I made a small testcase here: http://alex.onilabs.com/evaltest.html

As it turns out, on my chrome (mac os), "window.eval(.)" slightly edges out "Function(.)", whereas "eval(.)" is somewhat slower. In Firefox, "window.eval(.)" is significantly faster than "Function(.)".

afri commented 12 years ago

Another subtlety I forgot to mention: On older versions of IE (don't know up to which version; maybe even in recent versions), window.eval() erroneously does capture the closure. The workaround on these systems is to use "window.execScript(.)", which has slightly different problems (it can't return a value) - nothing that can't be worked around though. If you want to go down this route, I'm happy to help.

On Tue, Mar 20, 2012 at 3:03 PM, Alexander Fritze alex@onilabs.com wrote:

Whether eval has a performance impact depends on how exactly you use it to define functions. Firstly, a (somewhat) little known fact is that there are actually two different versions of eval. If you call it like this:

eval(.)

then it will indeed capture the current closure, which can lead to a performance impact. The other form is:

window.eval(.)    (or global.eval(.) on nodejs)

This will create a function that doesn't capture the closure (i.e. it can be used similarly to "Function(.)").

I made a small testcase here: http://alex.onilabs.com/evaltest.html

As it turns out, on my chrome (mac os), "window.eval(.)" slightly edges out "Function(.)", whereas "eval(.)" is somewhat slower. In Firefox, "window.eval(.)" is significantly faster than "Function(.)".

sloisel commented 12 years ago

I've gone down the "global eval" rabbit hole before and concluded that No Good Will Come of It!

I've been able to run the unit tests in node and all but 1 test pass. The 1 test that fails is an odd one that also fails in d8 and passes in all browsers so I'm not sure what exactly is going on, but I suspect it's because of eval().

sloisel commented 12 years ago

Addendum: upon rereading, I just caught your performance gains from window.eval(). This is not something I've measured but I'll try it again.