Небольшое сравнение производительности UWP/WinRT API языковых проекций
На мой взгляд, в разработке UWP/WinRT приложений сложилась необычная ситуация: компания продвигает использование нативного SDK из управляемой среды. Мне стало интересно, насколько эффективен данный подход. И для ответа, я решил написать несколько приложений, решающих одну и туже задачу, полагаясь на средства предоставляемые UWP/WinRT API.
За результатами моего небольшого теста добра пожаловать под кат.
Постановка задачи
Алгоритм работы каждого приложения включал в себя следующие шаги:
- Исполняемой средой вызывается функция Main ()(не удивляйтесь, в приложения WinRT/UWP эта функция всегда существует)
- Методу CoreApplication.Run (…) передаётся реализация IFrameworkViewSource, которая возвращает в методе CreateView () интерфейс IFrameworkView
- Через несколько шагов инициализации вызывается метод IFrameworkView.Run (), который выполняет активацию основного окна приложения, запускает обработку событий диспетчером и создаёт поток/задачу для выполнения вычислений
Чтобы точнее измерить необходимое время на выполнение вычислений, приложения запускались без отладчика. Значения результата и затраченного времени заносились в поля локальных настроек приложения «Result» и «Time» соответственно.
Последовательность замера времени, необходимого на выполнение всех вычислений, сводилась к следующему:
- Инициализация переменных
- Запуск таймера/инициализация переменной времени начала
- Многократное преобразование: входные данные → SHA256 хэш → Base64 строка → входные данные
- Остановка таймера/вычисление пройденного времени
- Запись значений в локальные настройки
Для чтения сохраненных значений приложения запускались в режиме отладки, а данные выводились в консоль.
Всего было создано пять тестовых проектов приложений, три из которых использовали в своей работе UWP/WinRT API, а два других полагались на собственную реализацию SHA256 и Base64.
Общий список приложений:
- CPP (C++). Сишные реализации алгоритмов были взяты из github.com/B-Con/crypto-algorithms.
- CPPCX (C++ CX). Использует API CryptographicBuffer, предоставляемого UWP/WinRT.
- CPPWRL (C++). Также использует API CryptographicBuffer, но вызовы осуществляются в манере COM.
- CS (C#). Взята реализации из github.com/yuriks/SHA2-Csharp/blob/master/Sha256.cs.
- CSWinRT (C#). Используется API CryptographicBuffer.
Проекты, написанные на C#, кроме обычного исполнения, тестировались также в режиме компиляции с использованием .NET Native.
Результаты тестирования
Тестирование проводилось в двух режимах компиляции и исполнения: ARM и x86.
Ниже представлены диаграммы времени исполнения (значения указаны в миллисекундах).
Столь значительная разница меня немного удивила. Чтобы разобраться я решил провести профилирование приложений, использующих UWP/WinRT API.
Если свести все скриншоты в таблицу то, можно получить следующее:
Легко заметить причину столь большой разницы: в проекте, написанном на чистом C++ с использованием WRL, время работы кода из библиотеки CryptoWinRT.dll, достигает значения 90 процентов, а в проекте C#, скомпилированном с использованием .NET Native, это значение равно всего 15 процентам. Вот и получается, что большую часть времени проекты, написанные на C#, работают в холостую.
Заключение
Конечно, понятно то, что выбран отдалённый от реальности метод использования UWP/WinRT API. Скорее всего в жизни такой код вообще никогда не встретится. Но факт остаётся фактом, при некотором стечении обстоятельств, ваш код может работать очень медленно только из-за накладных расходов, возникших в следствии использования языковой проекции. Может быть наилучшим решением в этом случае будет альтернативная реализация, выполняющая аналогичные задачи, но без использования системного API.
Исходный всех проектов код доступен по ссылке https://github.com/altk/sha256comparison