WWDC2004 Session 434

Transcript

Kind: captions Language: en hello welcome to session for 34 Coco performance techniques name is Troy Stevens I'm a software engineer and the cocoa frameworks group at Apple and i'm here today to talk to you about performance and optimization specifically as they applied in developing cocoa applications on mac OS pen now as we all probably know performance is work and diligence but it can also be exciting and rewarding it can pay off and applications that your users want to use if there's one thing that users love it's a well optimized responsive application one that not only empowers them to do something of interest but is ready to respond to their requests and provide feedback with quick turnaround to interact with them in a way that is responsive so optimization looks we're living so turnaround is part of it and one thing that you want to do is figure out where the opportunities for optimization in your application lie and how do you do that how do we figure out where to do things well it's partly a matter of metrics of using measurement techniques and measurement tools it's partly a matter of having some general ideas about optimization that may apply you may brought these from other platforms that you've worked on and it's partly a matter of knowing the framework that you're working within the platform that you're working with knowing the various capabilities that it provides their performance characteristics and so forth so that's what I want to talk to you about today the other the other issues the platform independent issues about performance are easily covered elsewhere their comprehensively covered elsewhere today I want to talk specifically about Coco and in particular we'll look at I'll try to point out to you some common performance techniques some issues that you may encounter commonly as you're developing Coco applications things lots of people run into I'll try to point out some API usage techniques and recommended usage patterns that you can use to help improve formance and we'll also look at alternative ways different ways that cocoa provides a variety of ways to perform a different tent as a particular task that give you the opportunity to choose the way that is most conducive to the getting the best performance so it's prerequisites for this talk I assume only that you have some general experience developing in cocoa some familiarity more or less with the various api's and classes we capabilities that it provides and if you have some general knowledge of performance techniques that you've brought from another platform that could be helpful too but it's not strictly required so first before we dive into the cocoa specifics I want to cover some general concepts just to get us into the right mindset for thinking about optimization there's a lot of wisdom that's been accumulated from various other platforms that we all have worked on some things about performance are constants and we can learn from that so one of the most important things about performance tuning is it involves trade-off some give and take on one hand you have this ideal of achieving high performance in your applications creating a responsive application but there are a number of various areas in which you may need to give a little and compromise there are trade-offs involved and one of those trade-offs is resource usage a common technique from producing higher performance is rather than recalculating results say cashing it you use a little more resource keep some block of memory around and reuse that you're trading memory for performance in that case there are lots of other examples of that with other types of resources engineering time is always at a premium there's never enough of it and there are any number of aspects of your application that you can spend time tuning you don't want to spend time optimizing every little bit of your application because many cases that can be premature and it can lead to more complex code that's just completely unwieldy to maintain so since engineering times limited resource you need to really focus in on where you'll get the most benefit from the performance optimizations you're doing convenience and simplicity a lot of times though not always the most convenient way to do something is not always the way that is conduced to the highest performance so sometimes you have to make given and make your codes a little more complicated to optimize it for the special case that it's really going to have to handle so that you can get maximum performance I loose coupling flexibility to change these are the hallmarks of object-oriented programming and to some extent you can achieve performance while still retaining some loose coupling but but the essence of programming and developing an application is in some sense making assumptions defining the assumptions of your problem domain so often the more assumptions you deny yourself the ability to make more general your code is and the fewer the optimization opportunities are really taking advantage of so sometimes you have to give up a little flexibility in order to hardwire your codes for the fastest path for the best performance lastly strict correctness and safety almost all the time you want to make sure no matter what your code is correct and does the right thing only a few cases can you actually get away with approximating but there are some cases such as when you're drawing if you're drawing something that doesn't need to be exact that maybe speed is more important you can approximate in cases like that and also in terms of safety if you're developing generic code let's say that you're going to put in the framework and you don't know who's going to use it yet it might be used by some unknown application or applications you might not need to you might not know whether those applications will need those objects that you're providing to be thread-safe let's say so if you have to provide locking and thread safety that's a whole other burden that can affect the performance you have to be able to balance the desire for performance against these other competing demands so there are some general recommendations we can make about performance when the most important things that you can do is to choose a scalable application architecture think about the ways that your application is going to be used try to anticipate the numbers of objects that your application is going to have to deal with and choose appropriate algorithms and data structures these can on any platforms can make a huge difference in how your application performs also when you're working with the framework api's don't fight the framework we try to leverage as much as possible what the framework provides and in terms of cocoa in particular or where cocoa provides a facility to do something it's in many ways to your advantage to leverage that facility to its maximum capability and use it because then you have the benefit of a whole team of frameworks engineers at Apple who are constantly making not only performance improvements but also enhancements and functionality and you get to inherit all of that for free also wherever possible when you're defining your own internal data structures and your own objects try to use API compatible types where possible you notice for example in app code api's we largely use NS array I don't think we have any api's maybe a few that use NS sets so if you use a lot of sets let's say for example in your in your code and your model code you may find you have to do a lot of conversions between your internal representation and what the framework expects when every time you make a message send that uses some particular type of data so try to avoid that sort of type impedance mismatch by using API compatible types where you can also try to keep in mind easily overlooked cost things like heap allocations including allocating and D allocating objects heap operations do take some time there's some overhead involved in that we don't normally think about that we kind of think that it's just kind of magic we get best for some memory and get it for free also keep in mind indeterminate time operations particularly synchronous operations such as synchronous file system and network operations things where your application is going to block for a fair amount of time you don't want to freeze up your user interface obviously while you're waiting while the users waiting for some file system or network access to happen so if you have things like that that may block for an unpredictable amount of time try to make those asynchronous and look at techniques for doing things like that consider caching expensive results where that may help if you have something that you do that you compute that you draw that may take a lot of time and you may reuse many times before you have to change it again it may pay off the cash it keep in mind at the same time that it may not pay off to cash it if the framework say up kit is already doing some caching for you and you add your own caching on top of that you may find that there's not a net win that you've actually added just another layer of of extra code that doesn't necessarily have any effect so you want to measure to see whether the caching actually helps but in general caching can be a useful technique also lastly consider deferring operations when you can and this is an interesting and powerful technique and so I'll go into a little more detail think about deferring operations benefits of deferring operations include not having obviously not having to perform an operation immediately if you don't need the result right away well maybe you don't have to perform the operation right away maybe you just need to make a note somewhere that hey I need to compute this later you obviously then don't incur the cost of the operation right now and if you have a number of requests a series of requests that come in maybe from different parts of the system to perform that given operation but they don't need again don't need the results immediately you may be able to save doing that operation several times by coalescing those requests just setting that flag and every time you set that flag well it's a constant time operation and it's really cheap and then later when you actually need the result you can go back and say oh I need to compute this and they're there you've got it and you only compute it once instead of several times lastly if you're really lucky you may never have to incur the cost of the operation at all if the result isn't needed so try to think as your coding about whether what you're computing really needs to be done is this really necessary right now on the flip side one of the cost of this is that when you do ask when some part of the application does ask for the result later it's not going to be there ready to go the computation has to be done then so obviously this is you want to use some discretion and applying this technique but it is very useful and powerful we use it for example in the app kit when we're drawing views you're familiar probably with the set needs display met neck mechanism that we recommend rather than asking views to display themselves immediately you tell them hey you're dirty in this area you're going to need to draw here and you can have potentially many requests to mark different parts of the view dirty as needing display at some later point and all that drawing doesn't happen normally until you get back to the run loop to the top of the run loop and then we look and say oh it's part of the window dirty yeah okay we've got to redraw the parts of the window that are dirty but we can do it all at once and so we can have you know thousands of requests to dirty different parts of the window satisfied by a single drawing path similarly in your own applications if you break your application in two components if you do initialization in stages that can be a useful way to to improve performance by deferring results operations till later lastly some other things to keep in mind some of the techniques that I recommend here or point out to you rather may not be appropriate in certain situations may actually degrade performance and the only way you can really know although our intuition as engineers is very useful the only way you can really know whether an attempted optimization has had a positive effect is to measure and remember to not just measure after but measure before so you have something to compare with that's the only way to really know whether you've made things better or worse and often the result is surprising so along these lines we provide some very powerful tools for you to use including shark which is covered in another session any of you who are here right now and not in the shark session I recommend looking into shark on your own later it's a very powerful tool enables you to sample not just your application but performance with the entire system while your app is running also if you happen to be using OpenGL OpenGL profiler is a tremendously powerful tool and getting better so be aware of these tools and use them to measure to find out what's really going on in your apps so okay now we're ready to get we're in the right frame of mind we're ready to get into Coco in particular i'm going to divide the rest of the talk into two general parts first we'll look at optimization techniques and API usage techniques that apply to the api's that app kit provides and then we're going to dive deeper and look at some foundation related issues so first the app get related stuff things that I refer to as user interface issues and a number of techniques I want to cover with you today including reducing launch time taking long operations as I was saying earlier making them a synchronous optimizing drawing and scrolling in various ways and so forth so what about reducing launch time this isn't exactly maybe strictly an optimization technique this is a matter more of deferring optimizations deferring operations until later you can actually use both tech niques here remember that the users experience using your app begins with launching the app the sooner your application can be ready to go the happier your user is going to be so there are a number of ways you can reduce lunch time obviously brute force if there are things that you do at launch time and you can optimize them to run faster well obviously that's one way to do it if you can defer loading of data initializing different subsystems if your app has a lot of different subsystems and maybe your user isn't going to touch them immediately certain features they may not use even in the session of launching the application using it and quitting it you can actually defer initialization of those subsystems if it's costly until later and one technique you can use for doing this you can use nib files and plug in bundles these are two very powerful capabilities and facilities that that cocoa provides for factoring your user interface and your code you can actually dynamically load code in bundles at runtime you can factor your application into components that are loaded in as needed and that also incidentally provides it wait for users to for other developers to be able to extend your applications potentially so plugins are great where you perform your initialization may be important you all know awake from mid this is our friend when we create instantiated objects in lib files and we load those nib files at runtime awake from nib is sent to every object at a stage after all of the objects interconnections as we're made in the nib have been established and they're all sort of the network of objects is intact they're ready to go and wired up that's usually a very good and convenient time to perform initialization one thing about that is that that awake from mid is usually sent say for your your main menu nib or for your document nib if you're writing a document based app that's sent before the UI is brought on screen so any initialization that you do there is going to make the user wait potentially if it takes a long time a lesser known place to perform initialization that's very useful is application did finish launching this is a message that is sent either to the applications delegate if it has one or two any observer any object that registers as an observer of the NS application did finish launching those education it is sent significantly after the UI is already on screen after any files that were requested to be open when the app was launched have been opened and the run loop is ready to begin handling events your app is ready to go so if there's if there's initialization operations you can perform later at application did finish launching that's a useful place to hook in what about long blocking operations that may block your user interface remember in a cocoa application by default unless you do otherwise you basically have one thread on which everything happens you're then handling your drawing any processing that your application does when I click this button over here and so forth and so any long synchronous operation that you perform on is performed on the main thread and blocks your user interface normally so there are a couple of ways to deal with this we can make the operation take less time again obviously or you can use very any of various techniques to move the operation into the background and a few of these facilities that we provide for doing this are asynchronous notifications which are also sometimes called idle timers run loop observers and threads let's look at those in some detail asynchronous notifications are simply notifications that are added to a notification cue they are n queued with the posting style of post when idle these notifications are handled when the run loop figures out the application is sitting idle and there's time to do something it's just sitting there spinning waiting for something interesting to happen optionally you can ask for such notifications to be coalesced so if there are a number of different places you want to post them from to stimulate this idle activity to happen whatever activity you've designated to happen in response to the notification you can you can have them coalesce either on name or on the notification sender on the poster another powerful technique that is exists actually in core foundation remember you can use core foundation from your cocoa apps is the CF run loop observer and what this lets you do is hook into not just your run loop but at the idle time point at any point you want really and we have a number of different options here on entry to the Rome loop beforetimers are handled and so forth so you can hook in actually this is a mask so you can specify several different places that you want to hook in and it's basically a mechanism for you to provide a callback function that will be called when the run loop reaches these various stages of processing and then you can do whatever you want there well these are two very powerful techniques but obviously they're still doing their processing on the main thread there may be a little more friendly to responsiveness of the application because they're letting you do things sort of when the application figures out that it's idle and if you don't do your operations and two big chunks then you're okay but what if you have a long operation and there's no two ways about it you've got to find another way to do it and keep it from disturbing your UI another possibility is to use a background thread spin off another thread this is this is supported through the NS thread class and in particular is very easy to do in cocoa the hard part as with multi-threaded programming on any app on any platform is getting synchronization right and synchronizing object accesses and getting thread safety but spinning off the thread is very easy and a thread provides the detached new thread selector method you specify the message you want a message that you want to send the target object you can optionally specify a parameter object to be passed in and then here we have a method that basically is the thread it becomes the threat when when thread is spun off that method begins execution and that method can run forever if it expects to existed for wants to exist for the lifetime of the app or it can quit out if it's done processing and then that terminates the thread and one thing that you want to do here is put model release pool in there so that any objects that you auto release during the course of processing in your background thread do get cleaned up otherwise you're going to get messages in the console when you're just doing main thread program and you get model release pool for free that's automatically provided for you so we'll see that again so as I said the main thing to do when you are doing multi-threaded programming you have to make sure that no two threads or simultaneously accessing and especially trying to mutate a given object we provide locking semantics for that and another thing that you need to do when you're doing the processing in the background thread obviously your main thread at some point wants to know about well what were the results of that processing they usually affect something on the main thread maybe some UI and you need a way to communicate back to your main thread what the results of that processing it was and maybe even some intermediate results so there are various ways to do that the easiest way to do it is to use perform selector on main thread this is a method that's defining a category on in this object you'll find it it's hidden away and then it's thread H if you don't know about it this is a great powerful method what enables you to do is not just send a message to the main thread but but it scheduled your message so that it's performed it doesn't interrupt the main thread well that's in the middle of doing something else and so you can pass objects across thread boundaries fairly safely so this is interesting stuff and to go into this further let's see what we have cooking on demo one so here I have a document based application that i call scrapbook and it's fairly simple got my model-view-controller divisions here basically what it does is it gives you a page on which you're able to arrange a number of photos and you have a page view that you can use to see that and visualize it so let's see I've launched the application and to we have a clean new document let's drag some pictures in and it loads them up then we can drag them around lose them simple document based application and this is a custom NS view that I simply wrote that the displays the photos when it's asked to draw and the actual page model and the photo model objects keep track of where the photos are so let's see I've got an existing album here did I built and loading it oh there it goes well that took a little while try that again drag the document down and took a few seconds to do it now we're running fortunately on a dual proc 2 gigahertz g5 here but you can imagine easily on a machine that's more memory constrained or that's slower that this could take a lot more time and especially if we we only have two dozen photos here that's not a whole lot so how could we make how can we make this a more satisfying experience for the user how could we make it so that can we make it some of the document comes up more immediately well sure we can in fact here we have a multi-threaded version of scrapbook that's not much more complicated run it dragged in my document and boom the photos load in the background and what I'm doing here it's spinning off a background thread requesting that the background thread do the loading of the images each time I want to load an image I say here load load this image at this path and then it does the loading completely independently of the main thread and passes back the result when it's done so that was kind of fun with I like seeing that was do that again there we go and we just display a placeholder image in place of each each image until the image arrives on the main thread and if I do this fast enough I can even grab the Windows Start resizing and interacting with it while the images are still loading so let's look at the code for that it's fairly simple any any venture into multi-threaded programming comes with certain warnings about how complicated it can be to get it right and to not have your app crash but this is in fact not very complicated I've got a background bitmap loader class with simply a subclass of ennis object that I created here it's got as its attributes ape ask you that keeps track of its to do this is basically it's to-do list these are the pads of the image files that I'm supposed to load we've got a lock that we use to ensure safe access to the path q so I need any time some code accesses the pass q it takes this lock and then release this lock when it's done I've got a simple flag here to keep track of whether the thread is running because I don't start it immediately when I create the object and now we've got a delegate here which is our connection back to the object on the main thread in this case I've chosen the document object as the objects that receives the images as they're loaded so you create this object with a delegate you can set its delegate and when you want to load an image we create one instance of the background bitmap loader when we want to load an image we send this request bitmap at pass message send it the path and that returns immediately to the caller but stuck the path in the queue already and it's on its to-do list for images to load we also wanted to have a cancel qwest message that i can send so that if we close the document let's say before all the images are done loading we want to be able to safely aboard out of this so that we were not messaging back to a non-existent document object and then the implementation the interesting methods here request bitmap that path as i as i said we take the lock on the path cube before we manipulate it we add the object add the path to the path q i actually make a copy here to be completely safe and then I check whether my thread is running already it's not i detach a new thread this is the method i was pointing out on the slide that is going to send the message load q damages to actually myself in this case the background image loader object and I don't need a parameter in this case I just want it to start loading all the images in the queue implicitly similarly for later when we want to cancel all requests all we do is take the lock drain the queue of all the past have been queued up and release the lock so this is the work force method of the thread load cute images we create an auto release pool here to make sure auto released objects are cute are automatically taken care of once the method exits and then I've got a loop here where I basically go until I run out of paps each time through the loop I look and see if I have another path to process I got to take the lock on the path cube before I touch it I grabbed the next pass in the queue remove it and unlock the queue because i'm done with the queue so the main thread is now free to add request to it and now if i've got a pass if the queue is an empty now i go ahead and basically load my my bitmap image rest using anna status and it with contents of file and then and it's bitmap image reps and it was data I check whether I still have a delegate and the delegate knows how to handle this message and I message it back by sending it because I don't want to just send it the image it's going to get the image and say well yeah okay which image is that I want to send it also the path to the image so I just wrap those up in a dictionary let's see I need some code wrap here yes I mean now okay that's much better so I create a dictionary and the only sort of unusual thing to notice here that I'm doing is in terms of the normal object ownership rules usually you hand something off to another method and you want to auto release it so that you've discharged yourself with responsibility for releasing it when you're passing an object to cross thread boundaries you kind of want to make sure that that objects still going to be around by the time the the other thread is grab hold of it and retains and so forth so so I'm sidestepping the usual rules and I'm Alec and hitting this dictionary and I'm passing across using perform selector on main thread I tell the object on the main thread the delegate the bitmap has been loaded and here's the dictionary that has the path from the bitmap itself wait until done basically says return immediately as soon as you as soon as you register this method to be message to be sent on the main thread just come back immediately it's safe to do because I've already made the bitmap info it's got to retain count of one it's going to stick around until the main thread takes responsibility for it and then that's it once I've handed off I can let go of the bitmap the data and the path and I'm also using an inner auto release pool here because I may be loading you know hundreds of images this is an important point when you're writing a secondary thread method is that I'm loading potentially many images I don't know how many could be hundreds could be thousands and I could be if I'm auto releasing things I could end up filling up memory with all these images that have sort of been handed off and still sitting there waiting to be Auto release so what I want to do is clean up each time through the iteration you don't necessarily have to do this every time through an iteration you might if you're doing 10,000 iterations do 100 iterations with 100 and have an inter auto release pool only in the inner loop but basically every time we hit this inner pool release we're going to clean up any auto release objects that were created during the course of that loop so that the most interesting part of it in the photo object itself where we used to load the image immediately we simply load a placeholder image that's small and quick to load and is only loaded ones because it's part of our resources and in the page view itself oops I'm sorry in the document class itself we have the bitmap loaded method that receives this dictionary this is the other side of the gate and this receives the dictionary passed off by the secondary thread hand it off to us we get the pass in the photo that are in the dictionary and we basically all we have to do here is identify the photo that the photo model object that that image belongs to and assign it and then we tell the page view hey you've got to redisplay this photo because something's changed about it and that's that we've got a multi-threaded version of our scrapbook app without a whole lot of effort let's go back to the slides that we could it will be eventually so what are some other techniques you can use that relate to app kit components and facilities this slide wasn't even here until a few days ago when I spoke to another developer who reminded me of how important this is and it's not something that people I didn't realize that this isn't something that people usually think of you want to try to avoid overusing views user are great and flexible and powerful they provide a large number of capabilities maybe more than you realize you can nest views within views and every view have in theory at least this capability although you don't usually create a control let's say and give it a subview controls are usually the leaves of the view hierarchy but this this potentiality is built in to NS view at the root class you can mess use in a tree you can rotate a views frame this is rarely done but you can actually take a view and set its frame rotation and it will rotate within its super view relative to its super views coordinate system you can have arbitrary coordinate transforms with interview hardly anything uses this most of the time views use the default coordinate transform if anything maybe they'll flip their coordinate system but that's it they have capabilities to support tooltips tracking Rex cursor X all kinds of bells and whistles and of course we have optimized for the common cases there are fast paths in the app gate code for the common cases where you're not using all of these features but at the same time just having to check for whether each given view when we process it is might be using one of these features has a certain cost there's overhead in generality and having all that generality in the base class so absolutely do use views and reasonable quantities there's no reason to get worried about using views and your preference panels and controls and oh do I have too many views here you know if you've got under a hundred views in a window that's completely fine usually the numbers even smaller than that but what I'm trying to suggest not doing is using very large numbers of views maybe hundreds or more one thing that I didn't do in that application is I didn't make every photo for example of view I could have implemented it that way I could have made it a photo of you that just draws the photon I could have made those views sub views of the document view and maybe that would have been okay for a couple dozen photos but if I got into nesting things within other things if I'm doing a complicated graphing application it's not the kind of thing where you'd want to model your document by having views within views within views to very large numbers consider instead using lighter weight objects it's very easy to derive something from nsobject teach it how to draw itself get it give it its own draw method you can you can define what the API looks like for a simple object that just needs to know where it's located and how to draw itself so I recommend avoiding overuse of views in extreme situations that's stressing the system a bit and will impact performance if you have custom views that you draw in your applications in particular large document views let's say you have a view that covers a lot of your window and draws a lot of complicated content you want to basically be lazy when you're drawing them try to avoid drawing stuff that you're not being asked to draw when your view is asked to display and as I alluded to before you want to avoid invoking the display methods we have a number of display methods on an NS view that are there for you to use if you want to really if you really mean that you want to force a view to display immediately but doing that has a cost particularly if you have different potentially different parts of your application that are all saying the same thing to the same view they will cause it to redisplay itself cause an actual redisplay of the window in fact or the dirty parts of the window every single time that they are invoked instead it's at all possible defer the drawing you set needs display in rec and set needs display if you absolutely don't really know you know what particular part of you might have become invalidated you just want to redraw the whole thing do that but ask yourself is that the best I can do whenever you're doing that oftentimes it's some state has changed into view maybe only some part or parts of the view have been affected mark those individual parts dirty and you tell us by doing that that there's only these parts that need to be drawn and when we call you back we tell you hey you only need to draw these parts here's a real simple cheap optimization this is an easy one liner you can add to your custom views and the reason for the existence of the is opaque method i should point out is that by default app kit cannot assume that a view will draw with complete opaque coverage of its background our aqua widgets are often non rectangular in shape they have nice rounded corners and so forth so this is less an issue of transparency as of coverage basically this is saying it so if I'm if I'm not opaque if I'm a custom view and I'm not opaque I'm saying that I may need some of the background of the provided by the views higher up than me and and the window background to show through to complete my drawing I only draw some stuff on top of that if you don't need the background to show through declare yourself to be opaque this saves app kit from drawing all the stuff behind your view if you've got this huge document view in particular that's covering your window making not if you don't make it opaque we're going to think okay we got to draw the window background behind it okay now we've got to draw your view we don't know that you're covering it we don't know what you're doing in your drawer XO be opaque when you can also when your draw rect method is called draw minimally try to draw only what we're asking you to draw the simplest first level optimization you can do is simply use and observe the drug the single rectangle parameter that is passed to draw rect that tells you the basically a bounding box on the area that that needs display and you can use NS intersect wreck for example to test against that bounding box if you have a number of objects for example here drawable things that we need to draw in our view this is a common case for each object before I go to the trouble of drawing it which may be costly see if its bounding box intersects the bounding box that I've been given if not no bother drawing on Panther and tiger you can further constrain drawing beyond that beginning in Panther we've been keeping a more detailed accounting than we used to of specific regions within a view that need drawing we keep actually we can give you a if you ask for it a list of rectangles that are non-overlapping that specify the area that needs to be drawn so you can get this directly using the get Rex being drawn count method if you want direct access to that rectangle list and if you use those wrecks you can use the parameter to draw rect you can still use that as a bounding box to do trivial rejection testing you can first check your objects against that bounding box if they lie within it okay well maybe they're in this list of wrecked somewhere maybe they intersect that list of wrecks when you drawing alternatively in the common case like we had before on the previous slide where you're just iterating over a list of things to be drawn you just want to test each of those things in sequence and not do anything fancy you can use needs to draw a wreck which was a new method provided in Panther that will do all the testing against this list list of wrecks for you it will do the trivial rejection test against the bounding box so use that that's sort of a more modern alternative to NS intersects wrecked that we have in this code sample if you're doing panther and later you can use that what about speeding up window resizing window resizing obviously is partly dependent on view drawing window resizing is something that heavily exercises hbu drawing when the user grabs the corner of your resizable window and starts dragging it basically at the worst case we have to redraw the entire window for every iteration every step of that drag if it takes your window longer to draw you have slower updates then you're you're resizing is going to chunk along on the other hand if you if you make the updates shorter and faster we get a higher frame rate a much more fluid motion and much more satisfying user experience so obviously anything you can do to optimize view drawing will also affect give you a payoff in window resize there are also some further things you can do specifically for the case where window is being resized not just drawing your views more quickly but preserving view content wherever possible one thing you can do that's been around for a long time is the concept of live resize I think since the beginning of cocoa a view when it's drawing itself can check whether it's being drawn in the middle of a window resize using the in live resize message that's highlighted in orange up at the top there if you are in live resize you may want to take advantage of the opportunity to do some less expensive drawing than you might otherwise do because you're deciding hey you know if I'm just going to be drawing a whole bunch repeatedly real fast you know maybe I don't need to be pixel exact maybe I can take a cheaper faster route and and trade some accuracy for performance let's say we also have view will start live resize and you did end library size or two methods that you can override to prepare to enter library size mode and prepare to do any cleanup you need to do after exit so those are a couple of additional hooks that you get before your that all views get this message sent to them before the window starts going into library sighs and then after it ends so if you need to do set up or clean up that's where you would do it in addition on tiger we've been doing some further optimizations to allow windows to preserve content when they're resizing one thing you'll notice when you resize a window oftentimes a lot of the pixels aren't really changing they don't need to be redrawn we rendered and to help support that we've added new API in tiger on NS window and NS view a window has the capability to be put in a mode where it preserves its content during library size and a view that is savvy about this mechanism can implement certain functionality to help support it to make it possible this is something that propagates down the view hierarchy so in order for view to take advantage of this all of its super views have to be clued in and know how to use this mechanism and we're working now actively to optimize the various using especially the container views in app kit that will help support this and an example of how you would use this if you have a custom view class my view you override the preserves content during live resize method this is a simple thing like is opaque you're just returning a bool to say hey I support this feature I'm clued in I'm in the know and I know what to do to help support this live resize mode then you override usually set frame size or if you have some other tile methods sometimes you'll override a different method to do this but usually set set frame size is a good point to do this you ho can you pass on the message to super first to do whatever normally would need to be done and then in addition you can check whether you're in library size mode this is again that in library size message if we are in live resize then what we become responsible for doing if we have declared that we support this feature is dirtying the parts of myself of the view that will need to be redrawn to accommodate the new size you know usually and when you're redrawing in a window if your views content is pinned to the upper left corner of the window which is the case that we support right now then usually you have sort of an l-shaped update region on the right-hand side and the bottom side as the user is resizing the view larger so you have these two strips that need to be drawn and in fact app kit computes these and figures them out for you so all you really need to do at this point is get the rectangles from app can we have this method get Rex exposed during library size count and you get back account of a number of wrecks and you get back two rectangles it's guaranteed to be never more than four rectangles and then you can iterate over that list of rectangles and mark yourself dirty in those areas so this is something that you do only during live resize when we're not in library size we simply do a full set needs display to require the entire view to be redrawn so that's kind of not so interesting in code let's look at a demo and see how this works so I've got my old scrapbook app here and one of the things you may have noticed as I started resizing my view is boy it's really chunking along there I mean that's just too slow now to be completely honest in disclosing what I'm doing here I'm not exactly trying hard to be very smart about the drawing on doing we've only got two dozen photos up here but what I'm doing is these are full-size images in memory and I'm scaling them down every time I draw them so I'm doing sort of an expensive unnecessary operation if I really want to make this fast one of the things I might do is resample the images as they're loaded down to thumbnail size since that's all I'm drawing the maps I don't need the full detail but you could suppose that you have some other objects that are complex and expensive to draw so you know this is just kind of embarrassing on a dual proc g5 we shouldn't be chunking along this flow and in fact if we run quartz debug this is a useful feature for for growth display speed measurement if you're not familiar with it there is a frame meter in quartz debug that's available from the tools menu show frame meter and you can use it as you tells you basically your number frames per second on the red gauge there as you move windows around and as you resize and if we start measuring this just sort of informally dragging it around I'm not even breaking ten frames a second there this is really embarrassing so what can we do about this well start by dragging okay scrapbook to the trash and luckily I made a copy of it first and we've got this better scrapbook and i'll go ahead and hide that and what I've done in my page view this is my custom document view class is I have overridden just as I said preserves content during library sighs I've got an I've are for this setting so that I can show it in the demo but basically we'd always return yes if we know that we want to support this feature and then inset frame size just as I showed you this is almost an identical snapshot of the code that was on the slide we figure out if this option is turned on for the demo okay yeah we want to preserve content if we do want to do that we check whether we're in library size and just as a paranoia check I checked whether we actually support this this message in NS view in case I was running this on Panther you know remember you can do these kinds of checks so that you can take advantage of a feature only if you're running on the newer version of the system we get the rectangles being exposed we mark them as meeting display and let's see if that was even worse all that trouble was two methods does the application here drag the album in we've got our background image loading happening and so again flow before if I go here and turn on preserving content during live resize look at that that's just silky smooth now we go up to 60 frames a second so that was significant in this case [Applause] and in fact while we're at it before we leave the demo machine let's look at another optimization that I put in there we've got preserving content but supposing also that you just want to take advantage of life in library size as I was saying to just do simpler drawing I've gone to an extreme here and I've got a mode where I can have my view just draw the photos as simple outlines just real simple stroke boxes during live resize mode I enabled that optimization and as soon as I you know things are normal when I'm just dragging things around but as soon as I grab the window quarter and start to resize everything changes to boxes and I can resize all I want obviously this is extremely cheap drawing so you know I could be on any old machine even if I'm on a limited memory I book or something like that this would be very fast and this technique is usual usable way back on earlier versions of OS 10 so you probably wouldn't want to go to this extreme I'm sort of doing it to make a point that you can do very different drawing entirely different drawing if you want or you can do somewhat similar drawing is the more likely case that is somehow less expensive and approximate in some way while you're in live resize mode you can basically you can do anything you want in this mode and what I've done to implement this is fairly simple there we go here we have the draw rect method and I guess we didn't look at this before I'm doing some of the other recommendation of other optimizations i recommended we're getting a list of wrecks that we're drawing so that when we go to do our background fill we can only fill that list of wrecks instead of trying to fill the whole view we do clip for you when we're drawing just specific areas with a view so even if you don't obey that list of wrecks even if you just look at the bounding box or just try to draw you know all over the view we will clip your drawing just to the area that that drawing is being requested in so but you still save something by not even issuing those drawing commands in the first place when you have a choice about it so we're drawing the background fill using that list of wrecks and the interesting part here is if I have this draws outlines in live resize mode enabled and we're in library sighs mode then instead of drawing the whole photo I'll just get the frame of the photo and i'll just use bezzie a path to stroke erect and light where it would be and that's pretty much all we have to do the only other thing that I need to add is in view will start live resize and view did and live resize I need to implement I need to tell the view to redisplay itself because when I grab that corner of the window and I start dragging if I don't do this then I will get the complete photo drawing on the one hand and then as I start dragging new stuff that appears will be drawn an outline and that looks kind of inconsistent and weird same thing when you're exiting live resize mode you want to redisplay the whole view at that point to go back into the old mode and reflect the change so okay if we could go back to slides please thank you scrolling not a whole lot to say about scrolling except that obviously it hinges somewhat on on view drawing performance and in some sense it's related a bit to window drawing window resize performance because it exercises you drawing intensively so if you want if you have a document view that you're going to put in a scrollview and you want your scrolling to be smooth and fluid one thing you can do is make sure that your document view uses these facilities that I pointed out earlier and can efficiently draw small bands of its content because that's those are the kinds of requests that you're likely to get during a scrolling operation lots of small little incremental Scrolls where you just have to draw a little strip and the more efficiently you can determine oh I don't need to draw this I don't need to draw that all this other stuff that's not in that strip the more quickly you can draw on the smoother your scrolling will be another little feature of scroll view and clip you to be aware of by default they are opaque they're set to draw an opaque background behind the document views of their size the clip view area size bigger than the document view you will get opaque fill all around there for best performance leave it that way unless you really really want that outside margin area to show through to the views on the window behind because it does significantly impact drawing performance and scrolling performance within the scroll view so that feature is there to use a be aware that that has a performance cost string drawing we provide very convenient methods as categories on NS string you can draw a string at any point or within a bounding rectangle and supply a dictionary of text attributes and we'll just do that for you I mean that's very little work to draw a string that's a one-liner right very convenient but note that when you invoke these methods to do this convenient string drawing what we have to do under the hood to implement that is potentially set up the text system wire together the various objects if you work with the cocoa tech system you know there's a lot of different things involved text containers layout manager and the text view itself and so forth so we've got it we've got to figure out how to layout the glyphs for the string that you've given us and then we've got to do in the actual rasterization or drawing of the glyph this is a lot of stuff going on under the hood now this is an area that's being actively optimized we do provide some degree of caching obviously it's sort of a general Cashin we can't know what the behaviors of your applications will be because every application is different but we do attempt to avoid these costs so you should see this improving on Panther and tigers continuing to evolve but it may still be advantageous to use an old technique that was discussed years ago and is illustrated still best illustrated by the worm demo that's available in dem developer examples app kit if you're repeatedly drawing the same strain with the same layout you need only compute the glyph layout once right the glyph layout isn't going to change and so for that reason and others you can get some performance benefit by assembling your text objects and hanging on to them if you just do a little manual mucking with the text system allocate your text storage object that text storage is an object that contains both the characters and the attributes applied two runs of those characters allocate your own layout manager allocated text container wire them together and hang on to them keep them around and use them every time you need to draw the glyphs then all you have to do is message the layout manager directly say draw glyphs for glyph range and if you play with the worm example you'll see that this makes a tremendous difference when you're rendering especially on older versions of the system when you're rendering text repeatedly desi a paths not a whole lot to say about veggie a paths but if you're drawing really complicated bezzie a path I'm talking about paths with hundreds or thousands of elements be aware that there are crossing calculations that have to be done and the scaling of the algorithms that necessarily need to be used to get the closures right and get everything right with the bezier patch rendering it doesn't scale too well when you get to very complex paths so one thing you can do if you don't need to get if you're drawing tasks like this that are very complicated if you don't need to get exact exact results you can split those paths into segments and render them in sequence and it looks like pretty much the same thing except you get a big performance win from doing that also if you have passed that you're creating over and over again the same path drawing it over and over again stroking or filling it keep them around and this applies in general to a lot of different types of graphics objects if you create them and hang on to them there's a lot more caching that we can do and that other layers below us like core graphics can do to recognize oh yeah that's the same thing I saw before edge you know draw that again and also you're saving the object allocation initialization the allocation there are a couple of interesting things to be aware of with relation to NS image set data retained we have this notion of where an image is able to reference its data source rather than rather than actually containing the image data if you initialize an NS image using either in it by referencing file or an it by referencing URL you will get an image that for example when it's archived instead of archiving itself with all the image data that load from that file it just archives the reference to the file or URL and then so that creates a very compact representation some people use it for that purpose of the thick and maintain the reference rather than just copy the image in but be aware that if you're drawing that image repeatedly and you are in particular changing its size this may cause NS image to go back to disk or go back to retrieve the image from the URL every time that it needs to resize it reek a at a new size to draw remember NS image does do some caching in the background that's transparent to you so if you if you're using a reference damage and you're resizing the window and things are chunking along this is a thing to look in to check whether this setting is on and be aware of it and if you need to resize an image try and you know resize it once and get the size you want and leave it that way there's also this notion of caching separately when you've used NS images if you've ever dumped a debug description for an m and s image you may have seen cached image reps and you may have seen these they're part of the public API a cached image rep is a representation of an image that usually is generated from something else and drawing you've captured from a view or from resizing an image that you originally loaded as a bitmap cached image rep is an image that lives in an off-screen window somewhere and that's how where the window server is keeping it you know in a screen compatible pixel format usually you know ready to go this is sort of an optimization keeping these cached images now by default an image is not cached separately in its own window we have the possibility for NS image is allowed to cash images together and to just sort of grow this shared window or shared windows as it sees fit when it needs to load additional images if you have large transient images that you're loading using NS image and load them and then they go away and you're not going to replace them with another large image and you want to avoid the potential resource usage cost of this cache window growing and growing and growing you may want to set set cached separately to yes to force the use of a separate off-screen window for that particular image that image will stand alone in the off-screen cash at the windows server that NS image uses so this is a good strategy to use for transient images so that's issues that generally pertain to app kit api's let's dive in deeper now and look at things that are more at the foundation level in particular we'll look at notifications these are venerable and widely used and still very powerful and important mechanism and app kit ways of accessing collections and strings are sort of a kind of collection more efficiently using Auto release and the memory management mechanisms that obviously and cocoa provide the immutability concept and also techniques for working with property lists so notifications first of all notifications are a very powerful and flexible mechanism for a given object to broadcast volunteer information about some interesting event or potentially interesting event that's occurred in it to any number of other observers that the object isn't aware of the observers don't need to be aware of one another they can all register with the central Notification Center and the Notification Center mechanism provides sort of the loose coupling that allows all these objects to communicate about a particular topic we're not necessarily knowing about one one another as an observer you can subscribe for specific notification posted by specific object or you can cast wider nets and ask for all notifications posted by a given object or all instances of a particular notification regardless of the object that posted them you can also use the distributed notification mechanism to pass notifications across process bounce is a very powerful general facility it's been in the app kit since the beginning but there are certain performance issues associated with its use particularly abuse one thing to be aware of is that when you post a notification there's a cost there even if nobody is listening obviously we've tried to optimize this to make it a fast pass within the notification centers dispatch mechanism but even if nobody's listening there's a certain cost involved in checking to see whether anyone is listening and this gets to be a problem in particular if you are sort of speculatively writing classes if you're writing code maybe that's going to go in a framework once again that's sort of intended for general reuse by potentially unknown clients clients you may not know about right now you may not know well what sorts of of notifications might they be interested in what kinds of changes in me might they want to know about so you get into this problem of sort of speculative posting you you tend to err on the side of caution and post notifications for all kinds of things potentially just in case somebody might be interested in listening for that that's a bit of a problem obviously because of the cost of posting even when nobody is listening these kinds of things add up so try not to try not to speculatively post try to restrict your usage of notifications somewhat to really sort of important things that need to be broadcast also note that repeatedly adding and removing observers from the notification center forces its dispatch tables to churn and and that has a performance impact too so in particular if you have transient short-lived objects and you're manufacturing these and discarding them in large quantities in your application and each of these objects maybe is observing a notification or maybe several notifications when they get created they add themselves with an observer to the notification center when their dialect they remove themselves these objects are coming and going a lot you're hitting the notification center pretty hard and this may start to turn up in your samples in shark so consider consider alternatives to that and we'll look at some also note that for the general case of local notifications usually you're not using distributed notification these are handled synchronously when a notification is posted control does not return to the poster until every single observer of that notification has been found and has been given in series it's chance to execute whatever it code it has to execute in response receiving that notification depending what you're doing depending how many observers there are obviously you as the posting class you may not know about all the observers or how many they're going to be this could be costly this can block your application for a fair amount of time so what can you do if you are going to use notifications to get around this in general help us help you be selective as much as you can about what kinds of events you really need to broadcast to the world be as specific as you can as an observer when you're registering for notifications if you know the name of notification you want and you know the object register using a non nil name and object these types of entries in The Dispatch table or in general easier for us to optimize and find find quickly to do the efficient dispatch you may want to if you have several notifications for a given object you can add yourself as an observer with nil is the name and just the object you can also remember as an alternative to that you can add yourself several times as an observer for once for each notification depending what version of the system you're running on that may or may not be a performance advantage so measure to see if that's an issue also obviously the notification handler methods that i mentioned that that block while they're processing and don't return control to the poster until they're done obviously make these as efficient as possible and if they don't have to do work right away and you can defer it defer that work and as I said avoid repeated removal and addition as observers if you possibly can one way around this is to consider using a longer-lived intermediary object that keeps track of all of these transient objects and directly messages them when it receives the notification as an example of this let's say we have an originator object that volunteers some information about itself by posting a notification it posts a notification of the Notification Center and if we do this in a really simplistic way we'll have all these transient objects here that are coming and going they're adding themselves as observers to the Notification Center they receive the notifications directly from the notification center when they are posted an alternative to this is to drop a longer-lived object in the middle there and have it be the only observer that is added to the notification center it's maybe some object that knows about all of these other transient objects and can more efficiently dispatch this information to them by say sending a simple ibc message to each object you've got less overhead there because you've got fewer entries in the notification center dispatch table at any one time and also you're not adding and removing them and turning that table over and over again so this is one technique you can use if you're seeing a lot of load in the notification center consider using intermediate objects it's an alternative to using notifications you may not have considered key value observing and key value binding these are powerful technologies that were introduced in Panther that basically enable you one object to observe value changes in an attribute of another object or bind one of its attributes to the attributes of another object you may not have thought of these as alternatives but really they're just sort of another way of propagating changes through your application one thing one advantage to this approach is you don't have to anticipate what others might be interested in you just have to make your your accessor methods key value coding compliant there's no performance penalty for unobserved changes because you're not posting trying to anticipate whether others may be interested in there's there's no overhead involved for an object whose attributes aren't observed or for a particular attribute that isn't observed to change so obviously that lists the burden from you too of having to figure out what others might be interested in and it facilitates a similarly loose coupling to what notifications provide I'm not saying that notifications are going away by any means they're hardwired into a lot of what we do in the kit we have notifications that objects advertise they'll continue to provide those like nfcu provides view bounce to change view frame did change notifications when it's geometry changes there are lots of things that are built on that but if you're writing new modern code you might want to consider KBO and KBB is another way to go if notifications aren't appropriate performance wise also obviously if your problem is simpler and you don't have a whole lot of objects you have to notify consider the delegation pattern that we use throughout the app kitten foundation just have an object have a delegate that it passes off a message to the simple objective c message sent to a single receiver with the same cost collection access you may be able to infer a lot of the performance characteristics of accessing collections from the general character of those collections obviously there are some things you can perform efficient random access is on like a raise if you iterate through an array doing linear search you know same thing is on any other platform that's a no event operation for sequential access through objects we do provide NS enumerator it's a very convenient sort of collection agnostic mechanism that we provide for sequential odd object access within a collection so you can you can ask an NS array or can be an unordered collection like an NS said you can ask any of the common data collection types that we provide for an enumerator for their objects and then you just talk to be immune to mer ater communicate with it to enumerate the objects in the collection there's potentially more overhead involved in doing this there's more in direction than doing direct element access and it may you may find that it affects object lifetimes and you may start to see objects showing up in the auto release pool when you're using enumerators so there are convenience and I certainly don't want to steer you away from using them in the general case in most cases this is the best way to do things but in cases where overhead is important as you're iterating through a collection of objects you may want to consider alternatives one alternative is to is be make objects perform selector with object method we also provide a version of that with no object parameter you can message a collection and have it send if you want to send the same message to a whole bunch to all the objects in a collection you can use make objects perform selector to more efficient do that dispatch because the the collection class does that for you and it's familiar with all its data structures that can access the internals and do that a bit more efficiently so that's a useful thing to know about also in court found a shin there's a comparable facility the CF apply functions there's a CF array applies function CF dictionary apply function and so forth for the various collection classes that core foundation provides and they basically give you the ability to specify callback function instead of a method that's executed for every single object in the collection if you have an ordered collection and specifically an NS array you can use the object at index method and get objects range to directly access the elements much more efficiently than you might be able to with an enumerator and in particular if you're doing random access this is the only way to really do it so if we're iterating through an array using using these methods instead of using an enumerator one thing you want to remember to do is cash your account and in fact this goes for anything that any properties of an object that are invariant over the course of an iteration move the getting of those values outside of the iteration and you save time and other languages that you may have used this may not be as important but the objective c compiler is not able to to know that for example if i had written i less than bracket an array account bracket there the compiler would not be able to know we have no way of knowing that the count is not changing it's got to send that message every time it checks the determination condition for the for loop so keep that in mind if they're their messages that you send to get invariant quantities cash those instead of using them each time through the array instead of asking for them each time calling a method through an in I don't know how many of you are familiar with this technique it's been around for a long time it should be used with care as far as for one thing it breaks polymorphism it only works when all of the objects that you are sending a message to our in general of the same type actually they really just need to have the same method implementation so you maybe a common base class would be sufficient for a given message that you want to send them what what an imp is is an instance method pointer a pointer to an implementation method for a particular message you can get that and then send use that as basically a function pointer to call that method directly and this is particularly useful where you have a whole bunch of objects with the same type and you want to really minimize the overhead of communicating with them here in this code sample we say have a do something useful with message that we want to send to all the objects in an array first thing we need to do is get the IMP for that message and I take the object at index 0 as an arbitrary object in the array I'm assuming that this is a heterogeneous collection I mean a homogeneous collection rather excuse me and these objects are all are going to have the same implementation this code will break otherwise so I get the the IMP pointer so basically now I've got the function pointer i got i don't have to go through the odd seed dispatch tables for four methods i just call that function method directly as if it were a function so i get my my town of the objects from the array and i'm also here using its not highlighted but i'm using the get object method which enables you to retrieve at once a whole bunch of objects in fact in this case all of the objects out of an array there's also a get object variant that has a range parameter that lets you specify a sub range of the array whose and elements you want to get so here what we're doing is we're getting all of the objects out and we're malaking memory block to hold the is the the instance pointers for all of these objects so that we have them in a standard C array and then we can access them any way we want it's not essential in this case but but it's something that might be useful if we wanted to do other really fast random accesses so we get the objects out and then for each iteration all we have to do is call through the function pointer and the important thing here to note is the first two parameters when you send a message to an object using the usual odd C syntax the target is specified as the first thing in the in the brackets and then the selector is sort of collected together as the message to send different selector elements if you have more than one parameter these are sort of implicit parameters to the method implementation that obviously usually hides from you so when you do call through an imp you have to provide those yourself so it's the first parameter to this imp that we're calling we provide the object that's the target of the message we provide the selector just in case the IMP wants to check these because it can it can ask for it obviously obviously is going to need self and it might actually check the command that is being sent to it the selector so we send those and then we provide an argument here because in this case our do something useful with needs something to do something useful with so that was collections in general string operations are in some sense sort of a special case I mean string is really just a collection of characters right so a lot of the same conceptual ideas apply in particular you want to avoid intensive character by character access to a string as you can character it index is there if you just need to grab a character to out of a string once in a while but it's not really intended for heavy use and again this is the kind of thing that the objective c compiler can't really in line these types of things it needs to send a message each time in order to actually get the character so as alternative look at all of the methods that NS string provides the big header one of the bigger headers we have and they're all kinds of useful methods that can directly access the internals of the string and and can do more efficient operations if you're looking for substrings doing searches through a string and so forth and or doing mutations mutable strings try to use the end of string methods when there's one available when you're doing sequential scanning or parsing through a string searching for sub strings and wanting to keep track of your position in a scanner remember in a scanner it provides a whole bunch of methods for doing that and if neither of those facilities provides what you need you might just want to fetch the characters in blocks and you can use the yet characters range method to do that similar thing to what I was doing with get objects for NS array you can get the all of the characters out of Unicode characters into a standard C array and then you can do whatever you want with them including using C functions and so forth object management and responsibility responsibility for objects is a big part of any object oriented application and cocoa applications are no different we provide a number of convenience methods factory methods class methods on various objects for very quickly and easily with very little code getting back an auto released object it's important to remember that these are Auto release by convention when you use a factory method so if i call NS mutable array array I'm getting back an array that has been auto released so it's been added to the auto release pool for the current thread and it's going to have to be processed by that auto release pool later this is a lot more convenient than writing say NS mutable array alloc init and then doing an NS me remembering to release the array later it's less code and so it's convenient and it's ok to use in the majority of cases and less performance is really being impacted if you have large numbers of objects or large numbers of iterations that you're dealing with also remember and also remember that there's no need to auto release a temporary object that you're using just within a method body is something you're not returning back from the method you're just creating it and you're going to use it temporarily to do some operation get rid of it that would be a case where you can alloc init and release instead again not in every case is this really necessary but you know this becomes a significant fraction of what you're doing you might want to consider that keep that in mind this is this is obviously most worthwhile for objects that are created an auto released in large numbers and it avoids potential spikes in memory or resource usage remember when an object is auto released all these Auto release objects start to sort of pile up and stick around because they're waiting around to be disposed of when the auto release pool is cleaned up by default unless you're creating your own auto release pools that's when your code kind of finishes up and returns control back to the to the run loop usually after processing a user input event or something like that you go back to the run with all that stuff gets cleaned up but in the end if you're doing a whole lot of operations from creating a lot of auto released objects and maybe a lot of big auto released objects if you're doing say image processing or image loading as I was doing you can get spikes in memory usage where instantaneously you get peaks in your usage of memory so for example that's the kind of thing that you would see using object Alec one of the performance tools that we provide for you and it's also a useful debugging tool so we have an application here and I'll magnify where we've gone through a loop and we've basically allocated 10,000 auto release strings real simple trivial example we have a current count of only a thousand and 97 strings but at the peak we had about 11,000 strings that we're running around in this application hanging around waiting to be Auto released at the end of the loop and as we got back to the main run loop so this is what not o rly spike looks like when you see these long bars in object Alec you may want to look at what your auto release patterns are and whether there are cases where you could use additional auto release loop auto release pools to provide clean up before memory usage gets out of control it's not a big deal if you're not using a whole lot of memory but if you're really using big chunks of memory remember you're sharing the system with a whole bunch of other processes or their applications and if memory is tight you may start swapping and thrashing and then it becomes a performance issue so again an applications main thread has a default auto release pool that's provided for you when you auto release you don't have to worry about it just goes to that you can bracket operations with your own auto release pools as we've already seen reference counting when you every object every nsobject has a reference count that determines how many other objects are retaining it you get a retain count of one when you allocate and in it your object you go back to when you go back to retain count of zero the object is deallocated that's how we know when to get rid of it and retain and release and auto release so the mechanism for maintaining that that reference count for historical reasons the reference count itself isn't really an instance variable that's stored directly in the object it's stored off somewhere else in a table and when you do retain we do a release that's under the hood that's that's going to go access something somewhere else so if you have very heavily used objects and by this I mean objects that are created in large quantities and are often retained and released above and beyond the default reference count of one that you get when you Alec if you don't just allocate and get rid of it but y ellicott and there's some retaining and then some releasing and then you finally get rid of it if you have heavily used objects like this it may be to your advantage to implement your own retain release and retain count methods and what you can do is simply add a reference count variable an integer unsigned value to your class and here we have a my object class that does just that then you implement retain count to return your retain count in place of this user overrides of the standard nsobject methods we override retain count to return your retain count instead of the one that nsobject normally maintains and in this case we're using the convention that my rest count the I've are actually holds one less than the retain count so it's initialized to zero that's just sort of a convention we're using here and so when we want the actual retained count we add one to that then when you get a message to retain you increment your own reference count I've are that you're keeping track of and we return cells because that's the assumption for retain then when your object is released you check whether you're your own reference count has reached 0 and you d alec yourself otherwise if you're not quite 20 you just diplomat your reference count so this is not something to go out and do for all your objects but it's something that is a useful technique for heavily used objects not a whole lot to say about immutability except be aware of the concept and use immutable objects immutable variants of objects where we provide them and where you don't really need to be able to mutate an object after you create it that facilitates certain optimizations and potential optimizations we can do another thing that you'll see that's common practice is returning and you'll be encouraged to do is to return immutable I've are even if it mutable as immutable you can do this for example if you have this this class myclass that's got a mutable array as an i varsity track of a list of objects it's children and it can be asked to return those by typing the the children access or as NS array instead of NS mutable array we're sort of we're making a contract with the client that we're going to return you an NS array and as far as you know you can't mutate it so this saves us from actually making an immutable copy of the Ray the array when we return it so we could have our children method they're doing a copy auto release instead but obviously that's overhead that's performance impact so by doing this you this is the usual practice it saves copying an auto release little activity it does sort of violates tricked encapsulation because someone could get hold of that array realize it's mutable and do something with it but that's malicious and hopefully you won't be doing that to your own data structures also note that if you're implementing your own immutable model objects you can implement the copy operation to simply do a retain if there is if your receiver is immutable copy can be made equivalent to retain because really how many copies is something that's not going to change do you need you can just sort of pretend to copy the same your hand back the same thing and that way you don't have a whole bunch of inflated memory usage in your apps it's sort of a way to provide singleton instances in a sense in the design pattern sense within your apps so we're almost done here one last thing I want to point out to you we have a binary format for property lists you've all seen the XML format property lists provide a convenient way if you have data structures data storage that uses dictionaries arrays strings and all the sort of standard foundation types they provide a convenient way to serialize those out save them out to disk and so forth the XML format is great it's human readable you can go in and hand edit it if your user has corruption in a document they can send it to you and you can read it and say oh this is where this went wrong but we also have a binary format that is smaller it is faster to read and parse it is very easy to specify it when you're serializing out your data it's just another parameter and we have a PLU till command-line tool that you can use to convert back and forth between binary and XML all you have to do is specify the format to be binary when you're writing out your when you're serializing your property list using NS property with serialization api's that's it and then you get a binary representation handed back to you instead of the textual xml representation when you're reading back that data you don't even have to worry about it it doesn't matter NS property list c realizations api's we'll figure out for you whether it's being handed some binary or textual xml property list data so if you want that information is available to you it will tell you oh that I open this document it was it was XML or this was binary but you don't even have to be concerned with it when you're reading the files back so that's it for foundation technique just some quick conclusions to help you optimize better know the framework that's that's the I hope to take home lesson today and I hope I've provided something new for everyone with respect to that know the functionality it provides know where there are different ways to perform a different task different given tasks that may have different performance characteristics work with the framework wherever you can wear appropriate look beyond the framework remember we have a lot of facilities available to co collapse things like core data and court score you've heard about at the talk here opengl is the fast path for the hardware for for rendering not just 3d but also to d we have accelerate framework and encapsulate altivec based image processing and general computation and remember use the provided performance tools to measure because if you don't measure you don't really know what's going on and you may actually waste time optimizing in areas that you think a lot of time is being spent in but you may be surprised to find that time is really being spent elsewhere and you could spend your time more productively for more info we have documentation about performance in general and drawing performance in particular that you might want to look at on the ABC homepage Matthew Formica is tact for any questions that you may have subsequent to this conference