- PVSM.RU - https://www.pvsm.ru -
Попалась мне задачка оптимизации, а так как я большой фанат Экселя, то и выбор инструмента был скорым. Единственная пакость: Эксель дико медленный. Так, на одну итерацию уходило как минимум 35 минут, а таких итераций планировалось сделать 1275 (как минимум)!
Цель этого небольшого проектика – ускорить исполнение VBA скриптов задействуя все доступные мне железяки: GPU и CPU. Ну и до кучи, так как библиотека моя, была реализована многозадачность.
Для тех, кто любит читать только код и не любит "растекания мыслию по древу", код находится здесь [1], инсталлятор здесь [2], архивированная библиотека и примеры (Excel/VBscript) здесь [3]. Примеры также включены в инсталляцию (папка "demo").
Требования:
Excel (можно и без него, см. про ненормальное программирование в самом конце).
.Net framework v4.0.
Windows/System32/ должен содержать opencl.dll.
Внутренности библиотеки состоят из двух непересекающихся частей: конфигурации доступного оборудования (только информация, поменять там ничего нельзя) и, собственно, программирования найденных устройств.
Конфигурация просто вываливает в табличку всю доступную информацию об устройствах, "до самой последней гнилушки". Логически OpenCl делит компьютер на платформы, внутри которых находятся устройства. Так, на моём домашнем компьютере обнаружилась только одна платформа с CPU на 4 процесса (Pentium 4417U 2.30GHz) и 12-ядерным GPU (Intel HD Graphics 610). А вот на компьютере жены обнаружилось целых 2 платформы: первая, с i3-7100U и 23-ядерной Intel HD Graphics 620, и вторая, опять же с i3-7100U и 5-ядерным GPU Hainan. Что интересно, интернеты говорят, что Intel HD Graphics 620 идёт с 24 ядрами. Либо OpenCl недосчитался, либо ядро отвалилось.
Программирование было реализовано на двух примерах: оценке производительности и параллельного исполнения кода.
Производительность оценивалась двумя способами:
Умножением больших матриц.
Кодом, заимствованным с CodeProject ("How to Use Your GPU in .NET [4]").
Время, затраченное на умножение двух матриц 2000 на 2000 (вычисления с двойной точностью) приведено на рисунке ниже. Обратите внимание на логарифмическую шкалу!

Обозначения CPU и GPU соответствуют использованию OpenCl на CPU и GPU. Native CPU – случаю использования простейшей программы на C# для перемножения матриц. Ну а VBA он и в Африке VBA. Кроме измерения времени результаты OpenCl вычислений сравнивались с результатами VBA (все сошлись!).
Довольно неожиданным открытием для меня стало то, что OpenCl на всех CPU считает в 8 раз быстрее, чем C# на одном процессоре. Так как у меня 2 процессора с 4 потоками, можно было бы предположить четырёхкратное уменьшение времени исполнения. Видимо здесь играет существенную роль то, что драйвер процессора, который компилирует CL-код, компилирует сразу в машинный код, а не использует интерпретатор, как C#.
Ну и результаты теста производительности полностью:
|
Время вычисления VBA: |
3.1 |
минуты. |
|
OpenCl на CPU в |
73 |
раза быстрее VBA. |
|
OpenCl на GPU в |
327 |
раз быстрее VBA. |
|
OpenCl на GPU в |
4.5 |
раза быстрее CPU. |
|
OpenCl на CPU в |
8.3 |
раза быстрее C#, CPU. |
|
C#, CPU в |
8.9 |
раз быстрее VBA. |
Следующим тестом производительности был тест, позаимствованный с CodeProject. Для моего домашнего Pentium 4417U (2 x 2.30 ГГц) выдал мне такие результаты производительности в Гфлопсах:
|
GFlops, single |
GFlops, double |
|
|
CPU |
18.1 |
9.1 |
|
GPU |
358.5 |
89.7 |
Как, наверное, всем известно, Эксель до боли однозадачен. Чтобы запустить две задачи одновременно, надо приложить довольно много усилий. Всё описанное выше запускалось командой ExecuteSync. Тем не менее, кроме функции ExecuteSync имеются также и функции ExecuteAsync и ExecuteBackground. Отличие первой функции от второй в том, что ExecuteAsync использует callback, в то время как вторая возвращает информацию о завершении исполнения в свойстве ExecutionCompleted (True/False), которое надо время от времени опрашивать.
В принципе, первой была создана функция ExecuteAsync, но по мере тестирования было замечено, что с callback работать довольно сложно:
Эксель не любит и рушится:
Когда при отладке исполняется callback.
Когда из callback функции что-либо пишется в таблицу.
Поэтому, в примере я использую ExecuteBackground. Результат работы в асинхронном режиме просто ошеломляет! Прогресс-бар бегает, процессы исполняются параллельно на CPU и GPU, поочерёдно завершаются, причём на один завершённый процесс CPU приходится 4 процесса GPU, как и предсказывали расчёты производительности умножения матриц.
Ну и обещанное ненормальное программирование (а всё написанное до того было нормальным программированием?).
Как оказалось, Эксель особо-то и не нужен! VBscript тоже поддерживает COM, правда со своими ограничениями: отсутствие типизации приводит к тому, что массивы необходимо передавать через object или ArrayList. Тем не менее, конфигурация работает как есть даже без дополнительных телодвижений:

И, наконец, всё в одном месте:
GitHub [1]
Инсталляция [2]
Всё в архиве [3].
Примеры в папке "demo".
Автор:
Krasnoarmeec
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/372873
Ссылки в тексте:
[1] здесь: https://github.com/Excel-lent/ClooWrapperVBA
[2] здесь: https://sourceforge.net/projects/cloowrappervba/files/ClooWrapperVBA%20setup.exe/download
[3] здесь: https://sourceforge.net/projects/cloowrappervba/files/ClooWrapperVBA.zip/download
[4] How to Use Your GPU in .NET: https://www.codeproject.com/Articles/1116907/How-to-Use-Your-GPU-in-NET
[5] Источник: https://habr.com/ru/post/655085/?utm_source=habrahabr&utm_medium=rss&utm_campaign=655085
Нажмите здесь для печати.