Я написал подпрограмму, которая выполняла непротиворечивые вычисления (вычисление π путем суммирования рядов Грегори-Лейбница, дросселированных до 1,2 м итераций каждую 60-ую секунду, с таким же танцем семафора / displaylink, как и в вашем примере). И iPad mini 2, и iPhone Xs Max смогли выдержать целевую скорость 60 кадров в секунду (iPad mini 2 едва ли) и увидели, что значения загрузки ЦП более соответствуют ожидаемому. В частности, загрузка процессора составила 47% на iPhone Xs Max (iOS 13), но 102% на iPad mini 2 (iOS 12.3.1):
iPhone Xs Max:
iPad mini 2:
Затем я запустил это через «Time Profiler» в Instruments со следующими настройками:
- «Высокочастотная» выборка;
- «Запись ожидающих потоков»;
- «Отложенный» или «оконный» захват; и
- Изменено дерево вызовов для сортировки по «состоянию».
Для репрезентативной временной выборки iPhone Xs Max сообщал, что этот поток работал 48,2% времени (в основном, просто ожидание более половины времени):
Принимая во внимание, что на iPad mini 2 этот поток работал 95,7% времени (почти нет избыточной пропускной способности, вычисляя почти все время):
Итог, это говорит о том, что конкретная очередь на iPhone Xs Max, вероятно, может сделать примерно в два раза больше, чем iPad mini 2.
Вы можете видеть, что график ЦП отладчика XCode и инструменты «Time Profiler» рассказывают нам довольно последовательные истории. И они оба согласуются с нашими ожиданиями, что iPhone Xs Max будет значительно меньше облагаться той же задачей, что и iPhone mini 2.
В интересах полного раскрытия, когда я снизил рабочую нагрузку (например, взяв ее с 1,2 м итераций каждую 60-ую секунды, до всего лишь 800 КБ), разница в загрузке ЦП была менее резкой, где загрузка ЦП составляла 48 % на iPhone Xs Max и 59% на iPad mini 2. Но все же, более мощный iPhone использовал меньше процессоров, чем iPad.
Вы спросили:
- Почему загрузка ЦП% может быть выше для устройств с более быстрыми ЦП? Есть основания полагать, что результаты не точны?
Пара наблюдений:
Я не уверен, что вы сравниваете яблоки с яблоками здесь. Если вы собираетесь проводить такого рода сравнения, убедитесь, что работа, проделанная в каждом потоке на каждом устройстве, абсолютно идентична. (Мне нравится эта цитата, которую я слышал во время презентации WWDC несколько лет назад; перефразируя: «в теории нет разницы между теорией и практикой; на практике существует мир различий».)
Если бы вы упали частоты кадров или другие временные различия, которые могли бы разделить вычисления по-разному, числа могут быть несопоставимыми, потому что другие факторы, такие как переключение контекста и тому подобное, могут вступить в игру. Я на 100% уверен, что расчеты на двух устройствах идентичны, иначе сравнения будут вводить в заблуждение.
ЦП отладчика «Процент использования», имхо, просто интересный барометр. То есть, вы хотите убедиться, что счетчик работает нормально и низко, когда у вас ничего не происходит, чтобы убедиться, что там нет какой-то жуликской задачи. И наоборот, когда вы выполняете что-то массово распараллеленное и требующее большого объема вычислений, вы можете использовать это, чтобы убедиться, что у вас нет какой-либо ошибки, которая мешает полному использованию устройства.
Но этот отладчик «Процент использования» - это вообще не число, на которое я вешаю шляпу. Всегда интереснее смотреть на инструменты, определять потоки, которые заблокированы, смотреть на использование ядром процессора и т. Д.
В вашем примере вы уделяете большое вниманиеОтчет отладчика об использовании CPU в процентах от 47% на iPad mini 2 против 85% на iPhone Xs Max. Очевидно, вы игнорируете, что на iPad mini он составляет примерно одну десятую от общей емкости, но только рядом с пятым для iPhone Xs Max. Итог, общий счетчик менее беспокоит, чем эти простые проценты.
- Как мне лучше интерпретировать цифры или получить более точные тесты между устройствами?
Да, инструменты всегда дадут вам более значимые, более действенные результаты.
- Почему запись ожидающих потоков приводит к более низкому проценту использования ЦП (но все же незначительно отличается, а иногда и выше для более быстрого устройства)?
Я не уверен, о каких «процентах» вы говорите. Большинство общих процентов дерева вызовов полезно для «когда мой код работает, какой процент времени тратится на что», но в отсутствие «записи ожидающих потоков» вы пропускаете большую часть уравнения, т.е. где ваш код ждет чего-то другого. Обе эти проблемы важны, но, включив «Запись ожидающих потоков», вы получаете более целостную картину (т. Е. Когда приложение работает медленно).
FWIW, вот код, который сгенерировал выше:
class ViewController: UIViewController {
@IBOutlet weak var fpsLabel: UILabel!
@IBOutlet weak var piLabel: UILabel!
let calculationSemaphore = DispatchSemaphore(value: 0)
let displayLinkSemaphore = DispatchSemaphore(value: 0)
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".pi", qos: .userInitiated)
var times: [CFAbsoluteTime] = []
override func viewDidLoad() {
super.viewDidLoad()
let displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink.add(to: .main, forMode: .common)
queue.async {
self.calculatePi()
}
}
/// Calculate pi using Gregory-Leibniz series
///
/// I wouldn’t generally hardcode the number of iterations, but this just what I empirically verified I could bump it up to without starting to see too many dropped frames on iPad implementation. I wanted to max out the iPad mini 2, while not pushing it over the edge where the numbers might no longer be comparable.
func calculatePi() {
var iterations = 0
var i = 1.0
var sign = 1.0
var value = 0.0
repeat {
iterations += 1
if iterations % 1_200_000 == 0 {
displayLinkSemaphore.signal()
DispatchQueue.main.async {
self.piLabel.text = "\(value)"
}
calculationSemaphore.wait()
}
value += 4.0 / (sign * i)
i += 2
sign *= -1
} while true
}
@objc func handleDisplayLink(_ displayLink: CADisplayLink) {
displayLinkSemaphore.wait()
calculationSemaphore.signal()
times.insert(displayLink.timestamp, at: 0)
let count = times.count
if count > 60 {
let fps = 60 / (times.first! - times.last!)
times = times.dropLast(count - 60)
fpsLabel.text = String(format: "%.1f", fps)
}
}
}
Итог, учитывая, что мои эксперименты с вышеизложенным, похоже, соответствуют нашим ожиданиям, тогда как ваши нет, я должен задаться вопросом, действительно ли ваши вычисления выполняют точно такую же работу каждую 60-ую секунду, независимо от устройства, как и выше. Если у вас есть пропущенные кадры, другие вычисления для разных временных интервалов и т. Д., Кажется, что в игру вступают все другие переменные, и сравнение становится недействительным.
Для чего бы это ни стоило, вышеизложенное относится ко всей логике семафора и отображаемой ссылки. Когда я упростил его, чтобы просто как можно быстрее суммировать 50 миллионов значений последовательности в одном потоке, iPhone Xs Max сделал это за 0,12 секунды, тогда как iPad mini 2 сделал это за 0,38 секунды. Очевидно, что с простыми вычислениями без каких-либо таймеров или семафоров производительность оборудования резко снижается. В итоге я не склонен полагаться на какие-либо расчеты использования ЦП в отладчике или инструментах для определения теоретической производительности, которую вы можете достичь.