Почему первоначальный вызов NSAttributedString с помощью строки HTML занимает более 100 раз больше, чем последующие вызовы?

Мне нужно было отображать текст HTML внутри моего приложения iOS. Я решил использовать встроенный метод NSAttributedString, initWithData:options:documentAttributes:error:. Фактический синтаксический анализ работает превосходно, однако, похоже, я столкнулся с очень странной ошибкой, которая, по-видимому, проявляется только в том случае, если у меня есть отладчик.

В первый раз, когда этот метод вызывается, для запуска на моем iPhone 5S под управлением iOS 7.0.4 требуется около 1 секунды и около 1,5 секунд на iPod Touch 5-го поколения. Причуда также проявляется на симуляторе, но она значительно менее заметна из-за чистой скорости симулятора.

Последующие вызовы занимают около 10-50 мс, что значительно быстрее, чем первоначальный вызов.

Это не похоже на кэширование входной строки, поскольку я протестировал ее с несколькими входными строками в моем "реальном" приложении.

Однако, когда я запускаю программу без отладчика, она работает так, как ожидалось, примерно 10-20 мс, что я ожидаю от синтаксического анализа HTML.

Вот соответствующий раздел кода:

-(void) benchmarkMe:(id)sender {
    NSData *data = [testString dataUsingEncoding:NSUTF8StringEncoding];

    NSTimeInterval startTime = [[NSDate date] timeIntervalSinceReferenceDate];

    // So the complier doesn't keep complaining at me.
    __attribute__((unused))
    NSAttributedString *parsed = [[NSAttributedString alloc] initWithData:data
                                                                  options:@{
                                                                        NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                        NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)
                                                                    }
                                                       documentAttributes:nil
                                                                    error:nil];

    NSTimeInterval endTime = [[NSDate date] timeIntervalSinceReferenceDate];

    NSString *message = [NSString stringWithFormat:@"Took %lf seconds.", endTime - startTime];

    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Benchmark complete!"
                                                        message:message
                                                       delegate:nil
                                              cancelButtonTitle:@"Ok"
                                              otherButtonTitles:nil];
    [alertView show];
}

Примечание. Полностью рабочий проект, демонстрирующий эту ошибку, можно найти здесь:
https://github.com/richardjrossiii/NSAttributedStringHTMLBug

Я сошел с ума? Здесь что-то не хватает? 1 секунда - очень большое количество времени, когда я пытаюсь оптимизировать свое приложение для производительности.

Мое текущее решение состоит в том, чтобы разобрать строку 'dummy' при запуске приложения, но это кажется невероятным хакерским решением.

Ответ 1

Это действительно хороший вопрос. Оказывается, что (по крайней мере для меня) он всегда медленнее при первом вызове метода, независимо от того, прикреплен ли отладчик или нет. Вот почему: при первом анализе строки, связанной с HTML, iOS загружает весь механизм JavaScriptCore и WebKit в память. Часы:

При первом запуске метода (перед разбором строки) существует только 3 потока: enter image description here

После того, как строка проанализирована, у нас есть 11 потоков: enter image description here

Теперь, когда мы в следующий раз запустим этот метод, большинство этих связанных с сетью потоков все еще существуют: enter image description here

Это объясняет, почему это замедляется в первый раз и быстро после этого.