On Nov 14, 2010, at 3:58 AM, Antonio Nunes wrote:

> I'm developing a daemon to run tasks as files get dropped into a folder. When 
> a new file is detected, a new operation gets put onto the daemon's operation 
> queue. The operation launches a task and waits for it to finish, then the 
> operation exits.
> 
> The issue I am seeing is that the operation queue appears to indiscriminately 
> run operations as they are added to the queue, regardless of system 
> resources, thus bringing the whole system to a crawl. I thought, from reading 
> the documentation, that the queue would be intelligent about how many 
> operations it would have running at the same time, depending on system 
> resources such as number of cores and available memory.

The system can't predict the future.  When an operation is pending in a queue, 
there's no way for the system to know if it's going to be CPU intensive, memory 
intensive, and/or I/O intensive.

Let's suppose the system primarily governs its thread pool based on available 
CPU time.  Let's also suppose that your operations start with some blocking I/O 
but then switch to computation.  When there are operations queued, but most of 
the already-running operations are blocked doing I/O, then the CPU may have 
idle capacity.  So, the system starts another operation (or several).  It will 
have effectively "overbooked" the CPU -- in short order, as I/O requests are 
satisfied, there will be more operations doing CPU-based computation then there 
is CPU capacity to handle them.

It's also the case that disk I/O degrades significantly with contention.  So, 
even just having a bunch of I/O-intensive operations queued can burden the 
system, even while there's plenty of spare CPU and memory capacity.

There's discussion of a related issue, this time having to do with Grand 
Central Dispatch, at Mike Ash's blog:
http://www.mikeash.com/pyblog/friday-qa-2009-09-25-gcd-practicum.html

> Since this doesn't seem to be the case, I have to assume something is not 
> quite right with my implementation.

Not necessarily.

However, one approach to working around the system's limitations is to separate 
your operations into pure I/O operations and pure CPU operations.  (If one 
logical operation involves both, you can split it and use dependencies to have 
the two halves run in the appropriate order.)  Schedule the I/O operations to a 
queue with a fairly small maximum concurrent operations limit.  If you want to 
get really fancy, you might have separate I/O queues for each device (e.g. 
disk) to maximize parallelism.  Schedule the CPU-intensive operations onto 
general, unlimited queues that the system can manage.  These operations should 
never block, if you can avoid it.

By separating the types of work, you give the system another opportunity to 
manage the load.  So, if one operation first does some I/O and then does 
computation, the system will be "fooled" by the CPU lull at the beginning into 
launching more operations and then hammered by the change to computation.  When 
the types of work are split, the I/O operations may be launched because there's 
free CPU capacity, but at least when the computation kicks in the system gets 
another chance to decide whether there are free resources to handle it.


> Also, is there a way to find out the number of cores on a machine so that I 
> can set that as a hard limit of number of operations on an NSOperationQueue?

-[NSProcessInfo activeProcessorCount]


> Here is an outline of how my code implements the daemon and the operations:
[snip]

It appears that your program is launching copies of itself to process 
individual files.  I suspect you're "bringing the whole system to a crawl" by a 
fork bomb.  Have you checked that your program is not infinitely-recursively 
launching subprocesses?

Also, why are you re-running your program in a subprocess, instead of just 
having your operation do the work directly?  (I suspect you do it that way in 
case processing of a particular file causes a crash.  You want to limit the 
crash to just the process handling that file.)

Have you considered letting launchd monitor the directories, launch your 
daemon, and deal with any crashes?

I'll also say that having NSOperations launch an NSTask and block waiting for 
it to exit is nearly a worst case scenario for NSOperationQueue.  It has a 
thread pool with idle threads, so it figures it can pull a new operation off 
the queue to start it with minimal new resources.  That operation not only 
monopolizes the thread, it does effectively nothing with it.  Instead, it 
launches a whole separate process, which is a significant use of new resources.

At least you could avoid the waste of the worker thread by launching the 
subprocess from the main thread.  Instead of blocking as you wait for it to 
exit, use the asynchronous notifications to trigger any followup work.  Of 
course, this approach doesn't integrate with NSOperationQueue, but you're 
effectively undermining that, anyway.

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to