Re: Silly ARC question - explanation requested

2014-06-04 Thread Alex Zavatone
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

2014-06-04 Thread Quincey Morris
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

2014-06-04 Thread Ken Thomases
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

2014-06-04 Thread Alex Zavatone

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

2014-06-04 Thread Alex Zavatone

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

2014-06-04 Thread Ken Thomases
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

2014-06-04 Thread Quincey Morris
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

Silly ARC question - explanation requested

2014-06-04 Thread Alex Zavatone
I was just reviewing a team member's code on iOS 7 for reading the 
CVPixelBuffer from a video frame, storing  it in a UIImage and converting to 
grayscale.  

Was testing against 720p video in the iPad and was pretty surprised to see 
memory leak faster than I've ever seen before, but only after our convert to 
grayscale utility function was called (which exists in an external lib).

Pretty surprised, I ran Instruments and saw that on every frame grab, the 
convert to grayscale allocates 3.52 MB of memory, but this wasn't being marked 
as a leak, which was surprising and it wasn't being released later on even if I 
nilled the local that it was assigned to.

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.

Within the loop, we do have another routine that draws items and to get it to 
not be a memory pig, he placed this within an autoRelease pool.  That works.  
Memory is released as expected.

Great.  (Though ugly).

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.

So I put the "leaking" grayscale conversion code in the external lib in an 
autoRelease pool as well and memory was released too, just as expected.

OK.  Sure.  This works, buuut.

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!"

His response was "Well, I was able to autorelease any offending bits within the 
loop without a problem, what's the big deal?"

Yeah, bt.

I'm trying to find a good way to explain to him that even though you can do 
things this way, Mr. Damn Competent Dot Net Programmer, but this is pretty much 
opposite to the way Apple wants you to code in Cocoa and it will make our lives 
sadder than wet puppies in the rain if we think this is OK.

He has a good point though - "don't you want to make our routines in our 
libraries robust or 'memory friendly'?  We have access to our own source, but 
what about for all the libs we use where we don't have access to the source?"

Weeelll, yeah, sort of.


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"?

About the "well, don't we want to use autorelease pools in our external libs to 
make them more memory friendly in case they are called from repeats or whiles?" 
point, what are your thoughts on that one as well?

Thanks in advance.  

- Alex
"He who is immune to the the horrors of using SVN in Xcode"
___

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