Cocoa Programming for Mac OS X - Aaron Hillegass [132]
You may be wondering why NSMutableArray is not thread-safe. One reason is that mutex locks have overhead associated with them, and thread safety would make NSMutableArrays significantly slower. Another reason is that it is often more useful (and common) to lock a section of code, not just a single method call, such as if you were moving objects from one data structure to another.
Cocoa provides a number of other tools for thread synchronization, such as NSLock and NSCondition. These tools, a more involved look at NSOperationQueue, and Grand Central Dispatch are discussed in detail in Advanced Mac OS XProgramming.
For the More Curious: Faster Scattered
Our goal in this chapter was to make Scattered a better-behaved application by moving heavy lifting off the main thread. We didn’t, however, fully address the performance issues with this application. If you were watching closely, you may have noticed that in init, we limited the number of concurrent operations in processingQueue to four. If you remove that constraint (by commenting out the line), you may find that Scattered runs somewhat faster, or you may find that it runs even slower. What’s going on here?
One of the most useful features of Grand Central Dispatch (GCD), which NSOperationQueue is built upon, is that GCD manages the number of running operations (threads) based on the system’s hardware (number of cores) and the current system load. In higher-level terms, GCD will create as many threads as it thinks the system can handle in order to process the operations in a queue as quickly as possible. If every queued operation is uniform as far as its required resources, this works very well.
Consider how Scattered works, however: Each operation starts by reading data (an image) from disk. Disk I/O puts very little demand on the CPU, so GCD sees that that the CPU isn’t being utilized and starts another operation, which starts by reading data from the disk, and so forth. Perhaps you can see how GCD would very quickly start a large number of threads to handle the operations in the queue.
When the image data for the first operation is fully read in, the image is decompressed, and a thumbnail is created, which is somewhat CPU-intensive work. While this work is being done, the second thread finishes reading from disk and starts decompressing, and so on. Suddenly, the CPU is being asked to do quite a bit of work!
In the exercise, we avoided this pile up by limiting the number of concurrent operations, but this approach is not ideal, because we are frequently wasting CPU time while waiting on the disk: recall how hilly the CPU graph in Instruments was. The proper solution to this problem is to use two queues: one queue to load image data from the disk, limited in the number of concurrent operations it can conduct (because disks are slow), and another queue to do the work of creating the thumbnail. This is, however, quite a bit more complicated to do properly.
Challenge
Adapt Scattered to use the proper solution outlined in the previous section. Because NSImage avoids doing disk I/O until absolutely necessary, you will need to read the data in manually and then create the image using that data. NSData will read the image data. Check the documentation or header file for NSImage to find a way to create an image from an NSData object.
Chapter 35. Cocoa and OpenGL
This chapter is not designed to teach you OpenGL. If you want to learn OpenGL, read The OpenGL Programming Guide. Rather, this chapter is intended to show you how to do drawing with OpenGL in an application that is written using Cocoa. Like all other drawing in Cocoa, OpenGL rendering will be done in a view. Until now, all your views have used an NSGraphicsContext to do drawing with Quartz (via NSImage, NSBezierPath, and NSAttributedString).
NSOpenGLView is an NSView subclass that has an OpenGL drawing context. Just as you needed the focus locked on a view to do drawing with Quartz, so the OpenGL