C++/Qt: пора валить?. — 15.05.2020

Qt
C++/Qt: пора валить?.

За что многие любят связку С++ и Qt? За то, что она позволяет решать практически любые задачи без смены контекста. Язык, на котором можно писать быстрые программы, простая, единообразная и хорошо документированная структура, кроссплатформенность, и за всем этим – огромное сообщество.

Давайте структурно декомпозируем разделим на части тот тип проектов. Получим два основных компонента: 1) клей из соплей и палок структурная логика, хранение данных и взаимодействие с пользователем, 2) там, где думать надо, и быстро обработка данных.

Первый компонент (общая логика и взаимодействие с пользователем) не накладывает особых требований к производительности (разница в три раза для пользователя не настолько важна, если это 10 против 30 миллисекунд), но должен позволять как можно более простую разработку/доработку/расширение функционала силами рабов на галерах.

Второй компонент (обработка данных) должен быть как можно «ближе к железу», безболезненно масштабироваться горизонтально (на множество ядер, серверов и т.д.) и, разумеется, должен иметь возможность использовать десятилетиями накопленные/оптимизированные библиотеки для предметной области приложения.

Обработка данных

Ну, тут вообще всё просто. Так как С/С++ со своими задачами по обработке данных вполне себе справляется и всем указанным требованиям удовлетворяет, шило на мыло менять смысла нет. Единственное, что тут дополнительно может возникать, так это небольшая обёртка вокруг алгоритмической части на чистом C для внешнего интерфейса, если планируется прямой вызов функций извне, а внутри используется C++. Ну или, как пример, такая же обёртка в виде интерфейса на сокетах.

Общая логика и взаимодействие с пользователем

Да и тут всё более-менее очевидно… Что общедоступно, работает везде, для чего напридумано всё, что только можно, и что никуда не денется в обозримой перспективе, как и наш, прости-господи, любимый, C/C++? Что позволит нам без дополнительных телодвижений выносить «тяжёлую» обработку от клиента на внешние мощные серверы?

Web-технологии, естественно. Под web-технологиями я понимаю связку из HTML, CSS и JS, плюс всё, что это генерирует/доставляет/выводит.

Со средствами доставки/вывода со стороны пользователя понятно – это либо браузер, либо какой-нибудь WebView. Ну да, кроссплатформенность, ага…

C HTML/CSS/JS мы ничего поделать не можем, оно уже тут и никуда не денется, значит выбора нет, а раз выбора нет – не надо задумываться, а когда задумываться не надо – голова не заболит.

Остаётся выбрать, что будет выдавать HTML/CSS/JS со стороны приложения.
Опять смотрим на требования (чтобы было просто и могло всё) и перебираем варианты. И тут опять просто.

image

Выбор небогат – это PHP. Да, я сказал PHP. И мне не стыдно.

Ещё раз напомню, о каком типе приложений идёт речь: о тех, где сложная логика, требующая вычислительной производительности, реализована на C/C++, а к ним в пару нам нужен как можно более простой открытый язык/экосистема для общения с внешним миром и связи компонентов между собой. А если будет C-подобный синтаксис – вообще хорошо. И тут мы ставим галочки напротив каждого пункта наших требований.

  • Главное – PHP прост. При том круге задач, которые он (и экосистема) может решать — он божественно прост. И дело не только в когнитивной нагрузке при кодинге и переключении контекста. Простота ещё и в развёртывании, администрировании и минимальном количестве вариантов, которыми можно решить одну задачу.
  • PHP медленно, без резких движений, ползёт в правильную сторону, от увеличения производительности в 3 раза, до строгой типизации, решая задачи простым способом. И обрастая по пути крутыми штуками типа Swoole.
  • PHP реализовал FFI (foreign function interface) к C. В том контексте, о котором идёт речь — сочетании простоты и возможностей. Вы только наберите в гугле «PHP: Basic FFI usage».
  • В PHP кругом $, а кто по нынешнему курсу их не любит…
  • PHP быстрый. Для своей простоты и задач он божественно быстрый.

Пипкомер

Для накидывания субстанции на вентилятор давайте сравним «в лоб» сферических коней в вакууме на PHP и C++/Qt.

Коней для замеров расположим в самом простом одноядерном вакууме с 1 Гб ОЗУ за $5 в месяц, а замерять их будем из другого такого же в том же дата-центре. Перед приложениями – nginx.

Первый конь – скрипт на PHP, выводящий шаблон страницы, на которой показывается значение из базы данных на основе параметра, переданного в запросе. Тормоза – для трусов Для простоты, ошибки не обрабатываем, и вообще не паримся. Но чтобы ещё хоть что-то происходило, для параметра из запроса ещё хэш sha256 посчитаем.

Второй конь – то же самое, но выводится с использованием QtWebApp.

Вот такая пара, потому что эта заметка о замене Qt.

Фрагмент на PHP

<?php

$horse_name = htmlspecialchars($_GET['horse_name'], ENT_QUOTES, 'UTF-8');
$horse_hash = hash('sha256', $horse_name);

$database = new PDO("mysql:dbname=vacuum;host=127.0.0.1", 'user', 'password');
$database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$query = "SELECT `horse_color` FROM `horses` WHERE `horse_hash`=:horse_hash";
$statement = $database->prepare($query);
$statement->bindParam('horse_hash', $horse_hash);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);

if ($result)
  $horse_color = $result['horse_color'];
else
  $horse_color = 'unknown';

?>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Horse Color</title>
</head>

<body>
  <p><?= $horse_name ?> color is <?= $horse_color ?>.</p>
</body>

</html>

Фрагмент с QtWebApp

void HorseHandler::service(HttpRequest& request, HttpResponse& response)
{
    QSqlDatabase database = QSqlDatabase::addDatabase(
        "QMYSQL", QString::number(
                      reinterpret_cast<uint64_t>(QThread::currentThreadId())));

    database.setHostName("127.0.0.1");
    database.setPort(3306);
    database.setDatabaseName("vacuum");
    database.setUserName("user");
    database.setPassword("password");
    database.open();

    QString horseName =
        QString(request.getParameter("horse_name")).toHtmlEscaped();
    QByteArray horseHash =
        QCryptographicHash::hash(horseName.toUtf8(), QCryptographicHash::Sha256)
            .toHex();

    QString queryString(
        "SELECT `horse_color` FROM `horses` WHERE `horse_hash`=:horse_hash");
    QSqlQuery query(database);
    query.prepare(queryString);
    query.bindValue(":horse_hash", horseHash);
    query.exec();

    QString horseColor;
    if (query.next())
        horseColor = query.value("horse_color").toString();
    else
        horseColor = "unknown";

    QString templateString(R"RAW(<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Horse Color</title>
</head>

<body>
  <p>{horse_name} color is {horse_color}.</p>
</body>

</html>)RAW");

    Template horseColorTemplate(templateString, "horseColor");
    horseColorTemplate.setVariable("horse_name", horseName);
    horseColorTemplate.setVariable("horse_color", horseColor);

    response.setHeader("Content-Type", "text/html; charset=UTF-8");
    response.write(horseColorTemplate.toUtf8(), true);
}

В итоге получили, что PHP выдаёт абстрактную фигню страницу даже в полтора раза быстрее.

ab -c 1 для PHP

root@horsesab:~# ab -n 1000 -c 1 http://x.x.x.x:8080/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking x.x.x.x (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests

Server Software:        nginx/1.17.10
Server Hostname:        x.x.x.x
Server Port:            8080

Document Path:          /
Document Length:        155 bytes

Concurrency Level:      1
Time taken for tests:   1.125 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      302000 bytes
HTML transferred:       155000 bytes
Requests per second:    888.77 [#/sec] (mean)
Time per request:       1.125 [ms] (mean)
Time per request:       1.125 [ms] (mean, across all concurrent requests)
Transfer rate:          262.12 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:     1    1   0.2      1       6
Waiting:        1    1   0.2      1       6
Total:          1    1   0.2      1       6

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      2
  99%      2
 100%      6 (longest request)
root@horsesab:~#

ab -c 1 для QtWebApp

root@horsesab:~# ab -n 1000 -c 1 http://x.x.x.x:8081/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking x.x.x.x (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests

Server Software:        nginx/1.17.10
Server Hostname:        x.x.x.x
Server Port:            8081

Document Path:          /
Document Length:        155 bytes

Concurrency Level:      1
Time taken for tests:   1.848 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      323000 bytes
HTML transferred:       155000 bytes
Requests per second:    541.26 [#/sec] (mean)
Time per request:       1.848 [ms] (mean)
Time per request:       1.848 [ms] (mean, across all concurrent requests)
Transfer rate:          170.73 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       4
Processing:     1    2   0.3      2       7
Waiting:        1    2   0.3      1       7
Total:          1    2   0.4      2       7
ERROR: The median and mean for the waiting time are more than twice the standard
       deviation apart. These results are NOT reliable.

Percentage of the requests served within a certain time (ms)
  50%      2
  66%      2
  75%      2
  80%      2
  90%      2
  95%      2
  98%      2
  99%      2
 100%      7 (longest request)
root@horsesab:~#

ab -c 20 для PHP

root@horsesab:~# ab -n 1000 -c 20 http://x.x.x.x:8080/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking x.x.x.x (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests

Server Software:        nginx/1.17.10
Server Hostname:        x.x.x.x
Server Port:            8080

Document Path:          /
Document Length:        155 bytes

Concurrency Level:      20
Time taken for tests:   0.664 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      302000 bytes
HTML transferred:       155000 bytes
Requests per second:    1505.59 [#/sec] (mean)
Time per request:       13.284 [ms] (mean)
Time per request:       0.664 [ms] (mean, across all concurrent requests)
Transfer rate:          444.03 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       6
Processing:     2   13   1.2     13      18
Waiting:        1   13   1.3     13      18
Total:          3   13   1.2     13      18

Percentage of the requests served within a certain time (ms)
  50%     13
  66%     13
  75%     14
  80%     14
  90%     14
  95%     15
  98%     15
  99%     16
 100%     18 (longest request)
root@horsesab:~#

ab -c 20 для QtWebApp

root@horsesab:~# ab -n 1000 -c 20 http://x.x.x.x:8081/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking x.x.x.x (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests

Server Software:        nginx/1.17.10
Server Hostname:        x.x.x.x
Server Port:            8081

Document Path:          /
Document Length:        155 bytes

Concurrency Level:      20
Time taken for tests:   1.716 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      323000 bytes
HTML transferred:       155000 bytes
Requests per second:    582.77 [#/sec] (mean)
Time per request:       34.319 [ms] (mean)
Time per request:       1.716 [ms] (mean, across all concurrent requests)
Transfer rate:          183.82 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       2
Processing:     4   34  13.3     34     188
Waiting:        2   34  13.3     34     188
Total:          4   34  13.3     34     188

Percentage of the requests served within a certain time (ms)
  50%     34
  66%     39
  75%     41
  80%     44
  90%     49
  95%     54
  98%     60
  99%     69
 100%    188 (longest request)
root@horsesab:~#

А отсюда избитая банальная истина мораль — вычислительные расходы на обвязку («инфраструктуру») приложения не сильно зависят от того, с помощью чего она реализуется. Отдельные компоненты, на которые и ложится основная нагрузка, от базы данных до внутренней реализации функций PHP, всё равно написаны на относительно низкоуровневых языках и достаточно оптимизированы. И могут быть даже быстрее криво написанных менее отточенных компонентов на номинально более быстрых языках. И где кто в какой ситуации будет быстрее — никто заранее не знает, так как все эти тупые бенчмарки ничего не скажут о той конкретной архитектуре, которая будет у вас в проекте.

Так что можем использовать для «обвязки» и интерфейса то, что проще, более открыто и распространённее, и не переживать. И «PHP плюс веб-фронтенд» для этого — оптимальный выбор.

Но в «тяжёлой» логике это, конечно, не так, поэтому никуда мы от второй части (С/С++) не денемся. И будем, как и раньше, писать её рабоче-крестьянским простым человеческим «Си с классами».

Вместо заключения

Я умышленно избегал технических деталей, чтобы не породить бессмыссленных дебатов о способах реализации конкретных решений. Для связки фронтенда/PHP/C++ cуществует огромное количество возможных комбинаций функций отдельных компонентов и их взаимного размещения, способов хранения информации и обмена сигналами/сообщениями, возможностей разработчиков в конкретном коллективе и т.д. Но однозначно можно говорить, что для всего этого уже всё придумано, на stackoverflow уже всё есть беспокоиться нечего.

Основной посыл такой – в огромном пласте приложений нет особых причин продолжать использовать Qt, а где-то теперь есть и противопоказания. И многое, что надо для замены, и в значительно большем количестве, уже есть в web-технологиях, от компоновки интерфейса до вывода графиков и трёхмерных моделей.
И технологии эти теперь имеют человеческое лицо со всех сторон: для владельца бизнеса, для пользователя, для программиста, и при общении с быстрым нативным кодом. Будем с ними дружить. А вот будем ли скучать по Qt — не знаю…

Благодарю за внимание!!!
Надеюсь статья  «C++/Qt: пора валить?.» была полезной для Вас
Специально для сайта ITWORLD.UZ. Новость взята с сайта Хабр