3 ms·
Aren't most of these “optimizations” things that a decent programming language implementation should automatically do for you in 2016? (Lambda lifting, using ef
by catnaroek 10y ago
Aren't most of these “optimizations” things that a decent programming language implementation should automatically do for you in 2016? (Lambda lifting, using efficient representations for variant types, etc.) Rolling them by hand is awfully low-level, and completely defeats the point to using a high-level language.
---
I can't properly reply to your replies, because “I'm submitting too fast”, so I'l reply here.
@leppr: I'm not denying the importance of these optimizations. I'm saying that they're being performed by the wrong programmer, or rather, by a programmer performing the wrong role (language user, rather than language implementor).
@pizlonator:
> Avoiding allocations is generally good advice. Compilers have a hard time eliminating them
The most effective way to avoid allocations is to make the language value-oriented (where values are merely represented in physical memory, but not identified with those representations) rather than object-oriented (where the physical identity of every object matters). An implementation of a value-oriented language is free to store the entire representation of an arbitrarily large compound value in a single memory block. By comparison, the gross amount of micromanagement shown in the article is a lot of effort, whose benefits have only limited local significance.
> Using bitfields is a classic trick that is harder to do for the implementation than it is for the user.
Then provide an abstract bitfield type as a library, and let that type be used by everyone else as if it were a primitive type.
> Finally, I love the no-op function trick. This is not a low-level optimization at all!
It is. What you really want here is an abstract type of lazy suspensions. Then, again, that type can be used by everyone else, without caring about its internal implementation.
@macspoofing
Wholeheartedly agree. I have no idea why you're being downvoted.
- macspoofing 10y agoEven if it doesn't, how much time is this actually saving? If you're doing heavy work on every frame ... maybe (and you'll have to profile to figure out if it's worth). If you're handling a user input event, or some IO event (like in the author's example) it makes no difference at all. It'll confuse some developer (possibly a junior or co-op tasked with clearing the bug queue) down the road, and probably will lead to a bug.
- leppr 10y ago>Rolling them by hand is awfully low-level, and completely defeats the point to using a high-level language. The problem is that you can't have "compiler/interpreter plugins" on the web, you have to use the language itself in order to extend it. This isn't React, Angular or even jQuery we're talking about, it's a library implementing a "fundamental" language feature that may be used tens of thousands of times in an app. This is why these kind of extremely low level optimizations matter for the makers of such a low-level library.
- pizlonator 10y agoI don't think this article is rolling anything too low-level by hand. Avoiding allocations is generally good advice. Compilers have a hard time eliminating them for you and so you can't be guaranteed that the compiler will eliminate a pointless optimization. Any allocation that isn't eliminated brings you one step closer to a GC, and it makes sense to optimize GCs away. Using bitfields is a classic trick that is harder to do for the implementation than it is for the user. If an implementation wanted to turn a bunch of boolean fields into a bitfield, it would have to first prove that they are all boolean, then it would have to recompile all accesses to those fields into bitfield accesses, and then it would have to walk the whole heap to make sure that there weren't any more objects that used the old shape (separate fields) instead of the new one (bitfield). You could skip the last step if boolean fields were always eagerly represented as bitfields (which would surely slow down start-up time, something that JS implementations don't want to do because page loading is so important) or if you were happy to live with objects having both representations simultaneously (which would slow down every access). I'm guessing that a good implementation would wait for the next GC after realizing that a cluster of fields is proved boolean, and then it would reshape those objects while GCing. So, it would be one of those "maybe you'll get it if you're lucky" optimizations that make you look good on benchmarks but won't really help developers. Even if an implementation did have any version of this trick - even one that worked eagerly, there are so many parts to the proof burden for the optimization that the smart play for users would still be to do the optimization themselves rather than hope that the compiler gets it right. Finally, I love the no-op function trick. This is not a low-level optimization at all! It's just a simple and clever way to structure your code that results in you getting to hook into the JS implementation's existing watchpoint optimizations: JS VMs will certainly set a watchpoint on a global function pointer that has been immutable so far, and they will definitely inline calls to that function via that global pointer subject to that watchpoint. So, by doing something that looks perfectly clean at a high level you are getting some really gnarly optimizations for free.
- yxhuvud 10y agoI don't understand how value oriented vs object oriented matter considering the allocations in question was functions. Functions are hard to optimize away due to the nature of functions being closures with pointers to the defining scope. Can you please elaborate what you mean?
- catnaroek 10y agoIf the identity of a function object doesn't matter, then a compiler can automatically lambda-lift inner functions that don't capture variables, without introducing new arguments.