I'm writing a library for WebWorkers, and I want to test the difference between running a script in the main page thread, versus in one or more workers. The problem is: I can't find out of hand a short function which will strain my browser enough that I can observe the difference.
A quick search didn't return much, but it might just be that I don't really know what to search for; usually I try to optimise my code, not make it slower...
I'm looking for algorithms or patterns that can be easily implemented in pure Javascript, that do not depend on the DOM or XHR, and which can have an argument passed to limit or specify how far the calculation goes (no infinite algorithms); 1s < avg time < 10s.
Extra points if it can be built without recursion and if it does not incur a significant memory hog while still being as processor intensive as possible.
Try using the obvious (and bad) recursive implementation for the Fibonacci sequence:
function fib(x) {
if (x <= 0) return 0;
if (x == 1) return 1;
return fib(x-1) + fib(x-2);
}
Calling it with values of ~30 to ~35 (depending entirely on your system) should produce good "slow down" times in the range you seek. The call stack shouldn't get very deep and the algorithm is something like O(2^n).
/**
* Block CPU for the given amount of seconds
* #param {Number} [seconds]
*/
function slowdown(seconds = 0.5) {
const start = (new Date()).getTime()
while ((new Date()).getTime() - start < seconds * 1000){}
}
slowdown(2)
console.log('done')
Calling this method will slow code down for the given amount of seconds (with ~200ms precision).
Generate an array of numbers in reverse order and sort it.
var slowDown = function(n){
var arr = [];
for(var i = n; i >= 0; i--){
arr.push(i);
}
arr.sort(function(a,b){
return a - b;
});
return arr;
}
This can be called like so:
slowDown(100000);
Or whatever number you want to use.
Check out the benchmarking code referenced by the Google V8 Javascript Engine.
For some reason Bogosort comes to mind. Basically it's a sorting algorithm that consists of:
while not list.isInOrder():
list.randomize()
It has an average complexity of O(n * n!) with little memory, so it should slow things down pretty good.
The main downside is that its running time can be anywhere from O(n) to O(inf) (though really, O(inf) is pretty unlikely).
Everyone seems determined to be complicated. Why not this?
function waste_time(amount) {
for(var i = 0; i < amount; i++);
}
If you're concerned the browser will optimize the loop out of existence entirely, you can make it marginally more complicated:
function waste_time(amount) {
var tot = 0;
for(var i = 0; i < amount; i++)
tot += i;
}
Compute lots of square roots manually?
function sqrt(number, maxDecimal) {
var cDecimal = -1;
var cNumber = 0;
var direction = -1;
while(cNumber * cNumber !== number && cDecimal < maxDecimal) {
direction = -direction;
cDecimal++;
while((cNumber * cNumber - number) / Math.abs(cNumber * cNumber - number) === direction) cNumber += direction * Math.pow(10, -cDecimal);
}
return Math.abs(cNumber);
}
function performTest() {
for(var i = 0; i < 10000; i++) {
sqrt(i, 3);
}
}
Maybe this is what you are looking for:
var threadTest = function(durationMs, outputFkt, outputInterval) {
var startDateTime = (new Date()).getTime();
counter = 0,
testDateTime = null,
since = 0,
lastSince = -1;
do {
testDateTime = (new Date()).getTime();
counter++;
since = testDateTime - startDateTime;
if(typeof outputFkt != 'undefined' && lastSince != since && testDateTime % outputInterval == 0) {
outputFkt(counter, since);
lastSince = since;
}
} while(durationMs > since);
if(typeof outputFkt != 'undefined') {
outputFkt(counter, since);
}
return counter;
}
This method will simply repeat a check in a loop
durationMS - duartion it should run in miliseconds
OPTIONAL:
outputFkt - a callback method, for logging purpose function(currentCount, milisecondsSinceStart)
outputInterval - intervall the output function will be called
I figured since you do not want to test a real function, and even NP-Hard Problems have a ratio between input length and time this could be a easy way. You can measure performance at any interval and of course receive the number of loops as a return value, so you can easily measure how much threads interfere each others performance, with the callback even on a per cycle basis.
As an example here is how i called it (jQuery and Dom usage are here, but as you can see optional)
$(document).ready(function() {
var outputFkt = function(counter, since) {
$('body').append('<p>'+counter+', since '+since+'</p>');
};
threadTest(1000, outputFkt, 20);
});
A last Warning: Of course this function can not be more exact than JS itself. Since modern Browsers can do much more than one cycle in one Milisecond, there will be a little tail that gets cut.
Update
Thinking about it... actually using the ouputFkt callback for more than just output could give great insight. You could pass a method that uses some shared properties, or you could use it to test great memory usage.
Related
I'm struggling to understand how to effectively determine the time complexity of recursive code. While I can see how we can find the time complexity for code that has two recursive calls (e. g. recursive fib) as O(2^n), I struggle to dertime it when there are n recursions. Here is an example:
Quick note: I tried to come up with an easy example. I admit that there's very likely a formula to calculate size of the "powerset/superset" and gives us the work needed. My question is more generic and should relate to all times when one resursion can produce n additional recursions.
var subsets = function(nums) {
const result = [];
function traverse(arr, start) {
result.push(arr)
for (let i = start; i < nums.length; i++) {
traverse([...arr, nums[i]], i + 1);
}
}
traverse([], 0)
return result;
};
I've been reading about data-oriented programming in the context of Entity Component Systems. Apparently, using a struct of arrays can more effectively leverage the cache and give substantial performance increases. Basically, if all of the data you're iterating over is contiguous, then cache locality is leveraged to give a large performance increase.
Because I'll be working in Javascript, I figured I'd first devise a small little benchmark to see how much of a performance increase is possible under ideal conditions. I made it very simple. In the first test I benchmark the speed of iterating over an array of structs, and in the second I benchmark the speed of iterating over a struct of arrays.
Here is the code:
function randomInt() { return Math.floor(Math.random() * 100) + 1; }
function randomStr() { return Math.random().toString(36).substring(7); }
let samples = 1000;
let count = 10000000;
function benchmarkArrayOfStructs() {
let AOS = [];
for (let i = 0; i < count; i++) {
AOS.push({ health: randomInt(), name: randomStr(), damage: randomInt() });
}
let t1 = performance.now();
let sum = 0;
for (let x = 0; x < samples; x++) {
for (let i = 0; i < AOS.length; i++) {
let item = AOS[i];
sum += item.health + item.damage;
}
}
console.log(performance.now() - t1);
}
function benchmarkStructOfArrays() {
let SOA = { health: [], name: [], damage: [] }
for (let i = 0; i < count; i++) {
SOA.health.push(randomInt());
SOA.name.push(randomStr());
SOA.damage.push(randomInt());
}
let t2 = performance.now();
let sum = 0;
let h = SOA.health;
let d = SOA.damage;
for (let x = 0; x < samples; x++) {
for (let i = 0; i < count; i++) {
sum += h[i] + d[i];
}
}
console.log(performance.now() - t2);
}
benchmarkArrayOfStructs();
benchmarkStructOfArrays();
Interestingly, the latter solution is only 20% or so faster than the first solution. In the various talks that I've watched, they've claimed 10x speed increases for this type of operation. Also, intuitively I feel as if the latter solution should be much faster, but it isn't. Now I'm beginning to wonder if this sort of optimization is even worth integrating into my project, as it severely decreases ergonomics. Have I done something wrong in my benchmark, or is this the actual expected speedup?
JavaScript isn't going to auto-vectorize with SIMD while JITting. That's one of the biggest advantages that an SoA layout allows, but you aren't taking advantage of it. (And AFAIK can't easily in JS.)
Also, if your code is the only thread running on an otherwise-idle desktop machine, you have much more memory bandwidth available to your thread than on a typical server, or on a busy machine with all cores competing for memory access. (Intel Xeons have lower max per-core DRAM memory bandwidth due to higher latency interconnects, but higher aggregate bandwidth with all cores busy. That's assuming you miss in private L2 cache.) So your benchmark probably tested a case where you have lots of excess memory bandwidth.
You might also see more benefit from SoA if your objects are bigger. Your AoS loop is reading 2 of the 3 objects from every array element, so only a small part of the data brought into cache is "wasted". If you try with more fields that your loop doesn't use, you may see a bigger advantage.
In Javascript, there is no guarantee that an Array is a true array - for all you know the engine could be storing elements all over memory, nullifying cache locality. To force the engine to store an array in contiguous memory, you must use a TypedArray.
TypedArrays are also intrinsically faster than regular arrays since the JS engine doesn't have to do any guesswork whatsoever about usage patterns.
I want to understand the performance difference for constructing arrays. Running the following program, I am puzzled by the output below:
Time for range0: 521
Time for range1: 149
Time for range2: 1848
Time for range3: 8411
Time for range4: 3487
I don't understand why 3 takes longer than 4, while 1 takes shorter than 2. Also, seems the map function is very inefficient; what is the use of it?
function range0(start, count) {
var arr = [];
for (var i = 0; i < count; i++) {
arr.push(start + i);
}
return arr;
}
function range1(start, count) {
var arr = new Array(count);
for (var i = 0; i < count; i++) {
arr[i] = start + i;
}
return arr;
}
function range2(start, count) {
var arr = Array.apply(0, Array(count));
for (var i = 0; i < count; i++) {
arr[i] = start + i;
}
return arr;
}
function range3(start, count) {
var arr = new Array(count);
return arr.map(function(element, index) {
return index + start;
});
}
function range4(start, count) {
var arr = Array.apply(0, Array(count));
return arr.map(function(element, index) {
return index + start;
});
}
function profile(range) {
var iterations = 100000,
start = 0, count = 1000,
startTime, endTime, finalTime;
startTime = performance.now();
for (var i = 0; i < iterations; ++i) {
range(start, count);
}
endTime = performance.now();
finalTime = (endTime - startTime);
console.log(range.name + ': ' + finalTime + ' ms');
}
[range0, range1, range2, range3, range4].forEach(profile);
I don't understand why 3 takes longer than 4
Me neither. It is a surprising result, given my superficial analysis and the results I obtained by profiling the code. On my computer running Google Chrome 50, range4 is twice as slow compared to range3.
I'd have to study the Javascript implementation you are using in order to figure out why that happens.
while 1 takes shorter than 2.
range1 executes faster because it uses a loop and optimizes memory allocations, while range2 uses functions and does unnecessary memory allocations.
Also, seems the map function is very inefficient; what is the use of it?
The map function is used to compute a new Array based on the values of an existing one.
[1, 2, 3, 4, 5].map(number => number * number);
// [1, 4, 9, 16, 25]
On my computer
Time for range0: 783
Time for range1: 287
Time for range2: 10541
Time for range3: 14981
Time for range4: 28243
My results reflect my expectations regarding the performance of each function.
A superficial analysis of each function
range0
Creates an Array and populates it via a loop. It is the most simple and straightforward code possible. I suppose it could be understood as the baseline for performance comparison.
range1
Uses the Array constructor with a length parameter. This greatly optimizes the underlying memory allocation required to store the elements. Since the exact number of elements is known beforehand, the memory does not have to be reallocated as the number of elements grows; the exact amount of memory needed to store all elements can be allocated exactly once, when the Array is instantiated.
range2
Applies an empty arguments list to the constructor function, with this set to the number 0. This is semantically equivalent to Array() – the fact the arguments list was created with a count parameter has no bearing on the result of the function application. In fact, it needlessly wastes time allocating memory for an empty arguments list.
You probably meant to use call:
Array.call(null, count)
range3
Like range1, but uses map with a function instead of a loop. The initial memory allocation is optimized, but the overhead of calling the function count times is likely to be huge.
In addition, map generates a new Array instance. Since that instance also has count elements, it would make sense to optimize that memory allocation as well, however it is unclear to me whether that actually happens. Nevertheless, two separate memory allocations are taking place, instead of just one as in range1.
range4
Combines all the inefficiencies of range2 and range3.
Amazingly, it executes faster than range3 on your computer. It's unclear to me why that happened. I suppose one would have to investigate your Javascript particular implementation in order to figure it out.
I have a doubt about how can be affected to speed the use of object data arrays, that is, use it directly or preasign them to simple vars.
I have an array of elements, for example 1000 elements.
Every array item is an object with 10 properties (for example).
And finally I use some of this properties to do 10 calculations.
So I have APPROACH1
var nn = myarray.lenght;
var a1,a2,a3,a4 ... a10;
var cal1,cal2,.. cal10
for (var x=0;x<nn;x++)
{ // assignment
a1=my_array[x].data1;
..
a10 =my_array[x].data10;
// calculations
cal1 = a1*a10 +a2*Math.abs(a3);
...
cal10 = (a8-a7)*4 +Math.sqrt(a9);
}
And APPROACH2
var nn = myarray.lenght;
for (var x=0;x<nn;x++)
{
// calculations
cal1 = my_array[x].data1*my_array[x].data10 +my_array[x].data2*Math.abs(my_array[x].data3);
...
cal10 = (my_array[x].data8-my_array[x].data7)*4 +Math.sqrt(my_array[x].data9);
}
Assign a1 ... a10 values from my_array and then make calculations is faster than make the calculations using my_array[x].properties; or the right is the opposite ?????
I dont know how works the 'js compiler' ....
The kind of short answer is: it depends on your javascript engine, there is no right and wrong here, only "this has worked in the past" and "this don't seem to speed thing up no more".
<tl;dr> If i would not run a jsperf test, i would go with "Cached example" 1 example down: </tl;dr>
A general rule of thumb is(read: was) that if you are going to use an element in an array more then once, it could be faster to cache it in a local variable, and if you were gonna use a property on an object more then once it should also be cached.
Example:
You have this code:
// Data generation (not discussed here)
function GetLotsOfItems() {
var ret = [];
for (var i = 0; i < 1000; i++) {
ret[i] = { calc1: i * 4, calc2: i * 10, calc3: i / 5 };
}
return ret;
}
// Your calculation loop
var myArray = GetLotsOfItems();
for (var i = 0; i < myArray.length; i++) {
var someResult = myArray[i].calc1 + myArray[i].calc2 + myArray[i].calc3;
}
Depending on your browser (read:this REALLY depends on your browser/its javascript engine) you could make this faster in a number of different ways.
You could for example cache the element being used in the calculation loop
Cached example:
// Your cached calculation loop
var myArray = GetLotsOfItems();
var element;
var arrayLen = myArray.length;
for (var i = 0; i < arrayLen ; i++) {
element = myArray[i];
var someResult = element.calc1 + element.calc2 + element.calc3;
}
You could also take this a step further and run it like this:
var myArray = GetLotsOfItems();
var element;
for (var i = myArray.length; i--;) { // Start at last element, travel backwards to the start
element = myArray[i];
var someResult = element.calc1 + element.calc2 + element.calc3;
}
What you do here is you start at the last element, then you use the condition block to see if i > 0, then AFTER that you lower it by one (allowing the loop to run with i==0 (while --i would run from 1000 -> 1), however in modern code this is usually slower because you will read an array backwards, and reading an array in the correct order usually allow for either run-time or compile-time optimization (which is automatic, mind you, so you don't need to do anything for this work), but depending on your javascript engine this might not be applicable, and the backwards going loop could be faster..
However this will, by my experience, run slower in chrome then the second "kinda-optimized" version (i have not tested this in jsperf, but in an CSP solver i wrote 2 years ago i ended caching array elements, but not properties, and i ran my loops from 0 to length.
You should (in most cases) write your code in a way that makes it easy to read and maintain, caching array elements is in my opinion as easy to read (if not easier) then non-cached elements, and they might be faster (they are, at least, not slower), and they are quicker to write if you use an IDE with autocomplete for javascript :P
Of these similar function definitions, what is optimal way to access arguments and why?
function DoStuff()
{
return arguments[0] + arguments[1] + arguments[2];
}
function DoStuff(first, second, third)
{
return first + second + third;
}
Does one allocate less memory than the other? Is one faster to access the parameter values than the other?
Here is my test:
function Test1()
{
var start = new Date();
for (var i = 0; i < 1000000; i++)
{
DoStuff1(i, i + 1, i + 2);
}
var done = new Date();
alert(done - start);
}
function Test2()
{
var start = new Date();
for (var i = 0; i < 1000000; i++)
{
DoStuff2(i, i + 1, i + 2);
}
var done = new Date();
alert(done - start);
}
function DoStuff1()
{
var result = arguments[0] + arguments[1] + arguments[2];
}
function DoStuff2(first, second, third)
{
var result = first + second + third;
}
Here are the results:
IE FF
Test1()
2355 402
2381 395
2368 392
Test2()
504 6
507 7
505 7
I figured that test 2 would be faster but it's drastically faster. So, not only is it more readable but it's more efficient.
Forget about performance in this case and go for readability. From that perspective, option (2) is much to be preferred -- though, I'd go for more descriptive names. ;-)
The second.
Using the second, you know based on the signature exactly what is expected when you call the method. It's also far easier for maintainability in the future.
Referencing arguments[] anywhere in a function will significantly decrease performance on many browsers.
The performance difference should be fairly negligible between the two, however the readability of named parameters is a better practice. There are times when you cannot use named parameters and in those cases I typically use a big block of script to unpack arguments into named locals.