What is the reason that executing forEach on an array in Javascript (V8) results in higher memory consumption compared to using a basic for loop?

Currently, I am conducting basic data validation on a large dataset using Node.js (version v7.5.0) with a matrix size of 15849x12771 entries. To maximize performance, the entire dataset is stored in memory. It is crucial for me to minimize memory consumption as much as possible, considering that each number uses 8 bytes in JavaScript.

I would like to compare two different methods to achieve the same goal:

Using forEach

 regressData.forEach((yxa, yxaIndex) => {
    yxa.forEach((yx, yxIndex) => {
      if (!_.isFinite(yx)) {
        throw new Error(`non-finite entry at [${yxaIndex}, ${yxIndex}]`);
      }
    });
  });

Despite consuming over 4GB of my node process' memory and struggling to complete the loop efficiently (possibly relying on slower swap memory), this method performs the desired validation.

The same validation can be achieved using a traditional for loop:

 for (var yxai = 0, yxal = regressData.length; yxai < yxal; yxai++) {
    const yx = regressData[yxai];
    for (var yxi = 0, yxl = yx.length; yxi < yxl; yxi++) {
      if (!_.isFinite(yx[yxi])) {
        throw new Error(`non-finite entry at [${yxai}, ${yxi}]`);
      }
    }
  }

This alternative approach drastically reduces extra memory consumption, enabling the validation process to be completed within seconds.

Is this behavior to be expected? I initially assumed that the closed scopes within the forEach method would prevent any additional memory usage compared to a more conventional for loop.

EDIT: Standalone Test

node --expose-gc test_foreach.js

if (!gc) throw new Error('please run node like node --expose-gc test_foreach.js');

const _ = require('lodash');

// prepare data to work with

const x = 15849;
const y = 12771;

let regressData = new Array(x);
for (var i = 0; i < x; i++) {
  regressData[i] = new Array(y);
  for (var j = 0; j < y; j++) {
    regressData[i][j] = _.random(true);
  }
}

// for loop
gc();
const mb_pre_for = _.round(process.memoryUsage().heapUsed / 1024 / 1024, 2);
console.log(`Memory consumption before the for loop ${mb_pre_for} megabytes`);
validateFor(regressData);
gc();
const mb_post_for = _.round(process.memoryUsage().heapUsed / 1024 / 1024, 2);
const mb_for = _.round(mb_post_for - mb_pre_for, 2);
console.log(`Memory consumption caused by the for loop ${mb_for} megabytes`);

// forEach loop
gc();
const mb_pre_foreach = _.round(process.memoryUsage().heapUsed / 1024 / 1024, 2);
console.log(`Memory consumption before the forEach loop ${mb_pre_foreach} megabytes`);
validateForEach(regressData);
gc();
const mb_post_foreach = _.round(process.memoryUsage().heapUsed / 1024 / 1024, 2);
const mb_foreach = _.round(mb_post_foreach - mb_pre_foreach, 2);
console.log(`Memory consumption caused by the forEach loop ${mb_foreach} megabytes`);

function validateFor(regressData) {
  for (var yxai = 0, yxal = regressData.length; yxai < yxal; yxai++) {
    const yx = regressData[yxai];
    for (var yxi = 0, yxl = yx.length; yxi < yxl; yxi++) {
      if (!_.isFinite(yx[yxi])) {
        throw new Error(`Non-finite entry at [${yxai}, ${yxi}]`);
      }
    }
  }
};

function validateForEach(regressData) {
  regressData.forEach((yxa, yxaIndex) => {
    yxa.forEach((yx, yxIndex) => {
      if (!_.isFinite(yx)) {
        throw new Error(`Non-finite entry at [${yxaIndex}, ${yxIndex}]`);
      }
    });
  });
};

Output:

toms-mbp-2:mem_test tommedema$ node --expose-gc test_foreach.js
Memory consumption before the for loop 1549.31 megabytes
Memory consumption caused by the for loop 0.31 megabytes
Memory consumption before the forEach loop 1549.66 megabytes
Memory consumption caused by the forEach loop 3087.9 megabytes

Answer №1

Update for the year 2022: This answer is now outdated.
The mentioned "new execution pipeline" has been active for quite some time now.

For those still using Node from 2017, the original post is preserved below:


(I'm a developer at V8.) The issue with Array.forEach in V8's old execution pipeline (full codegen + Crankshaft) causes changes to an array's internal representation leading to less memory efficiency under certain conditions. It's a bit complex and involves optimizations based on the types of elements present in the array and how hot the code runs.

Fortunately, with the upcoming new execution pipeline (currently accessible via the --future flag and soon to be default), this extra memory consumption should no longer occur.

(Although for loops may offer better performance than forEach due to fewer complexities as per the ES spec, the difference might not always be significant in real-world scenarios. For optimized performance where each CPU cycle matters, sticking to traditional

for (var i = 0; i < array.length; i++)
loops is recommended.)

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

How can I allow scrolling within a specific div element?

Here is the structure of my layout: <html> <head> stuff goes here </head> <body> <div id="master"> <div id="toolbar"> <input type="text" id="foo"> </div> <div id="c ...

What could be the reason for Next.js failing to retrieve client-side data following TypeScript validation and submission to the backend?

I am new to programming and encountering an issue with fetching data from MongoDB in my Next.js application. The client side appears to be working fine, and I have thoroughly reviewed the console logs and schema validation. Furthermore, I have implemented ...

Issue with AngularJS: $compile function is not functioning as expected

In this particular code snippet, I am encountering an issue with the $compile function. What I am trying to accomplish is taking an item, adding it to the scope, and then compiling it to generate HTML. $http({ method: 'GET', ...

Tips for eliminating choices upon button press

How do I remove nested options inside a select element upon button click? I'm not sure where my mistake is, could it be in the for loop? var select = document.getElementById('colorSelect'); var options = document.getElementsByTagName(&ap ...

"Exploring the power of Node.js Virtual Machines and the magic of

I'm currently facing a challenge with reading and extracting data from a dynamically generated HTML file that contains a commented out JavaScript object. My goal is to retrieve this object as a string and execute it using VM's runInNewContext(). ...

Comparing data in Knockout js

I'm trying to compare the values of the variable name by id, but I'm not sure how to do it. Here is what I have so far: if (this.name == self.copy().indexOf(this.id).name) { alert('equals'); } else { alert('not equals&apo ...

Issue encountered while attempting to start npm: Error code from NPM

I keep encountering the npm ENOENT error every time I attempt to execute npm start. I am unsure of what steps to take in order to resolve this issue. I have made efforts to adjust folder permissions. bryantcaruthers-> npm start npm ERR! code ENOENT npm ...

Creating dynamic components in ReactJS allows for versatile and customizable user interfaces. By passing

Within the DataGridCell component, I am trying to implement a feature that allows me to specify which component should be used to render the content of the cell. Additionally, I need to pass props into this component. Below is my simplified attempt at achi ...

Tips for customizing the border of an outlined TextField in MUI

Below is the current configuration of a TextField component: const styles = { resize: { fontSize: '50px', } } const textField = (props) => { const { classes } = props; return ( <TextField valu ...

Transforming a byte[] BGRA array into a more efficient and faster option (JAVA)

I'm working with a byte[] array that holds BGRA raster data, where each component is represented by a different index in the array (e.g. first byte for blue, second for green). I want to manipulate this data, but I'm curious if there's a Jav ...

Building a customizable grid using asp.net to display data from a SQL database and enabling users to edit

I'm currently working on a new project that involves dynamically writing an SQL table into an asp:gridview for editable purposes. I've come across some solutions, but they all seem to be static, and I need a dynamic solution due to the changing n ...

Guide to implementing if statements within a map function in JavaScript/Vue.js

Currently, I am developing a small application using Vuejs. In this application, I receive response data and then map it to a variable. However, there are some elements with empty arrays in the data. So, during mapping, I need to check a condition and ma ...

How does a JS event impact the state transition in a backend state machine built on Rails?

I am currently developing a new project where the backend involves Users who have multiple "Courses" each containing multiple "Steps". My goal is to create a JavaScript function that validates the user's answer. For example, if the user inputs "6", w ...

Comparing SHA and Python hashlib outputs show discrepancies with identical inputs

These code snippets both use Nodejs and Python to calculate a hash from the same input content, however they seem to be generating different results which is quite puzzling. // npm install jssha const jssha = require("jssha"); var s = new jssha(& ...

Guide on setting up haproxy as a reverse proxy for socket.io with SSL to avoid mixed content warnings

Over time, I've incorporated this configuration to integrate haproxy with node.js and socket.io. Highlighted sections from the haproxy setup: frontend wwws bind 0.0.0.0:443 ssl crt /etc/haproxy/ovee.pem timeout client 1h default_backend ...

Having trouble retrieving the table value from an HTML document?

I am trying to retrieve specific information from this source: https://i.sstatic.net/g1wDj.png This information is crucial for fetching data from a database using a primary key. However, extracting this value has proven to be quite challenging. Upon docu ...

locate sibling element

<div class="videoItem"> <div class="innerVideoItem"> <a><div class="overlayBg"></div></a> <a><img class="overlayPlay"><img></a> </div> </div> <script> ...

"Step-by-step guide on setting up routing in React using Redux for navigating to and from the root file (index

I am working on a React server webpage and I am trying to set up redirects from index.js (localhost:3000) to the Login page (localhost:3000/login), and from login back to index in case of a failed login attempt. Can you help me with what code I need to inc ...

Display a text box upon clicking an item

I've been curious about how to create the effect of a text box or div that appears when clicked on, similar to what you see in this. Scroll down and click on "show patchnotes"; I've been puzzled by this for a while but haven't come across a ...

Issues with Bootstrap tabs not updating content

I have been working on incorporating bootstrap tabs into one of our pages. I followed the example provided at https://getbootstrap.com/docs/4.0/components/navs/#javascript-behavior, but unfortunately, the tabs are not functioning correctly (they appear as ...