TypedArray Performance Comparison with Loop Unrolling in Javascript

When I set out to create my own WebGL 3D library for learning purposes, I followed documentation from various sources claiming that the set() function for Float32Array was touted as being "as fast as" memcpy in C and the fastest option according to html5rocks. However, upon testing, it appeared that this function was actually much slower compared to other techniques.

Upon further investigation, I looked into glMatrix, where the author had optimized their library by unrolling loops for speed. Even though this is a common practice for maximizing performance, I still believed that utilizing the set() function would provide me with the best results since I intended to work exclusively with TypedArray types.

To put my theory to the test, I created a jsperf benchmark. Surprisingly, not only did the set() function turn out to be significantly slower, but every other method I experimented with surpassed it in terms of speed. This led me to question why this supposedly efficient function was performing so poorly.

Ultimately, my question remains: Why is the set() function underperforming? Could it be an issue with my code, my browser (Firefox 24), or am I overlooking some key optimization strategy? Any insights or explanations on this unexpected outcome would greatly benefit my understanding.

Answer №1

Although dated, utilizing TypedArrays can be beneficial if aiming to optimize underperforming code. It is essential to grasp that TypedArray objects in JavaScript act as views representing a specific range of bytes within an ArrayBuffer. While the underlying ArrayBuffer holds the consecutive block of binary data for manipulation, using a view enables access and alteration of a window of this data.

Multiple distinct (and potentially overlapping) ranges in the same ArrayBuffer may be observed through various TypedArray objects. When two TypedArray objects share an ArrayBuffer, the set operation performs exceptionally quickly due to the contiguous memory allocation.

For instance, let's consider creating an ArrayBuffer of 32 bytes with two length-16 Uint8Array arrays representing separate halves:

var buffer = new ArrayBuffer(32);
var array1 = new Uint8Array(buffer,  0, 16);
var array2 = new Uint8Array(buffer, 16, 16);

Values can now be initialized 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]

A highly efficient transfer of these 8 bytes from the first half to the second half of the buffer can be achieved:

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]

It can be verified that both arrays share the same buffer by examining it via another view, like a length-8 Uint32Array covering the entire 32 bytes:

var array3 = new Uint32Array(buffer)
console.log(array3); // [50462976, 117835012, 185207048, 252579084, 
                     //  50462976, 117835012, 185207048, 252579084]

A performance test demonstrates a significant enhancement in copy operations on the same buffer compared to traditional methods:

http://jsperf.com/typedarray-set-vs-loop/3

The performance boost is substantial on browsers like Chrome and Firefox, outstripping the efficiency of duplicating the initial half into the latter half using regular arrays. However, the tradeoff between cycles and memory must be considered. As long as there is a reference to any single view of an ArrayBuffer, the remaining buffer data cannot undergo garbage collection. The proposed ES7 Harmony function ArrayBuffer.transfer addresses this issue by enabling explicit memory release without relying on garbage collection, along with dynamic expansion capabilities for ArrayBuffers without mandatory copying.

Answer №2

When it comes to the `set` function, its semantics are not as straightforward in V8. After some careful consideration and analysis (as seen here), it ultimately ends up executing a similar loop to what other methods are doing directly in the first place (as shown here).

It's important to remember that JavaScript gets compiled into highly optimized machine code when done correctly (as demonstrated by all the tests). Therefore, there's no need to excessively praise certain methods simply because they are considered "native".

Answer №3

In my recent investigations, I've delved into the performance of set() and found that for smaller blocks (like the 16 indices utilized by the original poster), set() is still approximately 5 times slower than a comparable unrolled loop – even when working on a continuous block of memory.

I ran some modifications to the initial jsperf test here. From what I observed, it's safe to conclude that for minor block transfers such as these, set() falls short in comparison to the efficiency of unrolled index assignments. On the other hand, with larger block transfers (as highlighted in sbking's trial), set() shows better performance. However, when pitted against a daunting one million array index operations, it seems unreasonable not to be able to outperform them with just a single instruction.

Through testing, I noticed that using a contiguous buffer with set() yields slightly improved results compared to utilizing a separate buffer with set(). Nevertheless, the advantage in performance at this transfer size remains negligible.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Troubles with setting up node modules in a Windows 10 environment

I'm encountering difficulties when trying to install modules on my Windows 10 laptop after setting up Node.js from scratch. Since it's my personal computer, I have complete control over the system. Despite searching through various online forum ...

Is there a way to determine if a user has already been timed out in Discord?

I'm in the process of developing a custom timeout command for my bot, as I believe it may offer greater functionality than the pre-existing feature. However, I am encountering an issue with determining whether a user is currently timed out or not. It ...

The 'disabled' property is not found in the 'MatButton' type, however, it is necessary in the 'CanDisable' type

Issue found in node_modules/@angular/material/core/option/optgroup.d.ts: Line 17: Class '_MatOptgroupBase' does not correctly implement interface 'CanDisable'. The property 'disabled' is missing in type '_MatOptgroupBas ...

Jupyter notebook does not support the visualization of D3 graphics

My code in html/javascript/d3 within jupyter notebook is running without errors, but no circles are being displayed. The expected output should show two circles, one blue and the other green. What could be causing this issue? HTML('<script src="ht ...

Adding elements to an array using JavaScript

What is the correct way to add new values to an array like this? json = {"awesome":"45.22","supercool":"9876"} I attempted json.push("amazingness":"45.22");, but unfortunately it was not successful. ...

Generate an array of checked inputs to be used when posting to a REST API

I have been using .push() to create a checked array of "List" inputs for posting to a REST API. However, it doesn't seem to be working correctly. When unchecking an item, it is not automatically removed from the array. Does anyone have a better solut ...

Achieving asynchronous results in the parent function with TypeScript: a guide

The code structure provided is as follows: import {socket} from './socket'; class A{ Execute(...args[]){ //logic with Promises SomeAsyncMethod1().then(fulfilled1); function fulfilled1(){ SomeAsyncMethod2(args).then(fulfilled2); ...

JavaScript function to display the anchor tag's title attribute in a modal when clicked

I am curious if you can assist me in finding the right path here. I have a PHP code that I would like to use onclick on dynamically created links to show the link's title in a modal window with a close option, similar to a tooltip. I prefer not to use ...

Executing an asynchronous action without linking promises to subsequent actions

Encountered a challenge while using componentWillReceiveProps with redux async action. Below is the code snippet along with an explanation: componentWillReceiveProps(nextProps) { if(nextProps.job.status === 'applied'){ this.showAppliedDial ...

Capturing user-selected option from dropdown menu using npm

I am a beginner when it comes to node.js and async programming. My current project involves creating a program that shuffles a Spotify playlist. I have managed to store the user's playlists in an array for easier access. My goal is to present this ar ...

Guide to setting up a datePicker in a cellEditorSelector

In my grid, I want to have different editors for different rows within the same column. The ag-Grid documentation provides information on how to achieve this using colDef.cellEditorSelector as a function: link I have successfully created unique editors fo ...

What makes jQuery objects inherently one-of-a-kind?

After researching jQuery objects on this website, it was mentioned that each jQuery object is distinct, even when created with the same selector or containing references to the exact same DOM elements. For instance, the comparison below would result in fa ...

"Stellar.js fails to function properly when applied to elements loaded dynamically through AJAX requests

I recently implemented the amazing Stellar.js for parallax effects in a project of mine. However, I've encountered an issue: Stellar.js does not recognize when I update content via AJAX (specifically loading new div elements from an HTML file and rep ...

What is the reason that textContent assignment does not function properly when used with HTML tags?

I have been attempting to insert additional text into an existing element using the textContent property. However, when I use the += operator for addition assignment, it seems to strip away the formatting of pre-existing tags like b. I expected that the ne ...

Utilize esbuild to monitor for any modifications, recompile the code, and automatically restart the express server

In my endeavor to develop a basic SSR-powered project using express + react, I find the need to monitor frontend and backend scripts concurrently in the development process. The primary objective is to utilize express routes in directing to react page com ...

Constantly retrieving AngularJS JSON data on the details page

Simply put, I have developed a 2-page AngularJS application because I am integrating it into CMS templates and a single page app would not work well with the CMS widgets in the sidebar. On my results page, I am pulling data from 3 different JSON files usi ...

Implementing a Comment Section on Your Website with HTML and JavaScript

Currently in the process of setting up my static webpage, and looking to incorporate user comments. I have already written the comment script using HTML, but I am unsure how to write the JavaScript necessary to display the comments beneath each post. Any ...

Update button text in real-time using JavaScript

I am currently working on a dropdown list that contains 5 elements displayed inside a button when pressed. I want the button to show a default text "Filter by:" before displaying the selected tab value. Additionally, I need the caret symbol to be visible. ...

Role Based Routing in React allows for different paths and components

I am currently working on a project involving React and I need to implement different routes for admin and driver roles. I have two separate route objects for each role's claims. I am retrieving the user's role from an API and I want to display t ...

What is the best way to run JavaScript using nodriver in Python?

I'm currently facing some roadblocks while writing Python code. To elaborate, I've successfully logged into a site protected by Cloudflare using nodriver, and have been able to display the values of elements on the page. However, my ultimate obje ...