Решила продолжить цикл статей об ОС «Аврора» (до недавнего времени называвшейся Sailfish). За время, что я работаю с этой ОС, у меня накопились разные наблюдения, которые касаются производительности приложений на Qt и системы в целом, ибо девайсами разнообразными обвешана, аки ёлка новогодняя, все мелочи при запуске подмечаю. Думаю, что это может быть интересно и полезно коллегам, кто тоже работает с Qt (или в скором времени будет работать). Предлагайте, что можно протестировать ещё.

Я программирую на Qt и часто обсуждаю с коллегами, iOS-разработчиками, сходства, различия и преимущества подходов. В какой-то момент мы всё-таки решили перейти от слов к делу и провести замеры. Android-программиста, готового участвовать в наших развлечениях, мы так и не нашли, так что сравнение с участием цифр и таблиц будет только для Swift и C++.
Хочу напомнить, что Qt/C++ не навязывает свой механизм управления памятью, и пользователь сам решает этот вопрос в рамках доступных в C++ возможностей, в то время как в Swift используется подсчёт ссылок, а в Java — сборщик мусора. Таким образом у программиста появляется возможность взаимодействовать с памятью эффективнее. Для того, чтобы отправить http-запрос или считать данные из базы данных, Qt полагается на собственные силы, а не использует готовые фреймворки, которые предоставляет ОС. Исключения — взаимодействие с клавиатурой, отрисовка стартового окна приложения, вывод уведомлений и другие вещи.
Первым делом мы решили написать простенький алгоритм (решето Эратосфена), позапускать его на больших числах и сравнить время вычислений. Запускали на iPhone 7.
Программа на Swift:
Подробно останавливаться на коде не буду, линейный Эратосфен не очевиден для понимания, тем более если один из языков вам не знаком. Вот ссылка с описанием: https://e-maxx.ru/algo/prime_sieve_linear, кому интересно, может проверить на честность. Кстати, версия swift оказалась чуть более оптимизирована в мелочах (можете их поискать), что не помешало плюсовой версии всё-таки выиграть в производительности.
Программа на Qt:
Запускаем программу. Приложения имеют поле для ввода числа n, кнопку старт и поле с итоговым временем:

Swift — слева, Qt — справа.
Результат. Приведу таблицу замеров для разных n и в разные моменты времени:

Как видите, приложение на С++ в ~1.5 раза быстрее нативного при идентичных алгоритмах.
Естественно, вычисления в контексте мобильных приложений — штука важная, но далеко не единственная. Поэтому мы нарисовали ListView, состоящий из 1000 элементов, каждый из которых содержит текст и картинку, чтобы посмотреть ещё и на скорость отрисовки графических элементов. Ниже, в видеороликах, можно увидеть результат:
Qt:
Swift:
Визуально разница не заметна.
В ОС Sailfish мы имеем ядро Linux и графическую нативную оболочку на Qt, а это уже само по себе наталкивает на мысли о неплохой произв��дительности этой ОС. Я часто замечаю, что Inoi R7 выигрывает по скорости выполнения некоторых задач, хотя рядом лежит и тем же самым занимается Samsung Galaxy S8. Так, например, Samsung Galaxy S8 отправляет, принимает, обрабатывает, упаковывает в БД и т.д. 10К http-запросов примерно за 3-4 минуты, а Inoi R7 всё то же самое делает минут 5-6. Учитывая разницу в производительности железа, результат впечатляющий.
Для более честной проверки производительности ОС я решила посмотреть на скорость отклика тача.
Просто и незатейливо. Телефона, поддерживающего Sailfish и Android, в личном пользовании меня нет, потому пришлось искать у коллег телефон максимально близкий по характеристикам к Inoi r7, но на Android. Что внезапно оказалось очень сложно, учитывая, что сижу я в офисе мобильной разработки.
Sony Xperia Z5 compact:
Процессор — Qualcomm Snapdragon 810 MSM8994, 2000 MHz
Количество ядер процессора — 8
Видеопроцессор — Adreno 430
Объем встроенной памяти — 32 Гб
Объем оперативной памяти — 2 Гб
Inoi R7:
Процессор — Qualcomm Snapdragon 212 MSM8909AA, 1200 МГц
Количество ядер процессора — 4
Видеопроцессор — Adreno 304
Объем встроенной памяти — 16 Гб
Объем оперативной памяти — 2 Гб
Sony всё-таки оказалась мощнее, но для уравнения шансов включим на ней режим энергосбережения, всё равно это не приведёт к полному равенству мощности девайсов. На видео можно заметить, что на Android линия получается не настолько плавная, как на Sailfish.
Слева — Sony, справа — Inoi:
Не спорю, это все не очень серьёзный показатель, нужно сравнивать не только возможности чистого языка, но и разные библиотеки, нативные и кроссплатформенные, сравнивать их производительность и удобство в использовании, потому что очень мало приложений, которые используют только ListView и решето Эратосфена. Хотя все эти мелочи вместе для меня выглядят весьма убедительно.
Конечно, всё не так радужно с Qt, как я пытаюсь тут расписать, минусы есть. Например, работой с TextInput на Android можно пытать особо чувствительных к костылям перфекционистов, потому что на каждом девайсе Qt умудряется ставить абсолютно уникальные палки в колёса при взаимодействии с клавиатурой. На одном телефоне картинка уезжает вверх, на другом — стоит на месте, но не работает EnterKey, на третьем всегда вводятся только заглавные буквы, и никак его не переубедить переключить на строчные. Продолжать можно до бесконечности. И всё это ещё и тормозит! (Ворчания актуальны только для Android, на Sailfish таких проблем не возникает, всё отлично работает). Кроме того, на Qt сложно добиться нативности внешнего вида приложения.
Главный вывод, который можно сделать: Qt, будучи кроссплатформенным инструментом, не уступает в производительности нативным средствам разработки. Он отлично подойдёт для программ, в которых помимо GUI есть ещё много математики, и в особенности для корпоративных приложений, где много нюансов и мало сотрудников, чтобы для каждой ОС не создавать самостоятельную версию. Корпоративным пользователям больше важны функции, чем нативность UI. Для «Авроры» Qt — нативное средство для разработки приложений, что, вероятно, даёт ещё какой-то прирост к производительности.
Было бы интересно испытать «Аврору» на мощном железе, вроде моего Galaxy S8.

Я программирую на Qt и часто обсуждаю с коллегами, iOS-разработчиками, сходства, различия и преимущества подходов. В какой-то момент мы всё-таки решили перейти от слов к делу и провести замеры. Android-программиста, готового участвовать в наших развлечениях, мы так и не нашли, так что сравнение с участием цифр и таблиц будет только для Swift и C++.
Хочу напомнить, что Qt/C++ не навязывает свой механизм управления памятью, и пользователь сам решает этот вопрос в рамках доступных в C++ возможностей, в то время как в Swift используется подсчёт ссылок, а в Java — сборщик мусора. Таким образом у программиста появляется возможность взаимодействовать с памятью эффективнее. Для того, чтобы отправить http-запрос или считать данные из базы данных, Qt полагается на собственные силы, а не использует готовые фреймворки, которые предоставляет ОС. Исключения — взаимодействие с клавиатурой, отрисовка стартового окна приложения, вывод уведомлений и другие вещи.
Тест 1.
Первым делом мы решили написать простенький алгоритм (решето Эратосфена), позапускать его на больших числах и сравнить время вычислений. Запускали на iPhone 7.
Программа на Swift:
swift // // ViewController.swift // Eratosthenes // // Created by Dmitry Shadrin on 22/11/2018. // Copyright 2018 Digital Design. All rights reserved. // import UIKit class ViewController: UIViewController { @IBOutlet weak var digitTextField: UITextField! @IBOutlet weak var timeLabel: UILabel! @IBAction func startAction(_ sender: UIButton) { guard let text = digitTextField.text, let number = Int(text) else { return } prime(n: number) } func prime(n: Int) { let startTime = DispatchTime.now() let _ = PrimeSequence(upTo: n) .reduce(into: [], { $0.append($1) }) // Тот самый массив с числами let endTime = DispatchTime.now() let time = (endTime.uptimeNanoseconds - startTime.uptimeNanoseconds) timeLabel.text = "\(time)" } } public struct PrimeSequence: Sequence { private let iterator: AnyIterator<Int> public init(upTo limit: Int) { self.iterator = AnyIterator(EratosthenesIterator(upTo: limit)) } public func makeIterator() -> AnyIterator<Int> { return iterator } } private struct EratosthenesIterator: IteratorProtocol { let n: Int var composite: [Bool] var current = 2 init(upTo n: Int) { self.n = n self.composite = [Bool](repeating: false, count: n + 1) } mutating func next() -> Int? { while current <= self.n { if !composite[current] { let prime = current for multiple in stride(from: current * current, through: self.n, by: current) { composite[multiple] = true } current += 1 return prime } current += 1 } return nil } }
Подробно останавливаться на коде не буду, линейный Эратосфен не очевиден для понимания, тем более если один из языков вам не знаком. Вот ссылка с описанием: https://e-maxx.ru/algo/prime_sieve_linear, кому интересно, может проверить на честность. Кстати, версия swift оказалась чуть более оптимизирована в мелочах (можете их поискать), что не помешало плюсовой версии всё-таки выиграть в производительности.
Программа на Qt:
#include "eratosthenes.h" #include <stdio.h> #include <stdlib.h> #include <time.h> #include <QVector> #include <QDebug> #include <vector> #include <cmath> Eratosthenes::Eratosthenes(QObject *parent) { time = 0; } void Eratosthenes::qtFunction(int n) { clock_t start, end; start = clock(); std::vector<int> lp = std::vector<int>(n + 1, 0); std::vector<int> pr; // зарезервируем памяти под элементы с запасом pr.reserve(std::sqrt(n) / 2); for (int i = 2; i <= n; ++i) { if (lp[i] == 0) { lp[i] = i; pr.emplace_back(i); } for (int j = 0; j < pr.size() && pr[j] <= lp[i] && i * pr[j] <= n; ++j) { lp[i * pr[j]] = pr[j]; } } end = clock(); time = (end - start) / (double)CLOCKS_PER_SEC; pTimeChanged(); qDebug() << "Количество простых чисел" << pr.size() << "Время" << time; }
Запускаем программу. Приложения имеют поле для ввода числа n, кнопку старт и поле с итоговым временем:

Swift — слева, Qt — справа.
Результат. Приведу таблицу замеров для разных n и в разные моменты времени:

Как видите, приложение на С++ в ~1.5 раза быстрее нативного при идентичных алгоритмах.
Тест 2.
Естественно, вычисления в контексте мобильных приложений — штука важная, но далеко не единственная. Поэтому мы нарисовали ListView, состоящий из 1000 элементов, каждый из которых содержит текст и картинку, чтобы посмотреть ещё и на скорость отрисовки графических элементов. Ниже, в видеороликах, можно увидеть результат:
Qt:
Swift:
Визуально разница не заметна.
Тест 3.
В ОС Sailfish мы имеем ядро Linux и графическую нативную оболочку на Qt, а это уже само по себе наталкивает на мысли о неплохой произв��дительности этой ОС. Я часто замечаю, что Inoi R7 выигрывает по скорости выполнения некоторых задач, хотя рядом лежит и тем же самым занимается Samsung Galaxy S8. Так, например, Samsung Galaxy S8 отправляет, принимает, обрабатывает, упаковывает в БД и т.д. 10К http-запросов примерно за 3-4 минуты, а Inoi R7 всё то же самое делает минут 5-6. Учитывая разницу в производительности железа, результат впечатляющий.
Для более честной проверки производительности ОС я решила посмотреть на скорость отклика тача.
Test.cpp:
#include "mypainter.h" #include <QPainter> MyPainter::MyPainter(QQuickItem *parent) : QQuickPaintedItem(parent) { } void MyPainter::paint(QPainter *painter) { QPen pen; pen.setWidth(10); pen.setColor(Qt::red); painter->setPen(pen); painter->drawPolyline(pol); } void MyPainter::xyCanged(int x, int y) { pol.append(QPoint(x, y)); update(); }
Test.qml:
import QtQuick 2.9 import QtQuick.Window 2.2 import Painter 1.0 Window { visible: true Painter { id: painter anchors.fill: parent MouseArea { anchors.fill: parent onPressed: { painter.xyCanged(mouseX, mouseY) } onMouseXChanged: { painter.xyCanged(mouseX, mouseY) } onMouseYChanged: { painter.xyCanged(mouseX, mouseY) } } } }
Просто и незатейливо. Телефона, поддерживающего Sailfish и Android, в личном пользовании меня нет, потому пришлось искать у коллег телефон максимально близкий по характеристикам к Inoi r7, но на Android. Что внезапно оказалось очень сложно, учитывая, что сижу я в офисе мобильной разработки.
Sony Xperia Z5 compact:
Процессор — Qualcomm Snapdragon 810 MSM8994, 2000 MHz
Количество ядер процессора — 8
Видеопроцессор — Adreno 430
Объем встроенной памяти — 32 Гб
Объем оперативной памяти — 2 Гб
Inoi R7:
Процессор — Qualcomm Snapdragon 212 MSM8909AA, 1200 МГц
Количество ядер процессора — 4
Видеопроцессор — Adreno 304
Объем встроенной памяти — 16 Гб
Объем оперативной памяти — 2 Гб
Sony всё-таки оказалась мощнее, но для уравнения шансов включим на ней режим энергосбережения, всё равно это не приведёт к полному равенству мощности девайсов. На видео можно заметить, что на Android линия получается не настолько плавная, как на Sailfish.
Слева — Sony, справа — Inoi:
Не спорю, это все не очень серьёзный показатель, нужно сравнивать не только возможности чистого языка, но и разные библиотеки, нативные и кроссплатформенные, сравнивать их производительность и удобство в использовании, потому что очень мало приложений, которые используют только ListView и решето Эратосфена. Хотя все эти мелочи вместе для меня выглядят весьма убедительно.
Минусы
Конечно, всё не так радужно с Qt, как я пытаюсь тут расписать, минусы есть. Например, работой с TextInput на Android можно пытать особо чувствительных к костылям перфекционистов, потому что на каждом девайсе Qt умудряется ставить абсолютно уникальные палки в колёса при взаимодействии с клавиатурой. На одном телефоне картинка уезжает вверх, на другом — стоит на месте, но не работает EnterKey, на третьем всегда вводятся только заглавные буквы, и никак его не переубедить переключить на строчные. Продолжать можно до бесконечности. И всё это ещё и тормозит! (Ворчания актуальны только для Android, на Sailfish таких проблем не возникает, всё отлично работает). Кроме того, на Qt сложно добиться нативности внешнего вида приложения.
Вывод
Главный вывод, который можно сделать: Qt, будучи кроссплатформенным инструментом, не уступает в производительности нативным средствам разработки. Он отлично подойдёт для программ, в которых помимо GUI есть ещё много математики, и в особенности для корпоративных приложений, где много нюансов и мало сотрудников, чтобы для каждой ОС не создавать самостоятельную версию. Корпоративным пользователям больше важны функции, чем нативность UI. Для «Авроры» Qt — нативное средство для разработки приложений, что, вероятно, даёт ещё какой-то прирост к производительности.
Было бы интересно испытать «Аврору» на мощном железе, вроде моего Galaxy S8.
