3

Yesterday I asked this question on how to iterate over a Map without allocating. A v8 developer replied with the following:

But if you're only interested in processing the values anyway, you can avoid it being created by iterating over just the values: for (let v of map.values()) {...} does not allocate any short-lived objects. The same is true for iterating over map.keys().

I'm trying to replicate this in a test but I'm having trouble. Here's a demo I made that reproduces the problem I'm having with a simple Set. Paste the following code in an empty HTML file in its script tags:

let a = new Set([ 1, 2, 3, 4, 5 ]);
let b = [ 1, 2, 3, 4, 5 ];

let sum = 0;

function setIterate() {
  for (let item of a.values()) {
    sum += item;
  }
}

function arrayIterate() {
  for (let i = 0; i < b.length; i++) {
    sum += b[i];
  }
}

setInterval(setIterate, 1);
// setInterval(arrayIterate, 1);

If you open this HTML file and then hit Shift+Escape it should open the Chrome Task Manager. If you then look at the Javascript Memory column, you'll see the memory slowly growing over time.

However, if you comment the line setInterval(setIterate, 1); and uncomment the line setInterval(arrayIterate, 1); and refresh the page you'll see that the memory usage stays constant.

That is, iterating over a Set produces garbage build-up over-time while iterating over an array does not.

Is there any way to iterate a Set without producing this garbage build-up over time? I'm developing a browser game and iterating over many Sets and Maps in my render loop and getting occasional frame-spikes due to the GC running.

Ryan Peschel
  • 11,087
  • 19
  • 74
  • 136
  • Can't reproduce, Chrome version 92. – Veedrac May 15 '21 at 00:36
  • Hmm, are you sure you're looking at the correct column? Make sure you're looking at `Javascript memory` column and not the `Memory footprint` column. Also, which operating system are you on? Maybe that has an effect? I'm on Windows 10. And I'm using Chrome 91 which I thought was the latest? I even tried downloading Chrome beta and there's still the same issue. – Ryan Peschel May 15 '21 at 03:48
  • I tried using Chrome Dev version (92) and it seems to still happen on there too. – Ryan Peschel May 15 '21 at 03:54
  • 1
    Oh, you're using the *Task Manager*. I was using the dev tools. The Performance tab will show heap growth if you run a profile (let it run for a while), and there is no sawtooth reminiscent of allocating code. – Veedrac May 15 '21 at 04:45
  • Isn't that just because it garbage collects every couple seconds so it's hard to tell? Because in this example it's very minimal garbage so it'd be very hard to tell since the difference is so small. – Ryan Peschel May 15 '21 at 05:09
  • In any case, why does the Task Manager show memory increasing over time (before being GC'd every couple seconds) in the `Set` example but not with the `Array` example? – Ryan Peschel May 15 '21 at 05:16

1 Answers1

4

Why does iterating over a Set's values allocate and create garbage?

It doesn't. Doing a million calls to setIterate with --trace-gc (in d8 or node) shows zero activity. If the function allocated even a single byte per iteration (which it can't; the minimum allocation granularity is two pointers, i.e. 8 bytes), then the young generation would have filled up at least once in that time.

Is there any way to iterate a Set without producing this garbage build-up over time?

Absolutely; for example how you're doing it.

why does the Task Manager show memory increasing over time?

I don't see it doing that. I've copy-pasted your snippet exactly as-is and let it sit for a couple of minutes; the "JavaScript memory" column of Chrome's task manager for that tab hasn't budged by a single kilobyte. (Which actually only proves that this test isn't really a stress test: it will start allocating like crazy once sum exceeds 1<<30, but that's not because of the set or the array...)


Wild guess: maybe you have some extension installed that injects code into every page and causes allocations? At any rate, whatever it is, it's not the for..of-loops over Sets.

jmrk
  • 34,271
  • 7
  • 59
  • 74
  • I don't think it's any extension, as I have no other programs running, no other tabs open, and no extensions enabled. I think maybe it's just a bug with Chrome? I notice it only happens when I refresh the page. That is, if I restart Chrome and then open the page, there are no allocations, but if I then hard refresh the page and then look in the Task Manager, then it shows allocations growing over time. Hmm.. In any case, I'll trust you that it isn't allocating. – Ryan Peschel May 16 '21 at 01:10