dispatch_group_async时间消耗很奇怪

时间:2014-07-14 10:04:38

标签: ios objective-c multithreading performance grand-central-dispatch

我有自定义单元格的UITableView。细胞的高度是动态的,因为它取决于其内容。所以对于快速滚动,我决定缓存单元格的高度。我用这个函数预先计算所有细胞的高度:

-(void)preCalculateHeightForCellsAndReloadTableAfterThat {
    NSLog(@"%s",__PRETTY_FUNCTION__);

    __weak typeof (self) weakSelf = self;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_t group = dispatch_group_create();

    [MMStopwatchARC start:@"preCalculateHeightForCellsAndReloadTableAfter"];

    for (NSInteger i = 0;  i < [self.dataProvider.dataArray count]; i++) {

        dispatch_group_async(group, queue, ^{

            NSString *keyPoitrait = [NSString stringWithFormat:@"%d-%d-p", 0, i];
            NSString *keyLandscape = [NSString stringWithFormat:@"%d-%d-l", 0, i];
            NSNumber* heightCache = [heightCellCache objectForKey:keyPoitrait];

            SETextViewCell *cell = (SETextViewCell *)[self tableView:self.tableView preparedCellForIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];

            [MMStopwatchARC start:[NSString stringWithFormat:@"preCalculateHeightForCells %@", keyPoitrait]];

            if (heightCache == nil) {


                CGRect screenBound = [[UIScreen mainScreen] bounds];
                CGSize sizeFitContent = [cell.richTextView sizeThatFits:CGSizeMake(screenBound.size.width, 100000)];

                CGFloat height = MAX(self.tableView.rowHeight, sizeFitContent.height);

                [heightCellCache setObject:@(height) forKey:keyPoitrait];

            }

            heightCache = [heightCellCache objectForKey:keyLandscape];

            if (heightCache == nil) {

                SETextViewCell *cell = (SETextViewCell *)[self tableView:self.tableView preparedCellForIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
                CGRect screenBound = [[UIScreen mainScreen] bounds];

                screenBound = CGRectMake(screenBound.origin.x, screenBound.origin.y, screenBound.size.height, screenBound.size.width);

                CGSize sizeFitContent = [cell.richTextView sizeThatFits:CGSizeMake(screenBound.size.width, 100000)];

                CGFloat height = MAX(self.tableView.rowHeight, sizeFitContent.height);
                [heightCellCache setObject:@(height) forKey:keyLandscape];

            }

            [MMStopwatchARC stop:[NSString stringWithFormat:@"preCalculateHeightForCells %@", keyPoitrait]];
        });

    }

    // after all task in group finished
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        [MMStopwatchARC stop:@"preCalculateHeightForCellsAndReloadTableAfter"];

        [weakSelf.tableView setTableFooterView:nil];
        [weakSelf.tableView reloadData];

    });

}

我在here使用了Matt Maher的观察,以便计算此任务的耗时。运行后,Log中的结果如下所示:

 -[ViewController preCalculateHeightForCellsAndReloadTableAfter]
 -> Stopwatch: [preCalculateHeightForCells 0-0-p] runtime: [0.053755]
 -> Stopwatch: [preCalculateHeightForCells 0-1-p] runtime: [0.100539]
 -> Stopwatch: [preCalculateHeightForCells 0-2-p] runtime: [0.098142]
 -> Stopwatch: [preCalculateHeightForCells 0-3-p] runtime: [0.062571]
 -> Stopwatch: [preCalculateHeightForCells 0-4-p] runtime: [0.081574]
 -> Stopwatch: [preCalculateHeightForCellsAndReloadTableAfter] runtime: [0.699024]

问题是为什么 Time-preCalculateHeightForCellsAndReloadTableAfter比preCalculateHeightForCells 大得多?

我将组添加到全局队列,因此任务必须运行并发日志,以便任务(计算每个高度的单元格)按顺序运行(就像串行队列一样)。

1 个答案:

答案 0 :(得分:0)

preCalculateHeightForCells运行时(0.396581s)和preCalculateHeightForCellsAndReloadTableAfter运行时(0.699024s)的总时间之间的差异可能来自主队列执行时间。

您正在为每个preCalculateHeightForCells使用全局队列。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    ...
    dispatch_group_async(group, queue, ^{

完成所有任务后的通知将在主队列(主线程)上执行。

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

主队列上的任务将在下一个runloop执行时间安排。但我不确定延迟为何如此之长(0.699024s-0.396581s = 0.302443s)。通常延迟为1/60秒(0.0167秒)或更短。那个应用程序当时很忙吗?如果全局队列可用于执行任务,则具有全局队列的dispatch_group_notify可能会获得更好的结果。

// after all task in group finished
dispatch_group_notify(group, queue, ^{
    [MMStopwatchARC stop:@"preCalculateHeightForCellsAndReloadTableAfter"];

    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf.tableView setTableFooterView:nil];
        [weakSelf.tableView reloadData];
    });
});