memory consumption of UUID strings.

Description of the problem

UUID generation in Math.js produces strings stored as concatenated strings in Firefox and Chrome.

On Chrome 40bytes are used for each component of the final string, creating heap overhead. In my application with thousands of objects this adds up.

For example, Heap size using Chrome

r89 as is: 95MB
r89 with uuid flattened with String().toUpper(): 66MB.

I see similar results using Firefox.

Three.js version
  • Dev
  • [x ] r89
Browser
  • All of them
  • [x ] Chrome
  • Firefox
  • Internet Explorer
OS
  • All of them
  • Windows
  • macOS
  • Linux
  • Android
  • iOS
Hardware Requirements (graphics card, VR Device, …)

Author: Fantashit

5 thoughts on “memory consumption of UUID strings.

  1. .toUpperCase() technique seems very interesting.

    I evaluated memory consumption with the following code.

    <html>
    <head>
    </head>
    <body>
    <script src="https://rawgit.com/mrdoob/three.js/r89/build/three.js"></script>
    <script>
    var num = 0x10000;
    
    var array = Array( num );
    
    for ( var i = 0; i < num; i ++ ) {
    
        //array[ i ] = THREE.Math.generateUUID().toUpperCase();
        array[ i ] = THREE.Math.generateUUID();
    
    }
    </script>
    </body>
    </html>
    

    600 Bytes per uuid with THREE.Math.generateUUID() while 64 Bytes per uuid with THREE.Math.generateUUID().toUpperCase() on my Windows10 + Chrome.

    This tech reduces the uuid memory consumption 8x. 64 Bytes per 36 chars sounds very reasonable.


    detail:

    5.1MB for Three.js itself + Array on heap. (heap where I comment out the loop)
    42.6MB with THREE.Math.generateUUID() on heap, 600B (= (42.6MB – 5.1MB) / 0x10000) per uuid
    9.1MB with THREE.Math.generateUUID().toUpperCase() on heap, 64B (= (9.1MB – 5.1MB) / 0x10000) per uuid

  2. As @aardgoose this’s JavaScript strings memory layout topic.

    I’m not so familiar with that, but the following is my rough understanding.

    For example, uuid “D30D4BC2-4BAE-49FF-8A3F-3F9A4467A0FA” layout in low-layer is represented as linked list, like

    // more precisely, not the raw string but pointer to LUT element of generateUUID()?
    'D3' -> '0D' -> '4B' -> ...
    

    because of generateUUID() logic, concatenating strings of LUT elements.

    https://github.com/mrdoob/three.js/blob/dev/src/math/Math.js#L11

    And seems like .toUpper/LowerCase() allocates new consecutive memory space “D30D4BC2-4BAE-49FF-8A3F-3F9A4467A0FA”. Therefore, memory consumption will be reduced by removing overheads.

    Even if the two strings looks the same in the JS code layer, the memory layout can be different in low-layer. .toUpper/LowerCase() tech optimizes it.

    I hope JS engine optimizes well and we won’t need this hacky tech in the future.

  3. @makc I have a bit of a counter to what you have written:
    https://jsfiddle.net/Travnik/3guqr5va/21/

    numbers:
    generateUUID: 347.219970703125ms <- base
    generateUUID2: 1697.90185546875ms <- your version
    generateUUID3: 158.04296875ms <- my version, using 16 byte array instead of your 36

    The main problem is in materializing the string. Actual generation is pretty quick and compact.

    [update]
    for purists, I added example without UUID object wrapper, just using Uint8Array:
    generateUUID4: 141.053955078125ms

  4. @mrdoob Probably no perfect solution but there may be better situation. I’m gonna follow up the discussion and try some ideas soon. In my mind, there’re two options now. Using “Number tech” mentioned in #13094 or reverting generateUUID. (#13077 would also work?)

    IMO huge memory consumption can lead slow performance, we should fix.

Comments are closed.