2016-03-10 2 views
1

Есть ли какое-либо решение для профилирования профилей функций/методов на iOS с недавним Xcode (7.x)?Профилирование профилей функций/методов на iOS

Что я имею в виду под этим эквивалентом GCC's -pg, что заставляет компилятор вводить производительность в ваш код. Когда Apple все еще использовала GCC, я успешно использовал -pg и gprof на iOS, но, похоже, не похоже на эквивалент с clang.

Мне нужно выяснить, кто микро-блокирует мой основной поток в определенном сценарии (приложение все еще реагирует, но блокировка делает прокрутку неустойчивой). К сожалению, инструменты Инструменты не справляются с задачей: выборка (остановка приложения и запись трассировки стека) слишком грубая/неточная для моих нужд.

Так что мне нужен способ узнать, какой метод работал в какое время и на какое время, точно. Единственный реальный способ сделать это - либо заставить компилятор ввести нужный код (что и делает -pg), либо, может быть, какой-то творческий взлом во время выполнения Objective-C. Есть ли какие-то решения для этого?

+0

-pg и gprof так и не сделали. Все, что они делали, это подсчет вызовов и выборка ПК (что слепо для блокировки). [* Больше. *] (Http://stackoverflow.com/a/1779343/23771) Вам не нужна точность. Вам нужны образцы стека * в нужное время * - то есть когда процесс блокируется. В большинстве случаев профилировщики проб проб не проверяются при блокировке, поэтому это не хорошо. (Zoom может работать?) Я бы попробовал [* мой любимый метод *] (http://stackoverflow.com/a/378024/23771). Это может потребовать достаточного количества образцов, но вы поймете правильный, когда увидите его. –

+0

@MikeDunlavey: Спасибо за прояснение вопроса о 'gprof', прошло несколько лет с тех пор, как я его последний раз использовал. Ваш метод прерывания вручную интересен, я попробую его, хотя я скептически отношусь к природе проблемы (I_expect_ очень часто ломается по основному потоку, проблема в том, что метод на основном потоке занимает слишком много времени; пока не вижу, как ваш метод поможет мне в этом). Я уже использую инструменты, которые отсчитывают когда-либо 1 мс, но он не может сказать мне, является ли это новой итерацией runloop (на самом деле это необходимая информация). – DarkDust

+1

Иногда игровые программисты имеют схожую проблему, где очень редко что-то бегает по бюджету времени. Таким образом, это не общая проблема с производительностью, а скорее раздражение. В этом случае я предложил установить таймер в начале «кадра», который истекает после того, как кадр обычно заканчивается. Затем сбросьте его после завершения кадра. Таким образом, он прерывается только тогда, когда код кадра занимает слишком много времени. Затем вы помещаете контрольную точку в обработчик прерываний. Может быть, что-то в этом направлении потребуется в вашем случае? Не простой, но никакой профайлер не может найти такую ​​проблему. –

ответ

0

Рекомендация @MikeDunlavey для установки таймера была правильным решением для меня. Настоящий таймер не работал бы в моем случае, поэтому я использовал фоновый поток с высоким приоритетом и NSCondition (механизм нижнего уровня, такой как pthread_cond_timedwait, также работал бы). С каждой итерацией runloop условие запускается. Если основной поток занимает слишком много времени, условие прерывается.

Вот очень грубый код, который я использовал. Поскольку моя проблема заключалась в очень загруженном запуске, этого было достаточно для того, чтобы я обнаружил свои проблемы; если вы хотите использовать это для обнаружения проблем во время «нормальной» работы, вам необходимо улучшить его, чтобы обрабатывать ожидающую часть runloop.

Я не смог поймать все проблемы с этим решением; прокрутка по-прежнему немного нервничает, но это лучше, чем раньше.

- (void)startWatchdog 
{ 
    static NSDate * lastIteration; 
    static BOOL hasHandled = NO; 
    NSCondition * condition = [[NSCondition alloc] init]; 

    lastIteration = [NSDate date]; 

    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, 
     kCFRunLoopBeforeTimers | kCFRunLoopBeforeWaiting, true, 0, 
     ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { 
      NSDate * now = [NSDate date]; 

      if (activity == kCFRunLoopBeforeTimers) { 
       if (!hasHandled) { 
        [condition lock]; 
        [condition signal]; 
        [condition unlock]; 
       } 

       lastIteration = now; 
       hasHandled = NO; 

      } else { 
       // About to wait for events. We might want tell the watchdog method 
       // that it's OK that there's going to be a timeout. 
       [condition lock]; 
       [condition signal]; 
       [condition unlock]; 
       hasHandled = YES; 
      } 
     } 
    ); 

    NSThread * watchdog = [[NSThread alloc] initWithTarget:self selector:@selector(watchdog:) object:condition]; 
    [watchdog setThreadPriority:1]; 
    [watchdog start]; 

    CFRunLoopRef runloop = [[NSRunLoop currentRunLoop] getCFRunLoop]; 
    CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); 
    CFRunLoopAddObserver(runloop, observer, (CFStringRef)UITrackingRunLoopMode); 
} 

- (void)watchdog:(NSCondition *)condition 
{ 
    @autoreleasepool { 
     while (1) { 
      BOOL wasHandled; 
      NSDate * start = [NSDate date]; 

      [condition lock]; 
      wasHandled = [condition waitUntilDate:[start dateByAddingTimeInterval:0.2]]; 
      [condition unlock]; 

      if (!wasHandled) { 
       NSLog(@"-- Timeout: %f", [[NSDate date] timeIntervalSinceDate:start]); 
      } 
     } 
    } 
} 
Смежные вопросы