iOS上的函数/方法级评测
是否有任何解决方案可以使用最新的Xcode(7.x)在iOS上进行函数/方法级评测 我的意思是,编译器在代码中注入性能日志。当苹果还在使用GCC时,我确实在iOS上成功地使用了iOS上的函数/方法级评测,ios,performance,profiling,Ios,Performance,Profiling,是否有任何解决方案可以使用最新的Xcode(7.x)在iOS上进行函数/方法级评测 我的意思是,编译器在代码中注入性能日志。当苹果还在使用GCC时,我确实在iOS上成功地使用了-pg和gprof,但似乎没有与clang相当的功能 我需要找出在某个场景中谁在微阻塞我的主线程(应用程序仍会做出反应,但阻塞会使滚动变得紧张)。不幸的是,Instruments的工具不能胜任这项任务:采样(停止应用程序并记录堆栈跟踪)对于我的需要来说太粗糙/不精确 所以我需要一种方法来精确地找出哪种方法在什么时间运行,运
-pg
和gprof
,但似乎没有与clang相当的功能
我需要找出在某个场景中谁在微阻塞我的主线程(应用程序仍会做出反应,但阻塞会使滚动变得紧张)。不幸的是,Instruments的工具不能胜任这项任务:采样(停止应用程序并记录堆栈跟踪)对于我的需要来说太粗糙/不精确
所以我需要一种方法来精确地找出哪种方法在什么时间运行,运行多长时间。唯一真正的方法是让编译器注入必要的代码(这就是
-pg
正在做的),或者对Objective-C运行时进行一些创造性的攻击。已经有什么解决办法了吗?MikeDunlavey建议设置计时器对我来说是正确的解决办法。在我的例子中,一个真正的计时器是不起作用的,所以我使用了一个高优先级的后台线程和一个NSCondition
(一个较低级别的机制也可以起作用)。每次运行循环迭代都会触发该条件。如果主线程花费的时间太长,则条件中止
这是我使用过的非常粗糙的代码。由于我的问题是在非常繁忙的启动,这足以让我发现我的问题;如果您想在“正常”运行时使用它来检测问题,则需要对其进行增强,以处理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]);
}
}
}
}
@MikeDunlavey建议设置计时器对我来说是正确的解决方案。在我的例子中,一个真正的计时器是不起作用的,所以我使用了一个高优先级的后台线程和一个
NSCondition
(一个较低级别的机制也可以起作用)。每次运行循环迭代都会触发该条件。如果主线程花费的时间太长,则条件中止
这是我使用过的非常粗糙的代码。由于我的问题是在非常繁忙的启动,这足以让我发现我的问题;如果您想在“正常”运行时使用它来检测问题,则需要对其进行增强,以处理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]);
}
}
}
}
-pg和gprof从来没有这样做过。他们所做的只是计算呼叫数,并对PC进行采样(这对阻塞是盲目的)。你不需要精确。您需要的是在正确的时间(即进程被阻止时)堆叠样本。大多数堆栈采样分析器在阻塞时不进行采样,因此这是不好的。(缩放可能有用?)我会试试。它可能需要相当数量的样本,但当你看到它时,你会认出正确的样本。@MikeDunlavey:谢谢你澄清了关于
gprof
,我上次使用它已经有几年了。您的手动中断方法很有趣,尽管我对问题的性质持怀疑态度,但我还是会尝试它(我希望经常在主线程上中断;问题是当主线程上的方法花费太长时间时;还不知道您的方法将如何帮助我)。我已经在使用1ms采样的仪器,但它不能告诉我这是否是一个新的运行循环迭代(实际上,这是我需要的基本信息)。有时游戏程序员也有类似的问题,很少有东西超出时间预算。因此,这不是一个整体性能问题,而是一个令人烦恼的问题。在这种情况下,我建议在“帧”的开头设置一个计时器,设置为在帧正常结束后过期。然后在框架完成后重置它。因此,它只在帧代码花费的时间太长时才会中断。然后在中断处理程序中放置一个断点。也许你的情况需要类似的东西?这并不简单,但没有剖析器能发现这样的问题。@MikeDunlavey:这实际上是一个非常有用的想法!我必须找到使用nsrunlop执行此操作的最佳方法,但我肯定可以使其正常工作。谢谢!祝你好运这很重要。让我们知道结果如何-pg和gprof从来没有这样做过。他们所做的只是计算呼叫数,并对PC进行采样(这对阻塞是盲目的)。你不需要精确。您需要的是在正确的时间(即进程被阻止时)堆叠样本。大多数堆栈采样分析器在阻塞时不进行采样,因此这是不好的。(缩放可能有用?)我会试试。它可能需要相当数量的样本,但当你看到它时,你会认出正确的样本。@MikeDunlavey:谢谢你澄清了关于gprof
,我上次使用它已经有几年了。您的手动中断方法很有趣,尽管我对问题的性质持怀疑态度,但我还是会尝试它(我希望经常在主线程上中断;问题是当主线程上的方法花费太长时间时;还不知道您的方法将如何帮助我)。我已经在使用1ms采样的仪器,但它不能告诉我这是否是一个新的运行循环迭代(实际上,这是我需要的基本信息)。有时游戏程序员也有类似的问题,很少有东西超出时间预算。因此,这不是一个整体性能问题,而是一个令人烦恼的问题。在这种情况下,我建议在“帧”的开头设置一个计时器,设置为在帧正常结束后过期。然后在框架完成后重置它。因此,它只在帧代码花费的时间太长时才会中断。然后在中断处理程序中放置一个断点。也许你的情况需要类似的东西?这并不简单,但没有剖析器能发现这样的问题。@MikeDunlavey:这实际上是一个非常有用的想法!