> Don't allocate when you need fast performance. This is good practice regardless of whether you are using a GC since allocation/deallocation of memory are slow operations (in fact game programmers NEVER allocate during game level execution).
> This isn't really hard, you just make sure that while you are performing an animation or within a game level you don't make any allocations. The GC is unlikely to kick in and your performance will be predictable and fast. ARC on the other hand doesn't allow you to do that since ARC instantly deallocates an object you finished working with.
I don't understand this. All the strategies one might use to avoid allocations in performance-critical code under GC (pooling resources, or whatever) are also available under ARC.
The difference is that if the way I use memory causes performance issues without GC, it will be much more reliably reproducible than with GC.
That's true. The article however, does state that a GC stall is bad but explains it can be avoided with a good GC and a good programmer.
With ARC you get more consistent performance but the article was going after the claim that GC inherently require 5 times more RAM to be reasonably performant.
Wouldn't cleanup in an ARC setup be more (or just as much) dependent on de-allocations (or actually reference loss) than allocations, while in a GC environment a freeup (albeit less predictable and of higher impact) is mostly triggered by deallocation? At least that's what I understood from the article.
So I don't know much about anything, but I'm confused on this point. You say "This effectively means it always performs late binding and invoking a message is REALLY slow in Objective-C." But I can't find any evidence of Objective-C performing late binding or in documentation [1][2][3][4].
The original article seemed to have a mistake there, it now says dynamic binding. This is indeed much slower than regular invocation as the first article you linked to states.
"Late binding" is a generalized term for associating a symbol with a value/code at run time, instead of compile/link time. (even if the term is not literally used in the obj-C docs)
> Objective-C doesn't use methods like Java/C++/C#, it uses messages like Smalltalk. This effectively means it always performs late binding and invoking a message is REALLY slow in Objective-C.
Well, "always" is a little wrong. The first time a message is passed and the bound method is called by the runtime is significantly slower (~4x slower than a virtual method call in C++). But then this message/method pair gets cached and for every subsequent call the cache is used.
Then there's some real neat trickery that is performed in objc_msgSend() ... after the method to "call" has been found the code directly jumps into that method without creating a new stack frame. (All needed arguments have been passed to objc_msgSend() and are already on the stack.) objc_msgSend() in essence is a trampoline.
So when sending a message to an object multiple times you only pay for the cache lookup - which is pretty fast by itself and a cached call is faster than a C++ virtual method call.
It really isn't that big of a deal. Trampolining is pretty straight forward and a caching of memory addresses isn't a big deal either. So I don't know why Apple wouldn't add this optimization to the iOS obj-c runtime.
Trampolining is harder to do if you keep around this absurdity (sorry "security feature") of not being able to mark pages as executable. If they open this up for the objc runtime then any other code in the same process would be able to do it too.
You're assuming writable trampolines. objc_msgSend()'s trampoline works by caching the target lookup and having an extremely short fast-path in the case that the target method implementation is cached.
objc_msgSend is a trampoline in the sense that it leaves the frame/registers untouched and jumps straight to the target implementation.
Though, while Java can, it looks like Dalvik generally does not (though, most of the info available seems to be 2.3-era; possibly things have improves since). It looks like 2.3 Dalvik can inline getters and setters, but it's certainly no Hotspot.
Dalvik is designed around criteria that are nearly diametrically opposed to those for Hotspot. Hotspot goes for maximum performance. Dalvik's JIT compiler is designed for maximum impact on performance with minimum computation. In other words, Dalvik's JIT is designed for battery powered devices.
Oh, sure, I absolutely realise that a very aggressive JIT wouldn't be ideal for Dalvik. However, given that the article is talking about how wonderful JIT is, it perhaps concentrates a little too much on all the wonderful JIT things that Dalvik (the only mobile JIT of serious interest to most developers) does not do.
Depends on what you mean by 'Hotspot'. Both of Sun's embedded JVMs (CDC, CLDC) had 'Hotspot' implementations. Both of these implementations performed many of these types of optimizations (which I can say with certainty because I wrote many of them). I'm no Dalvik expert, but there really isn't any reason why it shouldn't be able to do similar optimizations. Remember that the CDC and CLDC Hotspot implementations were originally developed for hardware of the early 2000s.
> So when sending a message to an object multiple times you only pay for the cache lookup - which is pretty fast by itself and a cached call is faster than a C++ virtual method call.
This is not true. The code in Mike's test for "IMP-cached message send" doesn't match the cached path of objc_msgSend. It matches what you would get by doing IMP-caching yourself, which is common in some Objective-C frameworks, especially older ones. The cached path of objc_msgSend is what is measured by the "Objective-C message send" row, which is a lot slower.
The fast path of objc_msgSend roughly goes like this: you dereference the isa of the object to get to a class description, then you dereference a pointer to the cache, and after finding the selector in the cache, perform a tail indirect call to the IMP. Even if the cache lookup is free this is two dependent pointer dereferences followed by a dependent indirect branch.
A C++ virtual call just dereferences a vptr and makes an indirect call to a function at a static index in the vtable, which will always be more efficient than objc_msgSend. It's true that manually caching the IMP yourself and calling it will be faster than a C++ virtual call, but there's nothing stopping you from caching the implementation of a C++ virtual call in exactly the same way.
Another major factor in the performance difference between C++ and Objective-C is that C++ allows you to have non-virtual member functions, which can be inlined, whereas Objective-C requires all method calls to go through the same mechanism.
DOM reflows are the major unsolvable source of perceived slowness.
Cool.
So, in an app where you avoid reflows, is mobile web fast? Why or why not? How avoidable are reflows?
Can anyone point me to any articles that talk about this specifically, or benchmarks that make this concrete? (I vaguely recall benchmarks that covered reflow, but it's a vague memory).
Edit: ah, posting the same link at the same time...
But I think the best tip is: be shallow. A lot of things trigger reflow but DOM-depth is causing slow reflows.
I'd agree that some of the causes are unsolvable if you want a design completely unconstrained by the performance limits of HTML rendering environments. This is why some people build HTML apps that look like some native app they had and say, "this is too slow!" However, there are some very bad anti-patterns that cause unnecessary reflows, and those can be avoided if you understand them.
For example, many infinite-scroll pages add a small amount of content to the DOM and then ask, "Did I fill the page enough?" by asking for the new height of the page. Each time they do that it requires a reflow where the browser recalculates the height of the page. When the added content is small it may take four or five trips through this loop to feed enough content into the page. A better alternative performance-wise would be to make each chunk of added content a fixed height. That way it's easy to do the math inside your own code, add four or five chunks, and the browser will do a single reflow at the end.
If you look at the "flat" designs that are becoming common in operating systems and apps, they perform well in these situations because they are very regular and easy to compute. Things like rounded corners, transparency, and drop shadows all make pages slower to render.
"They can also reallocate elements into the stack frame rather than heap when they detect specific allocation usage."
I've always been interested in whether this is done in various managed languages. Since I never know the answer, one pattern I've taken to is to allocate memory in static const/final fields that I would normally put on the stack in C, instead of doing a heap allocation to a variable whose scope is completely within one function. You have to watch out for some gotchas like recursing into the same function, but overall it's a pretty painless and clear pattern for me. Multiple functions can all share the same static memory too.
Not that I'm religious about this design pattern, but it's something I try to do in performance-critical code.
From the few times I tried to read Android code, I think the JIT is not touched since Android 2.3.
I am still curious what Google is going to do with Java and Dalvik, as the whole thing seems to be frozen since the whole process with Oracle, and they only add new APIs.
The last two Google IOs did not have any Dalvik related talk.
It's unclear if Dalvik's JIT compiler needs updating. For non-mobile uses like GoogleTV, it might be worth making a much more aggressive JIT compiler, since battery use would not be an issue. Other than that, it seems like changes would have a small benefit, a large risk, and a very large testing burden.
Android is, still, a very lean project inside and lean corporate structure. If it's a third of the way down the priority list, it probably ain't happening.
> It's unclear if Dalvik's JIT compiler needs updating.
Well the people doing games already gave up on it since the NDK is available, but given that Google gives a second class treatment to the folks using the NDK, it would be nice if they cared to improve the JIT.
Android contains a variety of performance strategies: Dalvik's JIT is transparent to the developer and it provides a performance boost for most Java apps, Renderscript is for compute-intensive operations. The NDK enables native code modules and makes it fairly convenient to support multiple architectures. Somewhere in there you should be able to find what you need. But I really don't see why anyone developing action games for a multi-threaded, multi-tasking Java OS isn't aware they are going to find it difficult to get a consistently performing game loop. Android just isn't designed for that. Perhaps if Google is really thinking of making a console, they will provide game-oriented scheduling.
Sure we can find how way around what is provided, but the performance story could be made better, specially when compared what is given to us in iOS and Windows Phone 8 environments.
Ah, but if you're going to talk about multi-threaded applications, we're going to have to start talking about things like thread-safe reference counts...
You certainly have to be careful passing data between threads in ObjC (though, of course, you do in Java, too); the real problem with managed memory in multi-threaded highly latency sensitive UI applications, though, is that a thread other than the UI thread can happily trigger a stop the world collection through allocation, and it's very difficult for the UI thread to say "I'm doing something important for now; please don't allocate for a bit" (it's possible, but it's a nightmare).
It feels like the author just hijacked the topic to rant about Objective-C. No big insights into why web apps are slow other than: javascript is not the bottleneck, rendering the dom (which is a complicated process) is.
> In fact JavaScript can't technically perform slowly since it is for most intents and purposes single threaded... so long running JavaScript code that will take 50 seconds just won't happen...
It's a valid point that almost nobody's writing 50-second raytracing routines in JavaScript, but it's trivial to run code that's executed in the background without freezing your app, by repeatedly calling it with setTimeout(). Just make sure that any "tick" of your code runs in under 30ms or so (although, depending on your task, that may not be so trivial).
This is where Web Workers [1] come in. You shouldn't be performing background processing in a way that blocks the UI. Heavy lifting like that should be pushed into the background, if it's done on the front-end at all.
Some numbers to back this up would be nice. Apple has been optimizing the hell out of Objective-C and I think it has a method invocation down to like 10 instructions or so.
47 comments
[ 4.1 ms ] story [ 95.0 ms ] thread> This isn't really hard, you just make sure that while you are performing an animation or within a game level you don't make any allocations. The GC is unlikely to kick in and your performance will be predictable and fast. ARC on the other hand doesn't allow you to do that since ARC instantly deallocates an object you finished working with.
I don't understand this. All the strategies one might use to avoid allocations in performance-critical code under GC (pooling resources, or whatever) are also available under ARC.
The difference is that if the way I use memory causes performance issues without GC, it will be much more reliably reproducible than with GC.
[1] - http://stackoverflow.com/questions/5943949/late-binding-vs-d... [2] - http://www.gnu.org/software/gnustep/resources/ObjCFun.html [3] - http://developer.apple.com/library/ios/#documentation/genera... [4] - http://stackoverflow.com/questions/9470824/dynamic-binding-l...
Well, "always" is a little wrong. The first time a message is passed and the bound method is called by the runtime is significantly slower (~4x slower than a virtual method call in C++). But then this message/method pair gets cached and for every subsequent call the cache is used.
Then there's some real neat trickery that is performed in objc_msgSend() ... after the method to "call" has been found the code directly jumps into that method without creating a new stack frame. (All needed arguments have been passed to objc_msgSend() and are already on the stack.) objc_msgSend() in essence is a trampoline.
So when sending a message to an object multiple times you only pay for the cache lookup - which is pretty fast by itself and a cached call is faster than a C++ virtual method call.
Performance Numbers: http://www.mikeash.com/pyblog/performance-comparisons-of-com...
More about objc_msgSend(): http://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-buil...
Now I don't know enough about Java but I guess it isn't much more faster than that. So calling Obj-C slow may be a little too bold.
objc_msgSend is a trampoline in the sense that it leaves the frame/registers untouched and jumps straight to the target implementation.
That said, you can implement the kinds of trampolines you're referring to without writable code pages, it just requires more work: http://landonf.bikemonkey.org/2011/04/index.html
The language alone cannot do anything.
http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-th...
This is not true. The code in Mike's test for "IMP-cached message send" doesn't match the cached path of objc_msgSend. It matches what you would get by doing IMP-caching yourself, which is common in some Objective-C frameworks, especially older ones. The cached path of objc_msgSend is what is measured by the "Objective-C message send" row, which is a lot slower.
The fast path of objc_msgSend roughly goes like this: you dereference the isa of the object to get to a class description, then you dereference a pointer to the cache, and after finding the selector in the cache, perform a tail indirect call to the IMP. Even if the cache lookup is free this is two dependent pointer dereferences followed by a dependent indirect branch.
A C++ virtual call just dereferences a vptr and makes an indirect call to a function at a static index in the vtable, which will always be more efficient than objc_msgSend. It's true that manually caching the IMP yourself and calling it will be faster than a C++ virtual call, but there's nothing stopping you from caching the implementation of a C++ virtual call in exactly the same way.
Another major factor in the performance difference between C++ and Objective-C is that C++ allows you to have non-virtual member functions, which can be inlined, whereas Objective-C requires all method calls to go through the same mechanism.
DOM reflows are the major unsolvable source of perceived slowness.
Cool.
So, in an app where you avoid reflows, is mobile web fast? Why or why not? How avoidable are reflows?
Can anyone point me to any articles that talk about this specifically, or benchmarks that make this concrete? (I vaguely recall benchmarks that covered reflow, but it's a vague memory).
https://developers.google.com/speed/articles/reflow
https://developers.google.com/speed/articles/javascript-dom
http://www.stubbornella.org/content/2009/03/27/reflows-repai...
Edit: ah, posting the same link at the same time... But I think the best tip is: be shallow. A lot of things trigger reflow but DOM-depth is causing slow reflows.
For example, many infinite-scroll pages add a small amount of content to the DOM and then ask, "Did I fill the page enough?" by asking for the new height of the page. Each time they do that it requires a reflow where the browser recalculates the height of the page. When the added content is small it may take four or five trips through this loop to feed enough content into the page. A better alternative performance-wise would be to make each chunk of added content a fixed height. That way it's easy to do the math inside your own code, add four or five chunks, and the browser will do a single reflow at the end.
If you look at the "flat" designs that are becoming common in operating systems and apps, they perform well in these situations because they are very regular and easy to compute. Things like rounded corners, transparency, and drop shadows all make pages slower to render.
I've always been interested in whether this is done in various managed languages. Since I never know the answer, one pattern I've taken to is to allocate memory in static const/final fields that I would normally put on the stack in C, instead of doing a heap allocation to a variable whose scope is completely within one function. You have to watch out for some gotchas like recursing into the same function, but overall it's a pretty painless and clear pattern for me. Multiple functions can all share the same static memory too.
Not that I'm religious about this design pattern, but it's something I try to do in performance-critical code.
I am still curious what Google is going to do with Java and Dalvik, as the whole thing seems to be frozen since the whole process with Oracle, and they only add new APIs.
The last two Google IOs did not have any Dalvik related talk.
Android is, still, a very lean project inside and lean corporate structure. If it's a third of the way down the priority list, it probably ain't happening.
Well the people doing games already gave up on it since the NDK is available, but given that Google gives a second class treatment to the folks using the NDK, it would be nice if they cared to improve the JIT.
Sure we can find how way around what is provided, but the performance story could be made better, specially when compared what is given to us in iOS and Windows Phone 8 environments.
That seems like a big 'just', at least in a multi-threaded application...
It's a valid point that almost nobody's writing 50-second raytracing routines in JavaScript, but it's trivial to run code that's executed in the background without freezing your app, by repeatedly calling it with setTimeout(). Just make sure that any "tick" of your code runs in under 30ms or so (although, depending on your task, that may not be so trivial).
[1] https://en.wikipedia.org/wiki/Web_Workers
Some numbers to back this up would be nice. Apple has been optimizing the hell out of Objective-C and I think it has a method invocation down to like 10 instructions or so.
http://widgetsandshit.com/teddziuba/2008/09/a-web-os-are-you...