So I recently made some changes to one of my tableviews and I noticed that when the cells are being reloaded (coming back onto the screen one way or another) there is like a quarter to half second lag. I decided I'd use xcode 4's nice built in profiler. What I found is that during these spikes, most of the time running is being spent in gzopen and png_read_filter_row
I traced these functions in the call tree viwer all the way back to main, and it is never in any of my real code. The only questionable thing I am doing is small image files (1 per cell) each time cellForRowAtIndexPath is called.
I guess my question is this:
Is the file i/o that slow so that I should switch to caching these images in memory rather than on the file system?
How can I figure out what exactly it is thats slowing down my application so much using the profiling tool?
Thanks!
Can you load the images asynchronously - show a spinner where the image goes and fire off a background thread to display the image. This is a common solution to the "thumbnail image in a table row" problem.
I think you already have used the profiling tool to track down the problem: loading images. Admittedly, I am no expert on the profiler, but I think you know what you need to know. So implement your table a) without images b) with images loaded from files c) with images loaded asynchrously and stick with the fastest one. (I'm not trying to be glib, I'm just highly prejudiced toward getting the code out the door.)
Hope this helps.
-Mike
Related
I have the following line of code:
imageView?.tintColor = UIColor.red
This line takes a relatively long time to execute(In the simulator it takes about .35s versus the .20s it takes to load the entire rest of my interface). The view needs to load as fast as possible so although .35s may not seem like a lot it is important. The image in the image view is a simple template image. How can I go about improving the performance of this?
The only thing I can thing of would be to draw the simple image from scratch using core graphics but that seems like a complex answer to a simple problem. Any ideas?
Note: This image tint is changed several times throughout the time the app is alive.
The problem was that I was not specifically running the code on the main queue. This massively sped up the code.
I have a scrolling grid of photos that looks and functions pretty much exactly like the photo picker on the iPhone. It is constructed from a UITableView that uses a custom UITableViewCell which displays a row of photos (very similar to how Three20 implements it).
It works great except that scrolling performance is poor. I'm already following most of the best practices for fast UITableView scrolling (à la Tweetie).
The images are all bundled with the app. I load them on the UI thread, on demand. I use UIImage's imageNamed: so that the images will be cached after they're first loaded. Once I've scrolled through the table view once, it scrolls very smoothly.
The problem is, the first time scrolling through the table view, scrolling is jittery. I've profiled the app and found that the majority of the time is spent loading the images from the file system. They are JPEG images, already sized correctly (small). I tried using PNG images instead, but performance doesn't improve very much.
The iPhone photo picker exhibits much better loading performance. I'm wondering if combining all the photos into a single image, which I load once and then split into smaller images would be faster. It certainly works in games, but I know that's really a totally different story. Has anyone had experience doing that?
Any other ideas for how I can improve performance?
Incidentally, I'm having a similar, albeit less, performance problem for another UITableView that just uses standard UITableViewCells with one image assigned to the imageView per row.
One thing to try could be pre-caching all the images when the view loads. Beyond that, perhaps loading the images in the background (even though it's loading from the filesystem and not the web). I haven't tried this myself, but perhaps you could use something like https://github.com/rs/SDWebImage and have the URLs point to the filesystem. Users might see some placeholder images at first, but then the UI wouldn't stutter while images are loading.
Setting the PagingEnabled property to off improves the scrolling performance.
I had a similar issue using a UITableView to display information parsed from an RSS Feed. I ran into scrolling performance issues when there was a significant amount of data. Though I'm still working on it I suspend the parsing when the table is being scrolled. It resumes when scrolling stops. I am not at my computer, but I believe I used tableViewDidScroll and tableViewDidEndDecelerating. You can check these tableview delegates. In any case it works very well, I just need a more elegant way to pause my parsing.
Fantastic. That does work like a charm! So efficiently I ended up using insertRowAtIndexPath rather that reloading the table. Now it behaves the way it
Thanks so much!
Joe
I'm trying to investigate a very long start up time for my app and could use some help. It takes about 6-7 seconds to start up and that's just FAR too long. I'm not loading any data sets or anything, but the app is a calculator so my xib does have lots of buttons and button images.
I'd like to use Instruments (or something else) to analyze where all of the time is being spent so that I can optimize, but the documentation for Instruments isn't really giving me much on specifics of how to use it for this specific problem and/or how to interpret the results.
Do you have any suggestions for how to test this? A pointer to a tutorial on how to use Instruments for this? An iTunes U video or something else?
Any suggestions are welcome!
Kenny
Palimondo asked how I eventually solved my problem... it was a bunch of small changes and I'm not truly sure where the savings came in to play and I'll explain why below.
My app is a calculator, so it has a ton of buttons. I thought at first it must be the button loading causing the problem, but as I explained in the comments above to Paul, even if I loaded 0 buttons, that only shaved 1 second off of the start up time. After changing many small things, I was able to save 3 seconds off of start up time which was good enough.
I had a background image that was a nice textured-gradient-ish image. I reduced the file size on it (changed the texture, exported it with different options).
I went from loading 100 buttons up front (it's a calculator) to loading about 15, but attempted to do it in a secret fashion so the user wouldn't notice. About 50 of the buttons aren't visible to start anyway because you have to tap a Shift button to see them, so they were easy to lazy-load. The others are visible, but I figured that the user isn't going to tap most of those right at start-up because they need to hit some numbers first. So I left all of the number pad buttons and the Clear button in the xib so they are loaded on start up but removed everything else from the xib.
That left me with a big blank area on the screen and you could actually see the buttons filling in the blank area as I lazy loaded them (filling very quickly, but you could see the blank area and then all the buttons appear). So I updated the background gradient graphic to include images of the buttons. The buttons appear to be there, but they aren't, so unless the user taps a fake button within 2 seconds they never notice a problem... and usually they just try to tap that button again anyway and by then it usually has been lazy-loaded and therefore works.
Before doing any of the above suggestions:
Is this 6-7 seconds while running from XCode? Or from starting directly by tapping it on the iPhone?
Debuggers and instruments won't help you much here, since they just add to the overhead, and won't help as much in profiling because it poisons the data you'll see.
Edit:
In terms of profiling tools, you may want to look into using Shark:
http://www.switchonthecode.com/tutorials/using-shark-to-performance-tune-your-iphone-app
It's pretty simple to use in general. It's sorta self-explanatory.
Here are couple of suggestions:
do you need all those buttons loading up at once?
do you (over)use transparencies in your images?
are the images the exact size required? Stretching (resizing) takes time to compute
do you perform any operation that is blocking the main thread?
I would measure the startup time of an empty dummy app (maybe the XCode Window-based app template) on your actual device (debugger disconnected). Then start adding your initialization code, views and objects from your app into this dummy app until you find what's slowing the startup time the most.
So I've seen this question asked before and in fact I asked it last night but I thought I'd give it another go just to see if I could get any other unique views on the problem.
The Problem — I have an app with a large number of uiimageviews (with image downloaded to disk) in a scrollview which of course has two large problems facing it: Memory use, and performance. In my app memory use isn't so much a problem because I am employing techniques such as dequeuing and reusing imageviews and such. But performance is another thing entirely. Right now, as a memory saving procedure, I only store image filepaths in memory because it would be ridiculous to store images in memory. But the problem with this is that reading from the disk takes more time than from memory and slows down scrolling on the scrollview immensely.
So, what kind of techniques do any of you suggest for something like this? I've seen three20 but don't want to use it because I need high customizability in my view and that just won't do. Image files are not large, but just thumbnail size so there is no scaling or excess size. There's got to be an intuitive way to handle this. The built in photos app handles up to thousands of photos perfectly with low memory and slick and smooth scrolling performance.
Fundamentally, the problem is that you're probably doing a bunch of disk I/O on your UI thread, which is basically guaranteed to cause performance problems.
You should consider loading your images on a background thread and updating the image views on the main thread when the images are loaded. Depending on your use case you can get more or less clever about how far you preload in advance, etc, so you can have images ready. (There might be some usable source code or even Apple sample code out there that does something like this, but I don't know of it off the top of my head.)
You may notice that some applications (not sure about the Photos app) have an intermediate stage where they load a very small thumb size image for all images, and scale it up to the render size, which acts as a placeholder until the full size version is loaded-- if the user scrolls past that image before the full size is loaded, the visible effect is nearly the same as if the image was there all along.
I've got an app I'm working on where we handle a LOT of images at once in a scrollview. (Here's how it looks, each blue block being in image on a scrollview expanding to the right: http://i.stack.imgur.com/o7lFx.png) So to be able to handle the large strain doing this puts on memory. So I've implemented a bunch of techniques such as reusing imageviews etc which have all worked quite successfully in keeping my memory usage down. Another thing I do is instead of keeping the actual image in memory (which I of course couldn't do for all of them because that would run out of memory very quickly) I only keep the image's filepath in memory and then read the image when the user scrolls to an area of the scroll view near that image. However, although this is memory efficient, it's causing a LOT of lag in the scrollview because of the fact that it has to constantly read images from the disk. I can't think of a good solution on how to fix this. Basically right now the app draws to the screen only the visible uiimageviews and while the user scrolls the app will look to see if it can dequeue another imageview so it doesn't have to allocate another one and at that point it reads the image into memory, but as I said it's causing the scrolling action to be very slow. Any ideas on a strategy to use to fix this? Does anyone know what the native photos app does to handle this kind of thing? Thanks so much!
I can suggest you a simple solution to balance both the memory and the computer processing. You only keep small images like thumbnails in memory and only keep about 20 of them. One project that I am doing, I keep 20 thumbnail images (100 x 100) recently accessed, which doesn't cost a lot of memory. I believe that it costs about 200 kb all the time but comparing to a general available memory. I think it is good enough.
It also depends on your use case : if user scroll really fast and you don't know when will they go. You can have even smaller images than the thumnail and when you show it on the UIImageView, you resize it to fit. When user stops scrolling for a while. You can start loading bigger images and then you have a nicer images. User may not even notice about the process
I don't think there is a solution that can be fast and using as less memory as possible. Because we have memory, maybe not big but have enough if we use it smartly.
Slow scrolling performance might mean that you're blocking the main thread while loading images. In that case, the scrolling animation won't continue until the images are loaded, which would indeed cause pretty choppy scrolling performance.
It would be better to lazily load requested images in the background, while the main thread continues to handle the scrolling animation. A library that provides this functionality (among other things) is the 'three20' library. See the Tidbits document, and scroll down to the bottom where the 'TTImageView' class is described.
I had a similar issue with a PDF viewer, The recommended way to do this is to have as low a res image as you can get away with and if you are allowing the user to blow the image up/zoom, then have two versions or three versions of that image increasing the res as you go.
Put as much code as you can get away with in the didDecelerate method (like loading in higher res images like vodkhang talks about), rather than processing loads in didScroll. Recycle Views out of scope as you have said. and beware of autoreleased Context based Image Creation functions.
Load images in on background threads intelligently (based on the scrollView Offset position and zoom level), and think about using CALayer/Tiled Layer drawing for larger images.
Three20 (an open source iOs lib) has a great Photo Viewer that can be subclassed, it has thumbnail navigation, large image paging, caching and gestures right out of the box.