Небольшое сравнение производительности UWP/WinRT API языковых проекций

    WinRT Language projections

    На мой взгляд, в разработке UWP/WinRT приложений сложилась необычная ситуация: компания продвигает использование нативного SDK из управляемой среды. Мне стало интересно, насколько эффективен данный подход. И для ответа, я решил написать несколько приложений, решающих одну и туже задачу, полагаясь на средства предоставляемые UWP/WinRT API.
    За результатами моего небольшого теста добра пожаловать под кат.

    Постановка задачи


    Алгоритм работы каждого приложения включал в себя следующие шаги:
    1. Исполняемой средой вызывается функция Main()(не удивляйтесь, в приложения WinRT/UWP эта функция всегда существует)
    2. Методу CoreApplication.Run(…) передаётся реализация IFrameworkViewSource, которая возвращает в методе CreateView() интерфейс IFrameworkView
    3. Через несколько шагов инициализации вызывается метод IFrameworkView.Run(), который выполняет активацию основного окна приложения, запускает обработку событий диспетчером и создаёт поток/задачу для выполнения вычислений

    Чтобы точнее измерить необходимое время на выполнение вычислений, приложения запускались без отладчика. Значения результата и затраченного времени заносились в поля локальных настроек приложения «Result» и «Time» соответственно.
    Последовательность замера времени, необходимого на выполнение всех вычислений, сводилась к следующему:
    1. Инициализация переменных
    2. Запуск таймера/инициализация переменной времени начала
    3. Многократное преобразование: входные данные -> SHA256 хэш -> Base64 строка -> входные данные
    4. Остановка таймера/вычисление пройденного времени
    5. Запись значений в локальные настройки
    Для чтения сохраненных значений приложения запускались в режиме отладки, а данные выводились в консоль.

    Всего было создано пять тестовых проектов приложений, три из которых использовали в своей работе 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.
    Ниже представлены диаграммы времени исполнения(значения указаны в миллисекундах).

    ARM

    x86

    Столь значительная разница меня немного удивила. Чтобы разобраться я решил провести профилирование приложений, использующих UWP/WinRT API.
    Если свести все скриншоты в таблицу то, можно получить следующее:

    Легко заметить причину столь большой разницы: в проекте, написанном на чистом C++ с использованием WRL, время работы кода из библиотеки CryptoWinRT.dll, достигает значения 90 процентов, а в проекте C#, скомпилированном с использованием .NET Native, это значение равно всего 15 процентам. Вот и получается, что большую часть времени проекты, написанные на C#, работают в холостую.

    Заключение


    Конечно, понятно то, что выбран отдалённый от реальности метод использования UWP/WinRT API. Скорее всего в жизни такой код вообще никогда не встретится. Но факт остаётся фактом, при некотором стечении обстоятельств, ваш код может работать очень медленно только из-за накладных расходов, возникших в следствии использования языковой проекции. Может быть наилучшим решением в этом случае будет альтернативная реализация, выполняющая аналогичные задачи, но без использования системного API.

    Исходный всех проектов код доступен по ссылке https://github.com/altk/sha256comparison
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 21
      +2
      Чтобы точнее измерить необходимое время на выполнение вычислений, приложения запускались без отладчика


      А сборка то хоть релизная была?
        0
        Конечно, тестировалась релизная сборка. Настройки компиляции релизной сборки не менял, значения были по-умолчанию (ну кроме того, что полностью выключил генерацию отладочной информации.
        0
        Проверка границ массивов всё сожрала?
          0
          Нет. В проектах, полагающихся на работу UWP/WinRT API никакие массивы в замере времени исполнения не участвуют.
          0

          del

            +1
            Что-то мне подсказывает, что большая часть времени уходит не на работу «в холостую» (и что это может значить в Вашем примере, если код все равно выполняется?), а на работу со строками.
            В C# примере вы используете:
            input = hasher.HashData(input);
            input = Encoding.ASCII.GetBytes(CryptographicBuffer.EncodeToBase64String(input)).AsBuffer();

            Здесь вы сначала хэшируете буфер, потом конвертируете в base64 string, затем в массив, а потом опять в буфер.

              0
              Если вы посмотрите проекты CPPWRL или CPPCX, то сможете заметить, что там работает аналогичный код:
              input = hasher->HashData(input);
              String^ base64String;
              base64String = CryptographicBuffer::EncodeToBase64String(input);
              input = CryptographicBuffer::ConvertStringToBinary(base64String, BinaryStringEncoding::Utf8);
              

              Для удобства восприятия могу его записать так:
              input = hasher->HashData(input);
              input = CryptographicBuffer::ConvertStringToBinary(CryptographicBuffer::EncodeToBase64String(input), BinaryStringEncoding::Utf8);
              

              Кроме того. Привёл к этому же виду код проекта CSWinRT:
              input = hasher.HashData(input);
              input = CryptographicBuffer.ConvertStringToBinary(CryptographicBuffer.EncodeToBase64String(input), BinaryStringEncoding.Utf8);
              

              Результаты стали даже хуже:
              • C# Native — ~195000 миллисекунд
              • C# — ~80000 миллисекунд
                0
                Так я не спорю, что это будет медленнее. Я говорю, что «в холостую» не совсем верное утверждение. Код не работает в холостую, он просто выполняется так, как Вы его написали — где-то быстрее, где-то медленнее.
                  0
                  Спасибо за замечание. Внёс изменения в текст статьи.
              +1
              Довелось мне очень плотно познакомиться с WinRT еще когда он только-только появился — а именно, поучаствовать в разработке Microsoft Minesweeper и других «стандартных» игр.
              Могу подтвердить что работает оно невероятно медленно.
              Причины — это урезанное (и очень медленное по сравнению с Win32) COM-based API плюс реализация .NET поверх этого API что убивает остатки производительности (да-да, рантайм .NET не имеет никаких «бонусов» в плане поддержки ОС).
              Произошло так потому, что разработкой WinRT руководил дядька который ненавидел и стемился уничтожить .NET и сделал все что было в его силах чтобы .NET в WinRT работал настолько плохо, насколько это вообще возможно.
              В итоге — приложения .NET работают в WinRT контейнерах заметно медленнее чем ожидается.
                0
                А если не секрет, то чего хотел этот дядька? На чем, по его мнению, нужно разрабатывать для WinRT? Или он в целом хотел сделать платформу так себе?
                –3
                С++ жил, с++ жив, с++ будет жить,

                www.techempower.com/benchmarks/#section=data-r12&hw=peak&test=query
                Здесь показывается Overhead веб фреймворков. Роза пахнет розой.

                C#-Java это все таки абстракции, Иногда стоит пожертвовать скоростью в угоду удобства. Зато Абстракция дает вам дженерики словари, замыкания )))
                  +3

                  Обращу ваше внимание лишь на то, что абстракции, генерики темплейты, словари и замыкания есть также и в стандартном C++.

                    0
                    Managed Code ????
                      0

                      Managed code тоже есть в C++, но называть C++/CLI (или C++/CX) "стандартным" я бы не стал. В замечании выше речь идёт именно о варианте C++, описанном в соответствующих стандартах ISO.

                        0
                        Много ли пишут Веб приложения и destop приложения на c++ в СНГ ?? Инструментом для быстрого создания проекта (EE) вряд ли можно назвать с++!
                          0

                          По моему опыту — да, достаточно много серверных компонентов веб-приложений, а также десктоп-приложений в СНГ разрабатываются с помощью C++. Хотя, честно говоря, я так и не понял, к какому заключению это рассуждение должно меня привести.

                            0
                            Можете привести пример ?? Там где критична производительность там и оправдано использование с++.
                              0

                              К сожалению, меня связывает NDA, так что конкретные примеры привести не вправе. Могу только отметить, что это не публичные проекты, в которых, тем не менее, критично важна производительность в обработке большого количества поступающих запросов. И при этом на C++ пишутся не приложения целиком, а отдельные компоненты или сервера (которые могут общаться как по HTTP, так и по каким-то инхаузным протоколам по мере надобности).


                              Лично я бы тоже не стал называть C++ "инструментом для быстрого создания проекта" — так же, например, как и nodejs. Там, говорят, можно по несколько месяцев фреймворк выбирать ;)


                              Это я к тому, что тут всё-таки зависит от навыков программистов, а не от технологии. Видимо, надобности в массовом штамповании проектов на C++ в индустрии нет, поэтому соответствующего рода инструменты (типа какого-нибудь yeoman) не столь распространены.

                                0
                                Это забавно, но я хотел сказать тоже самое!

                                Есть такая штука как https://lwan.ws/
                                А вот инфа насколько она быстра https://www.techempower.com/benchmarks/#section=data-r10&hw=peak&test=json
                                Но это больше для маленького кода там где критична производительность. Писать же на нем Ентерпрайз решения, нееее, да нуу его. Забивать гвозди плоскогубцами, плохая затея.

                                Хотя конечно скорость загрузки потрясает…
                  +1
                  Если не ошибаюсь, вот тут про это написано как раз
                  Секция Keep your app fast when you use interop in managed code
                  Вот тут msdn.microsoft.com/en-us/windows/uwp/debug-test-perf/windows-runtime-components-and-optimizing-interop

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

                  Самое читаемое