Transfer from http://www.tanhao.me/code/151113.html/
In developing software on mobile devices, performance has always been one of our most important topics, and we as programmers are our sacred duty to find and monitor the "culprits" in software that cause poor performance in a timely manner, in addition to efforts to improve code quality.
As we all know, the iOS platform because of the characteristics of the uikit itself, need to put all the UI operations on the main thread execution, so also many programmers are accustomed to some thread security uncertainty logic, and other threads after the end of the summary work and so on to the main line, so the main thread contains these large number of calculations, IO And drawing are likely to cause the Dayton.
In Xcode, we have integrated a very handy debugging tool instruments, which can help us analyze the performance consumption of software running during the development testing phase, but a software test process and laboratory analysis is certainly not enough, in the formal environment by a large number of users in the use of the process of monitoring, The data analyzed can solve some hidden problems.
Finding the starting point for stuttering
The most straightforward way to monitor the lag is to find out what the main thread is doing. We know that a thread's message event handling relies on Nsrunloop to drive, so you need to start with nsrunloop to know what method the thread is calling. Cfrunloop code is open source, The source code HTTP://OPENSOURCE.APPLE.COM/SOURCE/CF/CF-1151.16/CFRUNLOOP.C can be consulted here, with the main logic of the core method Cfrunlooprun simplified, presumably:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21st 22 23
|
int32_t __cfrunlooprun () {Notice coming into Runloop __cfrunloopdoobservers (Kcfrunloopentry); Do { The notification will be processed by the timer and source __cfrunloopdoobservers (kcfrunloopbeforetimers); __cfrunloopdoobservers (kcfrunloopbeforesources); __cfrunloopdoblocks ();Handle non-deferred main thread call __CFRUNLOOPDOSOURCE0 ();Handling Uievent Events GCD Dispatch main queue checkifexistmessagesinmaindispatchqueue (); Will enter dormant __cfrunloopdoobservers (kcfrunloopbeforewaiting); Wait for kernel mach_msg event mach_port_t wakeupport = Sleepandwaitforwakingupports (); Zzz ... Waking from the Waiting __cfrunloopdoobservers (kcfrunloopafterwaiting); //handle wake on timer if ( Wakeupport = = Timerport) __cfrunloopdotimers (); //handles asynchronous method wakeup, such as Dispatch_async else if (wakeupport = = Maindispatchqueueport) __cfrunloop_is_servicing_ the_main_dispatch_queue__ () //UI refresh, animated display span class= "keyword" >else __cfrunloopdosource1 (); //again ensure that there are synchronous methods that need to call __cfrunloopdoblocks (); while (!stop &&!timeout); //notice is about to exit Runloop __cfrunloopdoobservers (cfrunloopexit); } /span> |
It is not difficult to find Nsrunloop call method is mainly between Kcfrunloopbeforesources and kcfrunloopbeforewaiting, and after Kcfrunloopafterwaiting, That is, if we find that the two time is too long, then we can determine the main line incurring at this time.
Quantify the extent of the lag
To monitor the state of the nsrunloop, we need to use the cfrunloopobserverref, which can be used in real time to obtain changes in these status values, using the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
StaticVOID Runloopobservercallback (Cfrunloopobserverref Observer, cfrunloopactivity activity,void *info) { MyClass *object = (__bridge myclass*) info; Object->activity = activity; -(void) Registerobserver { Cfrunloopobservercontext context = { 0, (__bridge void*) self, Null,null}; Cfrunloopobserverref observer = Cfrunloopobservercreate (Kcfallocatordefault, Kcfrunloopallactivities, yes, 0, Cfrunloopaddobserver (Cfrunloopgetmain (), observer, Kcfrunloopcommonmodes); } /span> |
It is only necessary to open another thread, in real time to calculate whether the time between the two state areas to reach a threshold, you can pull out these performance killers.
In order to make the calculation more accurate, it is necessary to let the child thread know the main thread nsrunloop state change, so dispatch_semaphore_t is a good choice, in addition, the lag needs to be covered to a number of consecutive small and single-time lag two scenarios, So the judging conditions also need to be properly optimized. The logic to add the above two methods is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
StaticVOID Runloopobservercallback (Cfrunloopobserverref Observer, cfrunloopactivity activity,void *info) { MyClass *object = (__bridge myclass*) info; Record Status values Object->activity = activity; Send Signal dispatch_semaphore_t semaphore = moniotr->semaphore; Dispatch_semaphore_signal (semaphore); }
- (void) Registerobserver { Cfrunloopobservercontext context = {0, (__bridgevoid*)SelfNullNULL}; Cfrunloopobserverref observer = Cfrunloopobservercreate (Kcfallocatordefault, Kcfrunloopallactivities, YES, 0, &runloopobservercallback, &context); Cfrunloopaddobserver (Cfrunloopgetmain (), observer, Kcfrunloopcommonmodes); Create a signal semaphore = Dispatch_semaphore_create (0); Monitor the length of the child thread Dispatch_async (Dispatch_get_global_queue (0,0), ^{ while (YES) { Assuming a 5-time time-out 50ms thinks of the stutter (also including a single time-out of 250ms) Long st = dispatch_semaphore_wait (semaphore, Dispatch_time (Dispatch_time_now,50*nsec_per_msec)); if (st! = 0) { if (activity==kcfrunloopbeforesources | | activity==kcfrunloopafterwaiting) { if (++timeoutcount < 5) continue;
NSLog (@ "seems a bit of a card oh"); } } Timeoutcount = 0; } }); }
|
function calls that record stutter
Monitoring to the scene, of course, the next step is to record the function call information at this time, here you can use a third-party crash collection component Plcrashreporter, which not only can collect crash information can also be used to get the call stack of each thread in real-time, using the example as follows:
1 2 3 4 5 6 7 8 9 10
|
Plcrashreporterconfig *config = [[Plcrashreporterconfig alloc] Initwithsignalhandlertype: Plcrashreportersignalhandlertypebsd Symbolicationstrategy:plcrashreporte Rsymbolicationstrategyall]; plcrashreporter *crashreporter = [[Plcrashreporter alloc] initwithconfiguration:config]; nsdata *data = [Crashreporter Generatelivereport] ; plcrashreport *reporter = [[Plcrashreport alloc] Initwithdata:data error: null]; nsstring *report = [Plcrashreporttextformatter Stringvalueforcrashreport:reporter Withtextformat:plcrashreporttextformatios]; nslog (@ "------------\n%@ \ n------------", report); |
When the card detected immediately, grab the stack information, and then do some filtering on the client, you can escalate to the server, through the collection of a certain amount of data after the analysis can be accurately positioned to optimize the logic, so this real-time lag monitoring is done!
Article sample code Download: Performancemonitor.zip
"Go" ios real-time stutter monitor