To get the correct answer, we need to convert some of the numbers to BigInts
var result = 0n
function main() {
var sum = 0n
var i = 1
while (i <= 1000000000) {
sum = sum + BigInt(i)
i = i + 1
}
result = sum
}
main()
but after that the result is not quite so impressive anymore Now using node v15.6.0 (npm v7.4.0)
time node index.js
node index.js 51.50s user 0.40s system 103% cpu 50.072 totalYou could compare it with PyPy, a just-in-time compiler for python.
For example, on my machine, the same program runs in 2:12 min with the regular python interpreter, and in 1.57 seconds with pypy.
^1 Most likely because it generates C code and has to call an external C compiler to get things done.
Why do you think that, and how would it do that and still do the calculation? My timing tests showed that Go didn't optimize the loop away (if I counted to 10 billion it took about 10 times as long), and godbolt.org also shows the loop in Go's assembly output: https://www.godbolt.org/z/d38xfGYr6
$ time python -c 'sum = 0
> for i in range(1000000000): sum += 1'
real 0m56.655s
user 0m56.646s
sys 0m0.008s
Compare to: time python -c 'sum(range(1000000000))'
real 0m8.693s
user 0m8.684s
sys 0m0.008s
In reality, anyone is going to do heavy arithmetic using Numpy anyway: $ time python -c 'import numpy as np; np.sum(np.arange(1000000000))'
real 0m2.022s
user 0m1.661s
sys 0m2.061s
As a nice bonus, you get hardware speed but it still gives you the right answer, unlike Node trying to use limited precision primitive types.As for why Node runs quickly, adding integers (or floats or whatever) is easy mode for a JIT. This is basically why you see JIT numbers like speeding up the original implementation by factors of literally over 100x, yet you can't expect that multiplier against "real code", for essentially the same reason only now on the other end; the JIT can very successfully cut out all that work CPython or similar implementatiors are doing if you have completely stable types that it can infer (especially something as simple as a single int or float), but now the "payload" is preventing the JIT from posting big numbers in improvement when you have more complicated operations because the JIT isn't going to be able to improve something like a substring search. If you get something like NumPy where you can feasibly write a program that will spend 99+% of its time in highly optimized matrix code, a JIT can't help much with that, because even if it reduced the bookkeeping to zero, you've still got to do all that matrix work.
Statically-typed compiled languages basically bypass the whole "do lots of expensive bookkeeping to permit very flexble coding styles, then write a JIT that figures out how to bypass that expensive bookkeeping as often as possible" to simply constrain you to write in the fast subset in the first place.
"V8 Internals For JavaScript Developers"
I credit much of Chrome's success to V8. It's a marvel of engineering. Shame that the rest of Chrome hasn't been held to such a high standard.
Chrome came out a month after Mozilla published its TraceMonkey JIT. So JITing JavaScript has been the standard thing to do since the end of 2008.
> For reference, a Python version of this same loop runs in 1 minute and 38 seconds … a dynamically-typed bytecode interpreter is not a good choice for heavy integer arithmetic.
made me wonder about other dynamic langs.
ruby (3.0.0) and lua (5.4.2) ran it in 1:30 and 1:03 respectively but node (12.22.0) ran it in 0:01. how is node so quick?