7

I'm trying to optimize the hell out of a mobile app I'm working on, and I'd like to know what takes up the smallest memory footprint (I realize this may vary across browser):

  • object pointers
  • boolean literals
  • number literals
  • string literals

Which should theoretically take the least amount of memory space?

Bucket
  • 6,877
  • 9
  • 30
  • 41
user2800679
  • 488
  • 4
  • 20
  • 1
    My guess would be an empty string literal. – Niet the Dark Absol Sep 23 '13 at 23:26
  • Minifying your code is the best way for the hell to be optimized out of it. – Bucket Sep 23 '13 at 23:28
  • It's a boolean because those datatype only requires one bit in an ideal case http://en.wikipedia.org/wiki/Boolean_variable – Bernhard Sep 23 '13 at 23:28
  • 4
    @DesertIvy that is only for bandwidth optimization. – Daniel A. White Sep 23 '13 at 23:28
  • There's an answer to your question here: http://stackoverflow.com/questions/4905861/memory-usage-of-different-data-types-in-javascript – Charlie Sep 23 '13 at 23:30
  • 3
    'Optimize' is a relative term. Saving a few bytes on a literal won't save you much memory (unless you have a very large number of them), but will cost execution time if the interpreter has to coerce the value to some other form. –  Sep 23 '13 at 23:30
  • I have a very large number of them. – user2800679 Sep 23 '13 at 23:36
  • @DesertIvy If the code is going to be run under PhoneGap or a similar HTML-to-native platform, will minification make much difference? There's no code to be downloaded, in that case – Jay Sep 23 '13 at 23:39
  • 1
    Downvoted -3 times? It seems average JavaScript developer has little to no curiosity about the inner workings of the browser, wow. – user2800679 Sep 23 '13 at 23:41
  • 2
    I think downvoters should add comments to help the asker improve this question. It sounds promising, IMO, though maybe more specifics are needed. Which parts need to be optimized, for example? Has the asker profiled the application to determine that its definitely the memory footprint that's the problem? @user2800679 how do you know you need to make your objects smaller to improve your performance vs some other approach? What kind of app are you trying to build? – Jay Sep 23 '13 at 23:42
  • I'm not absolutely sure it's the memory footprint (I have profiled it and it is high), but I'd also just like to know... I'm curious if anyone knows or has a fair idea. – user2800679 Sep 23 '13 at 23:45

2 Answers2

17

On V8:

Boolean, number, string, null and void 0 literals take constant 4/8 bytes of memory to for the pointer or the immediate integer value embedded in the pointer. But there is no heap allocation for these at all as a string literal will just be internalized. Exception can be big integers or doubles which are boxed with 4/8 bytes for the box pointer and 12-16 bytes for the box. In optimized code local doubles can stay unboxed in registers or stack, or an array that always contains exclusively doubles will store them unboxed.

Consider the meat of the generated code for:

function weird(d) {
    var a = "foo";
    var b = "bar";
    var c = "quz";

    if( d ) {
        sideEffects(a, b, c);
    }
}

As you can see, the pointers to the strings are hard-coded and no allocation happens.

Object identities at minimum take 12/24 bytes for plain object, 16/32 bytes for array and 32/72 for function (+ ~30/60 bytes if context object needs to be allocated). You can only get away without heap allocation here if you run bleeding edge v8 and the identity doesn't escape into a function that cannot be inlined.

So for example:

function arr() {
    return [1,2,3]
}

The backing array for values 1,2,3 will be shared as a copy-on-write array by all arrays returned by the function but still unique identity object for each array needed to be allocated. See how complicated the generated code is. So even with this optimization, if you don't need unique identities for the arrays, just returning an array from upper scope will avoid allocation for the identity everytime the function is called:

var a = [1,2,3];
function arr() {
    return a;
}

Much simpler.

If you have memory problems with js without doing anything seemingly crazy, you are surely creating functions dynamically. Hoist all functions to a level where they don't need to be recreated. As you can see from above, merely the identity for a function is very fat already considering most code can get away with static functions by taking advantage of this.

So if you want to take anything from this, avoid non-IIFE closures if your goal is performance. Any benchmark that shows they are not a problem is a broken benchmark.

You might have intuition that what does additional memory usage matter when you have 8GB. Well it wouldn't matter in C. But in Javascript the memory doesn't just sit there, it is being traced by garbage collector. The more memory and objects that sits there, the worse performance.

Just consider running something like:

var l = 1024 * 1024 * 2
var a = new Array(l);

for( var i = 0, len = a.length; i < len; ++i ) {
    a[i] = function(){};
}

With --trace_gc --trace_gc_verbose --print_cumulative_gc_stat. Just look how much work was done for nothing.

Compare with static function:

var l = 1024 * 1024 * 2
var a = new Array(l);
var fn = function(){};

for( var i = 0, len = a.length; i < len; ++i ) {
    a[i] = fn;
}
Esailija
  • 130,716
  • 22
  • 250
  • 308
  • Awesome. This is exactly the kind of information I was hoping for; though, I have one question related to your last bit of code. If I were to call bind: a[i] = fn.bind(ref); (i realize bind is slow) Would this help with gc at all compared to: a[i] = function() {}; My guess is no, but if you have an idea please let me know. Anyway, thanks a lot for your informative answer. – user2800679 Sep 24 '13 at 16:28
  • @user2800679 no, `.bind` returns a new function every time except now you additionally also create a Context object, a FixedArray object and plenty of other crap (See https://github.com/v8/v8/blob/ea835a25d9e53c7f1104f019b18fd0bfc69f1d3b/src/v8natives.js#L1727 and https://github.com/v8/v8/blob/4d940b47fd46b1b4b1fcd50a2d34c74e1b320af4/src/runtime.cc#L8058). If you need functionality of bind, use a custom function instead of the built-in one: http://stackoverflow.com/a/18909668/995876. – Esailija Sep 24 '13 at 18:01
0

"Literal" means code (even if not in string serialisation), which is a more complex type and will therefore cost more space than values.

Theoretically, boolean values could take the least amount of space since they fit in a single bit. It's unlikely though that any engine does optimize this. If you want to force this, you can do it manually and juggle around with typed arrays.

However, performance is a practical thing, and you can only test, test, test it. As you already know, there is no definitive cross-browser cross-version answer.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164