Re: Silly ARC question - explanation requested
Sweet. Thanks guys. That's exactly what I needed to frame why this is bad to our former dot net guy. Cheers. On Jun 4, 2014, at 6:02 PM, Quincey Morris wrote: > On Jun 4, 2014, at 14:45 , Alex Zavatone wrote: > >> But I need to come up with an explanation of explain why that is a bad idea >> and why it smells > > It smells because a computationally intensive while loop will stall the > dispatch queue that’s stuck on it. > > If you’re programming with *threads*, it’s fine to dedicate a thread to a > long-running operation. > > If you’re programming with *dispatch queues*, you’re sharing a resource (a > CPU) with other applications. Taking the resource over for yourself is > detrimental to overall system performance. > > Breaking the long task into smaller pieces allows the pieces to be > distributed over as many threads as the OS wants to use, and doesn’t deny > access to CPUs to other threads and processes. > ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Silly ARC question - explanation requested
On Jun 4, 2014, at 14:45 , Alex Zavatone wrote: > But I need to come up with an explanation of explain why that is a bad idea > and why it smells It smells because a computationally intensive while loop will stall the dispatch queue that’s stuck on it. If you’re programming with *threads*, it’s fine to dedicate a thread to a long-running operation. If you’re programming with *dispatch queues*, you’re sharing a resource (a CPU) with other applications. Taking the resource over for yourself is detrimental to overall system performance. Breaking the long task into smaller pieces allows the pieces to be distributed over as many threads as the OS wants to use, and doesn’t deny access to CPUs to other threads and processes. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Silly ARC question - explanation requested
On Jun 4, 2014, at 4:26 PM, Alex Zavatone wrote: > On Jun 4, 2014, at 4:07 PM, Ken Thomases wrote: > >> You have a misconception as illustrated in the above quotes. I think you >> think of ARC as a garbage collector that needs idle time or for execution to >> return to the frameworks in order to work. It is not and does not. >> >> For the most part, ARC just inserts retains and releases just as you would >> in manual retain-release mode if you were extremely careful. (The exception >> is weak pointers which get nil-ed out when the pointed-to object is >> deallocated. There's also the returned-autoreleased object optimization >> that you couldn't achieve on your own, but that's not relevant here.) > > I was jumping to that conclusion because I had never seen ARC code in a > dispatch_async thread fail to release memory in low memory conditions. ARC is not sensitive to memory conditions, just as the behavior of retains and releases you write in your code in MRR mode are not. ARC inserts retains and release at compile time. > I'd never seen a situation where setting a variable to nil didn't release the > memory under ARC before, let alone keeping every instance allocated even if > I'd set it to nil. Setting a strong reference to nil releases the prior referent. That reference may not have been the last one, so the object is not necessarily immediately deallocated. There's still retain counting going on, it's just behind the scenes. An object that was retained and then autoreleased has not been released yet. The reference is, logically, in the autorelease pool. When that is drained, that's when the reference is released. > Our call to the grayscale conversion would allocate 3.52 MB every time it was > called and stored the UIImage in a local. Even if I tried to invalidate the > image, nil the memory would never get released until after the while loop > exited. Either the UIImage or some large internal resource was presumably held in the autorelease pool. > I made the while loop exit after 100 frames and our 172 MB of allocated RAM > instantly hopped back down to 2.8 MB. > > Honestly, I'd expect that as the loop looped, the previous instance of the > grayscale image would immediately be released, but somehow the while loop or > the dispatch_async prevented this. Again, if you're not draining an autorelease pool within your loop, objects can just accumulate in an outer pool indefinitely. > I guess a better question is "why/how does the while loop seem to delay ARC's > memory releasing, or was this caused by the while loop being inside a GCD > dispatch? The loop does not delay ARC's releasing. ARC is not responsible for the autorelease pool. ARC will autorelease objects that it has strong references to if they are returned out of the function/method, because that's necessary to keep them alive long enough for the caller to receive them. Likewise, it will autorelease objects returned through output parameters (e.g. NSError**), by default. In some cases, ARC can detect that the caller would immediately retain the object and it can cancel the autorelease and retain out, but that's just an optimization. But objects which have been autoreleased, whether by MRR code or ARC code, are in an autorelease pool. ARC no longer has any effect on them. They will live until that pool is drained. >> It sounds to me like this is just the classic sort of peak memory usage you >> would get with autoreleased objects with manual retain-release code if you >> don't drain the autorelease pool in your loops. > > So, while this feels to me at if it's encouraging this type of problem to pop > up, it is acceptable practice, as long as the critical elements are in > autorelease pools and are manually drained? What is "it"? > It just seems that a much better approach would be if the frame processing > was one async thread for one frame at a time and was only called upon a frame > change by watching hasNewPixelBufferForItemTime. Then when that frame thread > is completed processing, execution isn't stuck in a while loop while being > inside one large asynch-ly dispatched process. Well, a "while (true)" loop is a pretty strong code smell. If that while loop is ever busy looping or sleeping, the smell is even worse. But it's completely separate from the issue of memory management. Do not decide whether to use such a loop based on the autorelease pool management. Decide on more fundamental design tenets. Is such a loop the right design for what your program is doing? (From the sound of things, I would guess it's probably not.) Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/coco
Re: Silly ARC question - explanation requested
On Jun 4, 2014, at 4:02 PM, Quincey Morris wrote: > I think the deeper problem is that a while loop of this kind in an > asynchronously executed block is a bit of a code smell. Exactly. But I need to come up with an explanation of explain why that is a bad idea and why it smells. I know it smells, but he can't see why it smells bad in Cocoa. Ohh, I like the serial queue approach very much. Sweet. > This is not really an ARC-related pitfall, but a GCD-related one, IMO. Yeah, that is what I am trying to determine. Is it that the while loop is causing problems, or is it simply that since he's trying to process the whole video of arbitrary length and dimensions within ONE dispatch_asynch that's the root of the issue? Yeah, I'd expect all the memory to be released after the thread/bock completes and memory would be fine if the iDevice had 10 GB of RAM, or if the video was very short. And memory does get released if I break out of the while loop early which is exactly as expected. I was expecting that memory would get released if I voided the offending UIImage, but it doesn't and even as the loop iterates, the local UIImage instance piles up in memory. I'll be damned if I know where there's a reference to it lying around and since I'd set it to nil, I was wondering why ARC didn't take effect exactly when I did that, but instead waits until after the whole while loop and thread/block. Cheers and thanks man ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Silly ARC question - explanation requested
On Jun 4, 2014, at 4:07 PM, Ken Thomases wrote: > On Jun 4, 2014, at 2:24 PM, Alex Zavatone wrote: > >> As it turned out, my coworker had created a dispatch_async thread to start >> processing the video and within it, started at a framecount of 0, >> incremented it within a while(true) loop and grabbed the frame every time >> there was a new framePixelbuffer. All within the while(true) loop. >> >> Well, OF COURSE memory's not going to be released and of course it's not a >> leak, but here's where I need some help understanding how to explain this >> and why this approach is a bad idea within Cocoa. > >> But within the convert to grayscale routine in the external lib where we >> create a CGContextRef and later issue a CFRelease on it, memory is allocated >> within CGContextDrawImage. However it builds up for every frame grabbed and >> is not released until the while loop is exited. This is where we lose 3.52 >> MB each pass, but it's not really lost since it's allocated but can't be >> released by ARC since it's within a while loop. > >> My first inclination is, "DUDE. You silly rabbit! You created an async >> process, then wrapped the whole video processing section in a while loop and >> you expected memory to get released by ARC? ARGH! WHY? Bad codemonkey, >> bad!" > >> This is where I'm seeking a better explanation than I can give. Does anyone >> have a better explanation for "this is why we never do things this way - >> even though you can wrap time consuming operations in repeats and whiles, it >> doesn't mean that you should and expect ARC to work"? > > You have a misconception as illustrated in the above quotes. I think you > think of ARC as a garbage collector that needs idle time or for execution to > return to the frameworks in order to work. It is not and does not. > > For the most part, ARC just inserts retains and releases just as you would in > manual retain-release mode if you were extremely careful. (The exception is > weak pointers which get nil-ed out when the pointed-to object is deallocated. > There's also the returned-autoreleased object optimization that you couldn't > achieve on your own, but that's not relevant here.) > I was jumping to that conclusion because I had never seen ARC code in a dispatch_async thread fail to release memory in low memory conditions. I'd never seen a situation where setting a variable to nil didn't release the memory under ARC before, let alone keeping every instance allocated even if I'd set it to nil. Our call to the grayscale conversion would allocate 3.52 MB every time it was called and stored the UIImage in a local. Even if I tried to invalidate the image, nil the memory would never get released until after the while loop exited. I made the while loop exit after 100 frames and our 172 MB of allocated RAM instantly hopped back down to 2.8 MB. Honestly, I'd expect that as the loop looped, the previous instance of the grayscale image would immediately be released, but somehow the while loop or the dispatch_async prevented this. I guess a better question is "why/how does the while loop seem to delay ARC's memory releasing, or was this caused by the while loop being inside a GCD dispatch? Thanks Ken. > It sounds to me like this is just the classic sort of peak memory usage you > would get with autoreleased objects with manual retain-release code if you > don't drain the autorelease pool in your loops. So, while this feels to me at if it's encouraging this type of problem to pop up, it is acceptable practice, as long as the critical elements are in autorelease pools and are manually drained? It just seems that a much better approach would be if the frame processing was one async thread for one frame at a time and was only called upon a frame change by watching hasNewPixelBufferForItemTime. Then when that frame thread is completed processing, execution isn't stuck in a while loop while being inside one large asynch-ly dispatched process. Cheers. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Silly ARC question - explanation requested
On Jun 4, 2014, at 2:24 PM, Alex Zavatone wrote: > As it turned out, my coworker had created a dispatch_async thread to start > processing the video and within it, started at a framecount of 0, incremented > it within a while(true) loop and grabbed the frame every time there was a new > framePixelbuffer. All within the while(true) loop. > > Well, OF COURSE memory's not going to be released and of course it's not a > leak, but here's where I need some help understanding how to explain this and > why this approach is a bad idea within Cocoa. > But within the convert to grayscale routine in the external lib where we > create a CGContextRef and later issue a CFRelease on it, memory is allocated > within CGContextDrawImage. However it builds up for every frame grabbed and > is not released until the while loop is exited. This is where we lose 3.52 > MB each pass, but it's not really lost since it's allocated but can't be > released by ARC since it's within a while loop. > My first inclination is, "DUDE. You silly rabbit! You created an async > process, then wrapped the whole video processing section in a while loop and > you expected memory to get released by ARC? ARGH! WHY? Bad codemonkey, bad!" > This is where I'm seeking a better explanation than I can give. Does anyone > have a better explanation for "this is why we never do things this way - even > though you can wrap time consuming operations in repeats and whiles, it > doesn't mean that you should and expect ARC to work"? You have a misconception as illustrated in the above quotes. I think you think of ARC as a garbage collector that needs idle time or for execution to return to the frameworks in order to work. It is not and does not. For the most part, ARC just inserts retains and releases just as you would in manual retain-release mode if you were extremely careful. (The exception is weak pointers which get nil-ed out when the pointed-to object is deallocated. There's also the returned-autoreleased object optimization that you couldn't achieve on your own, but that's not relevant here.) It sounds to me like this is just the classic sort of peak memory usage you would get with autoreleased objects with manual retain-release code if you don't drain the autorelease pool in your loops. Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Silly ARC question - explanation requested
On Jun 4, 2014, at 12:24 , Alex Zavatone wrote: > His response was "Well, I was able to autorelease any offending bits within > the loop without a problem, what's the big deal?" I think the deeper problem is that a while loop of this kind in an asynchronously executed block is a bit of a code smell. Surely the first step is to refactor the code so that each iteration is a separate block invocation. You either queue these for execution on a parallel queue for multitasked execution, or on a serial queue if execution must be sequential**. (If the interior of the while loop was trivial, you might not do it that way, but if it’s allocating significant amounts of memory each iteration, and doing significant work, then block-per-iteration sounds more reasonable.) Then, you can and should enclose each block’s code in an autorelease pool, which should stop memory accumulating. In the context of your original question, the problem is that while you *might* be able to predict the autorelease behavior in regard to what you *think of* as the “final” autorelease, you don’t know for certain — when any frameworks or 3rd party code is involved — if there might be *other* life-sustaining references to the objects. It’s “autorelease” after all, not “autodealloc”. This is not really an ARC-related pitfall, but a GCD-related one, IMO. ** You can use counting dispatch semaphores if you want something in between — several blocks queued but not for every iteration at the same time. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com