In attempting to build a WebGL 3D library for myself (learning purposes mostly) I followed documentation that I found from various sources that stated that the TypedArray function set() (specifically for Float32Array), is supposed to be "as fast as" memcpy in C (obviously tongue in cheek), literally the fastest according to html5rocks. On appearances that seemed to be correct (no loop setup in javascript, disappearing into some uberfast typed array pure C nonsense, etc).
I took a gander at glMatrix (good job on it btw!), and noticed that he (author) stated that he unrolled all of the loops for speed. This is obviously something a javascript guru would do normally for as much speed as possible, but, based on my previous reading, I thought I had 1-up on this library, specifically, he created his lib to be functional with both arrays and typed arrays, thus I thought that I would get more speed by using set() since I was only interested in staying in TypedArray types.
To test my theory I set up this jsperf. Not only does set() comparatively lack speed, every other technique I tried (in the jsperf), beats it. It is the slowest by far.
Finally, my question: Why? I can theoretically understand a loop unrolling becoming highly optimized in spidermonkey or chrome V8 js-engines, but losing out to a for loop seems ridiculous (copy2 in jsperf), especially if its intent is theoretically to speed up copies due to the raw contiguous in memory data types (TypedArray). Either way it feels like the set() function is broken.
Is it my code? my browser? (I am using Firefox 24) or am I missing some other theory of optimization? Any help in understanding this contrary result to my thoughts and understandings would be incredibly helpful.
This is an old question, but there is a reason to use TypedArrays if you have a specific need to optimize some poorly performing code. The important thing to understand about TypedArray objects in JavaScript is that they are views which represent a range of bytes inside of an ArrayBuffer. The underlying ArrayBuffer actually represents the contiguous block of binary data to operate on, but we need a view in order to access and manipulate a window of that binary data.
Separate (or even overlapping) ranges in the same ArrayBuffer can be viewed by multiple different TypedArray objects. When you have two TypedArray objects that share the same ArrayBuffer, the set operation is extremely fast. This is because the machine is working with a contiguous block of memory.
Here's an example. We'll create an ArrayBuffer of 32 bytes, one length-16 Uint8Array to represent the first 16 bytes of the buffer, and another length-16 Uint8Array to represent the last 16 bytes:
var buffer = new ArrayBuffer(32);
var array1 = new Uint8Array(buffer, 0, 16);
var array2 = new Uint8Array(buffer, 16, 16);
Now we can initialize some values in the first half of the buffer:
for (var i = 0; i < 16; i++) array1[i] = i;
console.log(array1); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
console.log(array2); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
And then very efficiently copy those 8 bytes into the second half of the buffer:
array2.set(array1);
console.log(array1); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
console.log(array2); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
We can confirm that the two arrays actually share the same buffer by looking at the buffer with another view. For example, we could use a length-8 Uint32Array that spans the entire 32 bytes of the buffer:
var array3 = new Uint32Array(buffer)
console.log(array3); // [50462976, 117835012, 185207048, 252579084,
// 50462976, 117835012, 185207048, 252579084]
I modified a JSPerf test I found to demonstrate the huge performance boost of a copy on the same buffer:
http://jsperf.com/typedarray-set-vs-loop/3
We get an order of magnitude better performance on Chrome and Firefox, and it's even much faster than taking a normal array of double length and copying the first half to the second half. But we have to consider the cycles/memory tradeoff here. As long as we have a reference to any single view of an ArrayBuffer, the rest of the buffer's data can not be garbage collected. An ArrayBuffer.transfer function is proposed for ES7 Harmony which would solve this problem by giving us the ability explicitly release memory without waiting for the garbage collector, as well as the ability to dynamically grow ArrayBuffers without necessarily copying.
Well set doesn't exactly have simple semantics like that, in V8 after doing some figuring out of what should be done it will essentially arrive at exactly the same loop that the other methods are directly doing in the first place.
Note that JavaScript is compiled into highly optimized machine code if you play your cards right (all the tests do that) so there should be no "worshipping" of some methods just because they are "native".
I've also been exploring how set() performs and I have to say that for smaller blocks (such as the 16 indices used by the original poster), set() is still around 5x slower than the comparable unrolled loop, even when operating on a contiguous block of memory.
I've adapted the original jsperf test here. I think its fair to say that for small block transfers such as this, set() simply can't compete with unrolled index assignment performance. For larger block transfers (as seen in sbking's test), set() does perform better but then it is competing with literally 1 million array index operations so it would seem bonkers to not be able to overcome this with a single instruction.
The contiguous buffer set() in my test does perform slightly better than the separate buffer set(), but again, at this size of transfer the performance benefit is marginal
Related
While looking over new changes to JavaScript I noticed that Set and Map use .size instead of .length like arrays would.
This seems like a pointless diversion from what's normal with arrays - just one more thing to remember.
Was there a good design reason for this?
There's a lot of discussion in the esdiscuss thread "Set length property". This was a hotly debated issue, so it's not surprising that you do not necessarily agree with the resolution.
There is a tremendous amount of arguing about this in esdiscuss. Ultimately, the argument that prevailed (as evidenced by the fact that ES2015's Sets have size and not length) was summarized in a post by David Bruant:
...for me 'length' refers to a measurement with something like a ruler. You start at 0 and see up to where it goes. This is very accurate for an array which is an indexed set (starting at 0 and growing) and for arrays as considered in C (continuous sequence of bytes) which ECMAScript arrays seem inspired of. This is probably less relevant for unordered collections such as sets which I'd tend to consider as a messy bag.
And further discussed in a post by Dean Landolt:
Just wanted to jump in and say non-writable length is consistent with String behavior as well, but David makes a good point about length implying metric topology. David's suggestion of count is nice. ISTM what we're talking about is cardinality, but no need to get too silly w/ precision. Though size is just fine with me, and has plenty of prior art.
While apsillers' Jan 27, 2016 answer adds great links, a code example is missing. The size of a set is a read-only getter while that's not the case for arrays which allow modifying the length to truncate the array.
let arr = [1, 2, 3, 4]
arr.length = 2
console.log("modified length array", arr) // [1, 2]
let mySet = new Set([1, 2, 3, 4])
mySet.length = 2
mySet.size = 2
console.log("modified length set", [...mySet]) // [1, 2, 3, 4]
let str = "1234"
str.length = 2
console.log("modified length string", str) // "1234"
I'm working on Mat in OpenCV. However, I need to manually calculate the Mat by myself. Is there is a way of accessing Mat likes 2D array?
const myMat = cv.matFromArray(cv, 3, 3, cv.CV_64F, [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
])
const newMat = someProcessThatReturnMat(myMat)
/* OpenCV in JS cannot access Mat like this */
const result = someProcess(newMat[1][2], newMat[2][0])
Thank you in advance
Updated: The problem is cv.matFromArray cannot convert 2D array to Mat. You have to use it as 1D array. That's why it never return the correct values. For example:
const myMat = cv.matFromArray(3, 3, cv.CV_64F, [1,2,3,4,5,6,7,8,9])
And then, you can access the value
const value = myMat.doubleAt(1, 2) // row 1, col 2
You need to use the doubleAt(y,x) method.
It's double because the mat's content is CV_64F, not because you want doubles.
You can also use .data64F to access a flat Float64Array of the Mat's data.
OpenCV.js is... rough. It originated from someone's Google Summer of Code and hasn't received significant care since. Documentation amounts to some tutorials; API docs seem to be missing entirely. The "Mat" interface emulated the at() method from C++, badly, instead of looking at numpy (python) or making this access feel "native" to javascript. Overloading the [] operator is possible using a Proxy but that was not implemented.
Here's an example: https://docs.opencv.org/4.x/de/d06/tutorial_js_basic_ops.html
Feel free to browse OpenCV's issues and maybe suggest some improvements.
Story
During some tests for a performance critical code, I observed a side-effect of Math.random() that I do not understand. I am looking for
some deep technical explanation
a falsification for my test (or expectation)
link to a V8 problem/bug ticket
Problem
It looks like that calling Math.random() allocates some memory that needs to be cleaned up by the Gargabe Collector (gc).
Test: With Math.random()
const numberOfWrites = 100;
const obj = {
value: 0
};
let i = 0;
function test() {
for(i = 0; i < numberOfWrites; i++) {
obj.value = Math.random();
}
}
window.addEventListener('DOMContentLoaded', () => {
setInterval(() => {
test();
}, 10);
});
Observation 1: Chrome profile
Chrome: 95.0.463869, Windows 10, Edge: 95.0.1020.40
Running this code in the browser and record a perfromance profile will result in a classic memory zig-zag
Memory profile of Math.random() test
Obersation 2: Firefox
Firefox Developer: 95, Windows 10
No Garbage Collection (CC/GCMinor) detected - memory quite linear
Workarounds
crypto.getRandomValues()
Replace Math.random() with a large enough array of pre-calculated random numbers using self.crypto.getRandomValues`.
(V8 developer here.)
Yes, this is expected. It's a (pretty fundamental) design decision, not a bug, and not strictly related to Math.random(). V8 "boxes" floating-point numbers as objects on the heap. That's because it uses 32 bits per field in an object, which obviously isn't enough for a 64-bit double, and a layer of indirection solves that.
There are a number of special cases where this boxing can be avoided:
in optimized code, for values that never leave the current function.
for numbers whose values are sufficiently small integers ("Smis", signed 31-bit integer range).
for elements in arrays that have only ever seen numbers as elements (e.g. [1, 2.5, NaN], but not [1, true, "hello"]).
possibly other cases that I'm not thinking of right now. Also, all these internal details can (and do!) change over time.
Firefox uses a fundamentally different technique for storing internal references. The benefit is that it avoids having to box numbers, the drawback is that it uses more memory for things that aren't numbers. Neither approach is strictly better than the other, it's just a different tradeoff.
Generally speaking you're not supposed to have to worry about this, it's just your JavaScript engine doing its thing :-)
Problem: Running this code in the browser and record a performance profile will result in a classic memory zig-zag
Why is that a problem? That's how garbage-collected memory works. (Also, just to put things in perspective: the GC only spends ~0.3ms every ~8s in your profile.)
Workaround: Replace Math.random() with a large enough array of pre-calculated random numbers using self.crypto.getRandomValues`.
Replacing tiny short-lived HeapNumbers with a big and long-lived array doesn't sound like a great way to save memory.
If it really matters, one way to avoid boxing of numbers is to store them in arrays instead of as object properties. But before going through hard-to-maintain contortions in your code, be sure to measure whether it really matters for your app. It's easy to demonstrates huge effects in a microbenchmark, it's rare to see it have much impact in real apps.
Consider the following constants I have in my app:
const Actions = {
SONGS_CHANGED: 0,
PREVIEW_TAG_CHANGES: 1,
END_PREVIEW: 2,
SAVE_TAGS: 3,
PATTERNS_UPDATED: 4,
PATTERN_MAPPED: 5,
PATTERN_UNMAPPED: 6,
CHANGE_NUMBER_OF_PARENT_DIRECTORIES: 7,
}
I'm trying to keep these grouped together. But that means if I add one to the first group, then I have to update the numbers for everything below it. Well, obviously I don't have to, I could always just assign it the next higher number (e.g. give the next constant a value of 8 regardless of which "group" I put it in). But as the quantity of constants increases, it will become harder to identify the highest number in order to know what the next one should be.
In python, using something like range() to just generate numbers for the constants removes this issue entirely. What's a good JavaScript approach?
If you really do want to just number them in the order defined, you can use an incrementing variable to do so:
var x = 0;
const Actions = {
SONGS_CHANGED: x++,
PREVIEW_TAG_CHANGES: x++,
END_PREVIEW: x++,
SAVE_TAGS: x++,
PATTERNS_UPDATED: x++,
PATTERN_MAPPED: x++,
PATTERN_UNMAPPED: x++,
CHANGE_NUMBER_OF_PARENT_DIRECTORIES: x++,
}
console.log(Actions.END_PREVIEW); //2
console.log(Actions.SAVE_TAGS); //3
Note that changing constants in this fashion should only be used if the only place these numbers are used is by referencing the constants - if you're storing them elsewhere etc then changing them will break existing stored data. The choice comes down to whether they're just constant at runtime, or should be permanently constant.
If you know (or can estimate) a priori how big each group will ever be then you can use different subranges of numbers like this:
const Actions = {
SONGS_CHANGED: 0,
PREVIEW_TAG_CHANGES: 1,
END_PREVIEW: 2,
SAVE_TAGS: 3,
PATTERNS_UPDATED: 10,
PATTERN_MAPPED: 11,
PATTERN_UNMAPPED: 12,
CHANGE_NUMBER_OF_PARENT_DIRECTORIES: 50,
}
The advantage over James Thorpe's solution is that it is backwards compatible, i.e. you don't modify previously defined values ever.
A completely different approach that may or may not be suitable depending on what values your constants can take, but if they could be strings, for instance, you could use a GUID based approach:
const Actions = {
SONGS_CHANGED: '4e6c355f-d684-4544-8320-529f20c89ce7',
PREVIEW_TAG_CHANGES: '39a34b2b-0467-46f9-9d21-f407f89ae3c7',
END_PREVIEW: '350e1d61-e6f9-4cc5-9364-bbdc14fd7063',
SAVE_TAGS: '32e85a98-e13c-480c-9449-0a5bec7ec13c',
PATTERNS_UPDATED: '16536175-e421-40d9-9d5d-962bcd0f0771',
PATTERN_MAPPED: '0fd2bbd8-91f4-413b-8010-736bcacd1d88',
PATTERN_UNMAPPED: 'b946d566-d951-4695-a0ce-56b0bf987eb6',
CHANGE_NUMBER_OF_PARENT_DIRECTORIES: '5b0736f5-e5e9-48f3-a9a1-bed8d0882beb',
}
You can then add new constants anywhere without worrying about changing existing values, or needing to know roughly how many you may need in the future. There are plenty of ways of generating GUIDs, such as this online tool.
I am little bit confused about every() and forloop. Which is the best loop for array elements with some condition. For example:
function isBigEnough(element, index, array) {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
or
var arr = [12, 5, 8, 130, 44];
for(i =0 ; i<arr.length; i++) {
if(arr[i] >= 10)
console.log(arr[i])
}
Which is one best performance for check/sort out the element from array. I hope you to clear my doubts.
first thing instead of every use some function, it is more apt in your case.
secondly using asynchronous native array functions is always better in terms of code readability as well as performance when you app scales.
Plus they have additional benefit when it comes to object oriented implementation.
The best is to ignore issues like that unless you have a good reason to suspect that performance is going to be a factor.
Use the cleanest, most readable approach by default. Only consider performance when you think it's likely this is going to be a bottle-neck (and a bother). Iterating over ten elements isn't - and even then, the answer is that you need to profile the time for yourself; just try both the possibilities, measure the difference, and if it's big enough, pick the faster one. Saving 5% of run time isn't usually worth the costs.
I would go for the simplest approach and that is with the for loop.
The Every loop is something new that not all browsers are compatible with and this mean you will add more code to check this.
Therefore the for loop is the best idea to use .
Thanks