1

I have been thinking about this for a few days trying to see if there is a generic way to write this function so that you don't ever need to worry about it breaking again. That is, it is as robust as it can be, and it can support using up all of the memory efficiently and effectively (in JavaScript).

So the question is about a basic thing. Often times when you create objects in JavaScript of a certain type, you might give them an ID. In the browser, for example with virtual DOM elements, you might just give them a globally unique ID (GUID) and set it to an incrementing integer.

GUID = 1

let a = createNode() // { id: 1 }
let b = createNode() // { id: 2 }
let c = createNode() // { id: 3 }

function createNode() {
  return { id: GUID++ }
}

But what happens when you run out of integers? Number.MAX_SAFE_INTEGER == 2⁵³ - 1. That is obviously a very large number: 9,007,199,254,740,991 quadrillions perhaps. Many billions of billions. But if JS can reach 10 million ops per second lets say in a pick of the hat way, then that is about 900,719,925s to reach that number, or 10416 days, or about 30 years. So in this case if you left your computer running for 30 years, it would eventually run out of incrementing IDs. This would be a hard bug to find!!!

If you parallelized the generation of the IDs, then you could more realistically (more quickly) run out of the incremented integers. Assuming you don't want to use a GUID scheme.

Given the memory limits of computers, you can only create a certain number of objects. In JS you probably can't create more than a few billion.

But my question is, as a theoretical exercise, how can you solve this problem of generating the incremented integers such that if you got up to Number.MAX_SAFE_INTEGER, you would cycle back from the beginning, yet not use the potentially billions (or just millions) that you already have "live and bound". What sort of scheme would you have to use to make it so you could simply cycle through the integers and always know you have a free one available?

function getNextID() {
  if (i++ > Number.MAX_SAFE_INTEGER) {
    return i = 0
  } else {
    return i
  }
}

Random notes:

The fastest overall was Chrome 11 (under 2 sec per billion iterations, or at most 4 CPU cycles per iteration); the slowest was IE8 (about 55 sec per billion iterations, or over 100 CPU cycles per iteration).


Basically, this question stems from the fact that our typical "practical" solutions will break in the super-edge case of running into Number.MAX_SAFE_INTEGER, which is very hard to test. I would like to know some ways where you could solve for that, without just erroring out in some way.

Lance
  • 75,200
  • 93
  • 289
  • 503
  • Why not just use GUIDs? The chance of a collision is beyond astronomically low. – VLAZ Aug 27 '19 at 07:52
  • Because GUIDs are slower to generate than incremented IDs for particle systems and high-intensity games. – Lance Aug 27 '19 at 07:53
  • Ah, gotcha. I wasn't sure there was this limitation. – VLAZ Aug 27 '19 at 07:54
  • I'd use strings instead of numbers. – CertainPerformance Aug 27 '19 at 08:26
  • @CertainPerformance that just pushes the problem off to a further larger set. – Lance Aug 27 '19 at 09:09
  • @lancePollard right, then you have 2 ** 16 ** (2 ** 32 - 1) possible ids .... thats enough to uniquely identify *every atom in this universe*. Your usecase, please. – Jonas Wilms Aug 27 '19 at 09:13
  • It's a *much, much* larger set, though. Javascript strings can contain [*many* characters](https://stackoverflow.com/questions/34957890/javascript-string-size-limit-256-mb-for-me-is-it-the-same-for-all-browsers) (~1 billion on my Chrome, at least) - compare that to `Number.MAX_SAFE_INTEGER`, or `9007199254740991`, which is only 16 characters long. That can't even be said to be an order-of-magnitude increase, it's a whole different ballpark. – CertainPerformance Aug 27 '19 at 09:13

1 Answers1

0

But what happens when you run out of integers?

You won't. Ever.

But if JS can reach 10 million ops per second [it'll take] about 30 years.

Not much to add. No computer will run for 30 years on the same program. Also in this very contrived example you only generate ids. In a realistic calculation you might spend 1/10000 of the time to generate ids, so the 30 years turn into 300000 years.

how can you solve this problem of generating the incremented integers such that if you got up to Number.MAX_SAFE_INTEGER, you would cycle back from the beginning,

If you "cycle back from the beginning", they won't be "incremental" anymore. One of your requirements cannot be fullfilled.

If you parallelized the generation of the IDs, then you could more realistically (more quickly) run out of the incremented integers.

No. For the ids to be strictly incremental, you have to share a counter between these parallelized agents. And access to shared memory is only possible through synchronization, so that won't be faster at all.

If you still really think that you'll run out of 52bit, use BigInts. Or Symbols, depending on your usecase.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151