AnsweredAssumed Answered

Memory bloat, CPU spikes, and UI thread locking with 10.1.1 SDK

Question asked by geonetadmin on Jan 23, 2013
Latest reply on Dec 16, 2013 by geonetadmin
Original User: bernese

I am seeing UI thread locking, very high CPU load spikes, and a much larger memory footprint after upgrading a project from the 2.3 SDK to the new 10.1.1 SDK and making no other changes. Within a test project I created to isolate the issue from my own code, I am seeing identical CPU load spikes, increased but more reasonable memory use, and some brief UI thread locking. Both projects target iOS 5.0 at minimum and are configured in the default manner for an iOS project using the ArcGIS 2.3 or 10.1.1 SDK. I have tested both on all versions of the iPad and with OS versions ranging from 5.0 to 6.0.1.

The use case I am benchmarking with in my production project is the following:
[INDENT]1) User moves finger across map to sketch a query geometry.
2) Query is performed against a dynamic map service layer using AGSQueryTask.
3) About 200 returned features are drawn in a graphics layer as composite symbols consisting of a picture marker symbol (pin) and simple marker symbol (colored dot).
4) User navigates features using either the map or a sorted table view, and can drill down into details/sub views for each feature. In my test case, I always query against a layer of state traffic cameras, so the user is looking at the video feed for a given camera.[/INDENT]

I have attached screen shots of my diagnostics using Instruments and refer to those when appropriate. I have also attached my test project.

In 2.3, the app (item iRaptor in the Instruments screen shots) develops an initial memory footprint of about 20 mb (see 2.3_baseLoad.png). Performing the described query and navigating the result in the table view increases that to around 65-70 mb (see 2.3_afterRendering.png). Removing the graphics layer from the map view and emptying it of graphics drops the memory footprint to around 30 mb. In general, panning/zooming the map or adding and removing a layer seems to generate a slight, persistent increase in the app memory footprint, presumably because of some caching action. CPU usage maxes out around 60-80% when interacting with the map and table view, and although map rendering sometimes takes a second or two to catch up to the user panning/zooming/selecting, no UI locking occurs.

In 10.1.1, the initial app footprint is about 50mb (see 10.1.1_baseLoad.png). Performing the test case query initially increased the footprint to the 350-400 mb range (see 10.1.1_afterRendering4GraphicsLayers). I initially tried troubleshooting this inexplicably high memory footprint, and discovered that adding 4 empty AGSGraphicLayer objects caused an instant memory footprint increase into the 350-400 mb range. This level of memory usage is high enough to trigger numerous first level memory warnings on an iPad 3 while viewing the query results. Adding the empty graphics layers also created a CPU spike into the 160-180% range (most of both cores on an iPad 3), and a complete lockout of the UI that persisted for 2 to 3 seconds.

I pared the 4 graphics layers down to 2; one for displaying the selection the user made and one for displaying returned graphics with any geometry type. Previously, I had a separate graphics layer for each geometry type because the SDK documentation indicates that this is best practice and there was no real cost in maintaining many graphics layers, only in drawing many graphics. With only two graphics layers on the map, the memory spike is down to about 160mb, which is too low to trigger memory warnings but still seems very high for adding two empty graphics layers.

However, getting the memory usage down has not affected CPU spikes into the 140-180% range when panning, tapping on a graphic to display a popover, or selecting a table view item and causing the corresponding graphic to display its popover (see 10.1.1_cpuLoadSpike_production). UI locking occurs during and after these spikes and can persists for 2-3 seconds, making the the map and table view entirely unresponsive. This makes 10.1.1 unusable for my production app. Again, no other changes were made and there were no issues in 2.3.

I created a simple test app using the 10.1.1 SDK that explores rendering performance of different layer types by plotting a single layer from a dynamic map service using either AGSDynamicMapServiceLayer, AGSFeatureLayer, or AGSGraphicsLayer. For the AGSGraphicsLayer, graphics can be rendered with a simple picture marker symbol or a more complex composite marker symbol. Additionally, the CPU and memory usage are displayed in one corner (these calculations match Instruments fairly accurately). Inspecting the test app in Instruments, I see significantly smaller memory bloat, CPU usage spikes of up to 160% when panning/zooming on either the feature or graphics layer, and momentary stuttering/locking of the map during and after these spikes (see 10.1.1_cpuLoadSpike_test). The dynamic layer re-renders with a CPU load of up to 110% and is generally more smooth/responsive. 

I believe that the UI locking issues on my production app are due to the CPU usage spikes during rendering. In my production app and when rendering feature or graphics layers in my test app, I can see that the generated CPU load significantly drives down CPU time given to system processes. If an app is using most of both cores on an iPad 3 when rendering the map, it seems reasonable to assume that, in conjunction with system processes, there are not enough cycles available to maintain a smooth UI, especially when displaying a table or content view alongside the map. My experience with iOS over the past few years/versions is that once a CPU spike of that size occurs and interferes with the UI and other threads, UI lock will persist for a few seconds after the spike has subsided as the system sorts itself out.

I am unsure about the memory bloat in my production app. As noted, I have determined that adding an empty AGSGraphicsLayer to the map has what appears to be an oddly high memory cost. To be clear, the memory cost is incurred when the layer is added to the map, not when it is created.

I benchmarked a few graphics-intensive iPad apps/games that use OpenGL for rendering and did not see CPU usage spikes or CPU usage anywhere near as high as I am seeing with the 10.1.1 SDK, so I don???t think it is anything intrinsic to using OpenGLES for rendering. For instance, InfinityBlade2 did not get over 110% during gameplay and was typically in the 50-60% range.

I noted a similar thread from a few days earlier, but wanted to add my in-depth analysis. I would love to hear of any solutions to the CPU spiking and memory footprint issues that can be implemented with the current SDK. If these issues will persist until an update is released, any sort of timeline on that is greatly appreciated since 10.1.1 contains a number of breaking changes and I would like to be able to balance the lost time of going back to 2.3 against my need to move this production app forward and plan for updating other projects from 2.3.