Out of curiosity I wrote some trivial benchmarks comparing the performance of golang maps to JavaScript (v8/node.js) objects used as maps and am surprised at their relative performance. JavaScript objects appear to perform roughly twice as fast as go maps (even including some minor performance edges for go)!
Here is the go implementation:
// map.go
package main
import "fmt"
import "time"
func elapsedMillis(t0, t1 time.Time) float64 {
n0, n1 := float64(t0.UnixNano()), float64(t1.UnixNano())
return (n1 - n0) / 1e6
}
func main() {
m := make(map[int]int, 1000000)
t0 := time.Now()
for i := 0; i < 1000000; i++ {
m[i] = i // Put.
_ = m[i] + 1 // Get, use, discard.
}
t1 := time.Now()
fmt.Printf("go: %fms\n", elapsedMillis(t0, t1))
}
And here is the JavaScript:
#!/usr/bin/env node
// map.js
function elapsedMillis(hrtime0, hrtime1) {
var n0 = hrtime0[0] * 1e9 + hrtime0[1];
var n1 = hrtime1[0] * 1e9 + hrtime1[1];
return (n1 - n0) / 1e6;
}
var m = {};
var t0 = process.hrtime();
for (var i=0; i<1000000; i++) {
m[i] = i; // Put.
var _ = m[i] + 1; // Get, use, discard.
}
var t1 = process.hrtime();
console.log('js: ' + elapsedMillis(t0, t1) + 'ms');
Note that the go implementation has a couple of minor potential performance edges in that:
Go is mapping integers to integers directly, whereas JavaScript will convert the integer keys to string property names.
Go makes its map with initial capacity equal to the benchmark size, whereas JavaScript is growing from its default capacity).
However, despite the potential performance benefits listed above the go map usage seems to perform at about half the rate of the JavaScript object map! For example (representative):
go: 128.318976ms
js: 48.18517ms
Am I doing something obviously wrong with go maps or somehow comparing apples to oranges?
I would have expected go maps to perform at least as well - if not better than JavaScript objects as maps. Is this just a sign of go's immaturity (1.4 on darwin/amd64) or does it represent some fundamental difference between the two language data structures that I'm missing?
[Update]
Note that if you explicitly use string keys (e.g. via s := strconv.Itoa(i) and var s = ''+i in Go and JavaScript, respectively) then their performance is roughly equivalent.
My guess is that the very high performance from v8 is related to a specific optimization in that runtime for objects whose keys are consecutive integers (e.g. by substituting an array implementation instead of a hashtable).
I'm voting to close since there is likely nothing to see here...
Your benchmark is synthetic a bit, just like any benchmarks are. Just for curious try
for i := 0; i < 1000000; i += 9 {
in Go implementation. You may be surprised.
Related
I have been playing around with the Riemann zeta function.
I want to optimize execution time as much as possible here so I put the intermediary results in temporary variables. But testing revealed that I get no performance boost from this. At least not noticeably.
function zeta(z, limit){
var zres = new Complex(0, 0);
for(var x = 1; x <= limit; x++){
var ii = z.imaginary * Math.log(1/x);
var pp = Math.pow(1/x, z.real);
zres.real += pp * Math.cos(ii);
zres.imaginary += pp * Math.sin(ii);
}
return zres;
}
My question is: Even though I couldn't measure a difference in execution time, what's theoretically faster? Calculating ii and pp once and handing them over as variables, or calculating them twice and not wasting time with the declaration?
Putting things in (local) variables on its own will usually not have a major effect on performance. If anything it could increase the pressure on the register allocator (or equivalent) and slightly reduce performance.
Avoiding calculating expressions multiple times by putting the result into a local variable can improve performance if the just-in-time compiler (or runtime) isn't smart enough to do the equivalent optimization (i.e. compute the value only once and re-use the computation result each times the expression is used).
There's really no universally applicable rule here. You need to benchmark and optimize on the specific system you want best performance on.
Instanciating a variable is surely faster then a math operation (like Math.log or Math.pow) so it is better to instantiate them. If you want to prevent the local scope of the for to waste some very little less time due to the variable initializzation and collection you can declare pp and ii out of the loop. This is a quite ridiculous time in respect of all the other operations.
function zeta(z, limit){
var zres = new Complex(0, 0);
var ii, pp;
for(var x = 1; x <= limit; x++){
ii = z.imaginary * Math.log(1/x);
pp = Math.pow(1/x, z.real);
zres.real += pp * Math.cos(ii);
zres.imaginary += pp * Math.sin(ii);
}
return zres;
}
So I was curious what would be faster for iterating through an array, the normal for loop or forEach so I executed this code in the console:
var arr = [];
arr.length = 10000000;
//arr.fill(1);
for (var i_1 = 0; i_1 < arr.length; i_1++) {
arr[i_1] = 1;
}
//////////////////////////////////
var t = new Date();
var sum = 0;
for (var i = 0; i < arr.length; i++) {
var a = arr[i];
if (a & 1) {
sum += a;
}
else {
sum -= a;
}
}
console.log(new Date().getTime() - t.getTime());
console.log(sum);
t = new Date();
sum = 0;
arr.forEach(function (value, i, aray) {
var a = value;
if (a & 1) {
sum += a;
}
else {
sum -= a;
}
});
console.log(new Date().getTime() - t.getTime());
console.log(sum);
Now the results in Chrome are 49ms for the for loop, 376ms for the forEach loop. Which is ok but the results in Firefox and IE (and Edge) are a lot different.
In both other browsers the first loop takes ~15 seconds (yes seconds) while the forEach takes "only" ~4 seconds.
My question is can someone tell me the exact reason Chrome is so much faster?
I tried all kinds of operations inside the loops, the results were always in favor for Chrome by a mile.
Disclaimer: I do not know the specifics of V8 in Chrome or the interpreter of Firefox / Edge, but there are some very general insights. Since V8 compiles Javascript to native code, let's see what it potentially could do:
Very crudely: variables like your var i can be modelled as a very general Javascript variable, so that it can take any type of value from numbers to objects (modelled as a pointer to a struct Variable for instance), or the compiler can deduce the actual type (say an int in C++ for instance) from your JS and compile it like that. The latter uses less memory, exploits caching, uses less indirection, and can potentially be as fast as a for-loop in C++. V8 probably does this.
The above holds for your array as well: maybe it compiles to a memory efficient array of ints stored contiguously in memory; maybe it is an array of pointers to general objects.
Temporary variables can be removed.
The second loop could be optimized by inlining the function call, maybe this is done, maybe it isn't.
The point being: all JS interpreters / compilers can potentially exploit these optimizations. This depends on a lot of factors: the trade-off between compilation and execution time, the way JS is written, etc.
V8 seems to optimize a lot, Firefox / Edge maybe don't in this example. Knowing why precisely requires in-depth understanding of the interpreter / compiler.
For loop is the afastest when compared to other iterators in every browser. But when comparing browsers ie is the slowest in iteration of for loops. Go and try jsperf.com for optimization is going to be my best recommendation. V8 engine implementation is the reason. After chrome split from webkit it stripped off more than 10k line of code in first few days.
I just discovered that Javascript has typed arrays via this link. I was immediately curious what the benefit of such objects might be in the language.
I noticed that UInt8Arrays lose the .map()-type functions that I would have for normal arrays objects so if you want to loop over them you would need a for loop.
I assumed that I might be able to expect some performance boost when using UInt8Arrays but this doesn't seem to be the case.
var a = d3.range(225)
var b = new Uint8Array(d3.range(225))
console.time("a")
var result = 0;
for (var j = 10000; j >= 0; j--) {
for (var i = a.length - 1; i >= 0; i--) {
result += a[i];
};
};
console.timeEnd("a")
console.time("b")
var result = 0;
for (var j = 10000; j >= 0; j--) {
for (var i = b.length - 1; i >= 0; i--) {
result += b[i];
};
};
console.timeEnd("b")
I am using the d3 library to quickly generate a large array. This script gives the following output:
a: 2760.176ms
b: 2779.477ms
So the performance doesn't improve. The UInt8Array also doesn't throw an error when you insert a wrong value.
> new Uint8Array([1,2,3,4,'aasdf'])
[1,2,3,4,0]
With this in mind, what is the proper use case for UInt8Array in Javascript? It seems like the normal array is a lot more flexible, equally robust and equally fast.
"Performance" usually doesn't mean just how fast your script runs. There are also many other important factors, like how fast it freezes your pc and/or crashes. memory. The smallest amount of memory javascript implementation usually allocate for a var is 32 bit. This means
var a = true;
a boolean looks like this in your memory:
0000 0000 0000 0000 0000 0000 0000 0001
It's a huge waste, but usually not a problem, as no one uses a significant enough amount of them for it to really matter. Typed Arrays are for cases where it does matter, when you can actually reduce your memory usage by a huge amount, like when working with image data, sound data, or all sorts of raw binary data.
Another difference, that allows you to potentially save even more memory in some cases is that it allows you to operate on data passed by reference you'd normally pass by value.
Consider this case:
var oneImage = new Uint8Array( 16 * 16 * 4 );
var onePixel = new Uint8Array( oneImage.buffer, 0, 4 );
you now have 2 independent views on the same ArrayBuffer, operating on the same data, applying that concept allows you to not only have that one huge thing in your memory, it allows you to actually subdivide it into as many segments as you currently want to work on with little overhead, which is probably even more important.
I am profiling my javascript code intended to be used on embedded browser on Android (PhoneGap).
Basically I need a very large bitfield (200k+ bits) for my calculations.
I've tried to put them into array of unsigned integers with each item storing 32 bits - this indeed reduced memory usage but made execution time drastically too slow (over 30 seconds for simple iterating and reversing all bits in the bitfield on modern PC!)
Than I made good old fashion array of bools. This increased memory usage (but still it was less than 15 mega on Android for entire PhoneGap framework around my code). Profiling showed me that initial step in my algorithm - setting all elements of the bitfield to 1 (simple for- loop) - takes half of the execution time (~1.5 seconds on PC, more than few minutes on Android). I can rewrite my code so default value would be 0 not 1 (reverse all conditions), but I still don't know how to set such large array to 0'es fast.
Edit adding my code, as requested:
var count = 200000;
var myArr = [];
myArr.length = count;
for(var i = 0; i < count ; i++)
myArr[i] = true;
Could someone point me how can I clear very large array, or is there any faster way to store and operate on large bitfields in javascript?
See if this is a faster way to create the array:
var myArray = [true];
var desiredLength = 200000;
while (myArray.length < desiredLength) {
myArray = myArray.concat(myArray);
}
if (myArray.length > desiredLength) {
myArray.splice(desiredLength);
}
I've added a few more test cases to the jsperf page that Asad linked in his comment. By far the fastest in my browser (Chrome 23.0.1271.101 on Mac OS X 10.8.2) is this one:
var count = 200000;
var myArr = [];
for (var i = 0; i < count; i++) {
myArr.push(true);
}
Why pre-fill the array in the first place! Use undefined to your advantage. Remember that undefined acts as a falsey value. So it will act exactly like 0/false when you do a boolean check.
var myArray = new Array(200000);
if (myArray[1]) {
//I am a truthy value
} else {
//I am a falsey value
}
So when you initialize the array this way, there is no reason to prefill! That means no extra processing and take advantage of the sparse Array!
Edit: Cleaned up the code and the player (on Github) a little so it's easier to set the frequency
I'm trying to synthesize strings using the Karplus Strong string synthesis algorithm, but I can't get the string to tune properly. Does anyone have any idea?
As linked above, the code is on Github: https://github.com/achalddave/Audio-API-Frequency-Generator (the relevant bits are in strings.js).
Wiki has the following diagram:
So essentially, I generate the noise, which then gets output and sent to a delay filter simultaneously. The delay filter is connected to a low-pass filter, which is then mixed with the output. According to Wikipedia, the delay should be of N samples, where N is the sampling frequency divided by the fundamental frequency (N = f_s/f_0).
Excerpts from my code:
Generating the noise (bufferSize is 2048, but that shouldn't matter too much)
var buffer = context.createBuffer(1, bufferSize, context.sampleRate);
var bufferSource = context.createBufferSource();
bufferSource.buffer = buffer;
var bufferData = buffer.getChannelData(0);
for (var i = 0; i < delaySamples+1; i++) {
bufferData[i] = 2*(Math.random()-0.5); // random noise from -1 to 1
}
Create a delay node
var delayNode = context.createDelayNode();
We need to delay by f_s/f_0 samples. However, the delay node takes the delay in seconds, so we need to divide that by the samples per second, and we get (f_s/f_0) / f_s, which is just 1/f_0.
var delaySeconds = 1/(frequency);
delayNode.delayTime.value = delaySeconds;
Create the lowpass filter (the frequency cutoff, as far as I can tell, shouldn't affect the frequency, and is more a matter of whether the string "sounds" natural):
var lowpassFilter = context.createBiquadFilter();
lowpassFilter.type = lowpassFilter.LOWPASS; // explicitly set type
lowpassFilter.frequency.value = 20000; // make things sound better
Connect the noise to the output and the delay node (destination = context.destination and was defined earlier):
bufferSource.connect(destination);
bufferSource.connect(delayNode);
Connect the delay to the lowpass filter:
delayNode.connect(lowpassFilter);
Connect the lowpass to the output and back to the delay*:
lowpassFilter.connect(destination);
lowpassFilter.connect(delayNode);
Does anyone have any ideas? I can't figure out whether the issue is my code, my interpretation of the algorithm, my understanding of the API, or (though this is least likely) an issue with the API itself.
*Note that on Github, there's actually a Gain Node between the lowpass and the output, but this doesn't really make a big difference in the output.
Here's what I think is the problem. I don't think the DelayNode implementation is designed to handle such tight feedback loops. For a 441 Hz tone, for example, that's only 100 samples of delay, and the DelayNode implementation probably processes its input in blocks of 128 or more. (The delayTime attribute is "k-rate", meaning changes to it are only processed in blocks of 128 samples. That doesn't prove my point, but it hints at it.) So the feedback comes in too late, or only partially, or something.
EDIT/UPDATE: As I state in a comment below, the actual problem is that a DelayNode in a cycle adds 128 sample frames between output and input, so that the observed delay is 128 / sampleRate seconds longer than specified.
My advice (and what I've begun to do) is to implement the whole Karplus-Strong including your own delay line in a JavaScriptNode (now known as a ScriptProcessorNode). It's not hard and I'll post my code once I get rid of an annoying bug that can't possibly exist but somehow does.
Incidentally, the tone you (and I) get with a delayTime of 1/440 (which is supposed to be an A) seems to be a G, two semitones below where it should be. Doubling the frequency raises it to a B, four semitones higher. (I could be off by an octave or two - kind of hard to tell.) Probably one could figure out what's going on (mathematically) from a couple more data points like this, but I won't bother.
EDIT: Here's my code, certified bug-free.
var context = new webkitAudioContext();
var frequency = 440;
var impulse = 0.001 * context.sampleRate;
var node = context.createJavaScriptNode(4096, 0, 1);
var N = Math.round(context.sampleRate / frequency);
var y = new Float32Array(N);
var n = 0;
node.onaudioprocess = function (e) {
var output = e.outputBuffer.getChannelData(0);
for (var i = 0; i < e.outputBuffer.length; ++i) {
var xn = (--impulse >= 0) ? Math.random()-0.5 : 0;
output[i] = y[n] = xn + (y[n] + y[(n + 1) % N]) / 2;
if (++n >= N) n = 0;
}
}
node.connect(context.destination);