Пакет колеса MegEngine Windows Python Weight Loss Road

искусственный интеллект глубокое обучение задняя часть

Автор: Чжан Хаолун | Архитектор MegEngine, Megvii Technology

написано раньше

Цель этой статьи

  • Поддержите, рассказавMegEngine Windows Python wheelПроблемы, возникающие в процессе и процессе решения проблем, окончательное решение этой статьи может быть не самым лучшим, добро пожаловать, чтобы оставить сообщение для исправления.
  • В процессе я узнаю некоторые базовые вещи о конструкции MegEngine и основных вещах, используемых в строительстве.Конечно, я считаю, что эти базовые знания часто используются в инженерии, включая, помимо прочего:
    • CMake
    • Компиляция, компоновка, скрытие символов, экспорт символов и т. д. Здесь я рекомендую "старую книгу" "Саморазвитие программиста". Естественно, она не такая утомительная, как ххх Сику Цюаньшу, но базовые знания в ней все же те, с которыми мы часто сталкиваемся в нашем "общении" с компьютерами. .
    • Сборка пакета колеса Python

MegEngineПоддержка каждой платформы

  • поддержка вывода cpp:

表 1

ТРОЙНИК:En. Wikipedia.org/wiki/trust E…

  • тренироваться:

    • Сторона питона:

表 2

  • В настоящее время официально выпущенными пакетами колес являются только Windows-X64-CPU-CUDA, многие Linux 64bit-X64-CPU-CUDA и MacOS-X64-CPU.Другие можно скомпилировать самостоятельно или получить накладную сообщества.
  • Поддержка обучения на стороне cpp согласуется с приведенным выше выводом cpp.

Из приведенной выше ситуации видно, что MegEngine поддерживает очень всесторонне, будь то обучение или вывод, или различное оборудование, или различные ОС.Если вам это нужно, вы можете попробовать! ! ! !

возникшие проблемы

Чтобы полностью поддерживать различные платформы MegEngine, упомянутые выше, каждая ОС будет сталкиваться с некоторыми проблемами в большей или меньшей степени в течение периода, такими как большой размер пакета колеса Python на платформе Windows.

Давайте посмотрим на текущий размер пакета колес MegEngine, взятый из версии 1.7.pypi

Среди них, поскольку Linux и Windows поддерживают CUDA, размер пакета составляет около 900 МБ, что является нормальным размером.

В прошлом размер пакета Windows CUDA составлял около 1,7 ГБ: это проблема, которую попытаются проанализировать и исправить позже..

вопрос первыйMECEнемного

MECE — это аббревиатура от Mutually Exclusive Collectively Exhaustive, что в переводе с китайского означает «взаимно независимый и полностью исчерпанный». То есть для темы проблемы ее можно классифицировать без дублирования или упущения, и она может эффективно понять суть проблемы и решить проблему. Подчеркните два момента:

  • Каждая часть независима друг от друга (MutuallyExclusive)
    • После упрощения ощущение такое, что при анализе задачи возможные методы должны быть максимально независимыми, и стараться не иметь пересечений
  • Все детали полностью исчерпаны (CollectivelyExhaustive)
    • После упрощения я чувствую, что при анализе проблемы постарайтесь максимально обдумать все методы и постараться не упустить ни одного упущения.

Почему вы выбираете рыбу: голова рыбы, как правило, больше (жирнее), и чем больше вы идете к хвосту, тем меньше (тоньше) вы будете, поэтому я надеюсь добиться снижения веса с помощью этого процесса «веса колеса Windows». потеря" цель.

Влияние проблемы

В чем проблема с таким объемом (ведь у слишком толстых людей будут какие-то побочные эффекты ххх и т.д.)

  • Во-первых, объем превышает то, на что мы подали заявку.pypiОграничение максимального размера для одного файла
  • Пользовательский интерфейс не очень хорош.Почему Window намного больше, чем Linux для той же версии?
  • Видеопамять в Windows намного больше, чем в Linux (подсчитано, что когда вы упоминаете об этом, все уже догадались о проблеме)

Соответствующие знания, которые могут потребоваться для решения этой проблемы

Первое впечатление от вопроса:

  • Компиляция и сборка связанных
  • Связано с упаковкой колеса Python
  • Эксклюзив для ОС Windows

Во-первых, сделайте несколько основных дополнений к следующим базовым знаниям о MegEngine (должна быть какая-то наука, прежде чем похудеть, принимать ли лекарства или заниматься спортом, или какое лекарство принимать).

Процесс сборки MegEngine CMake

  • Зависимости сборки MegEngineCMakeиNinja

  • Описание CMake в основном находится в:

  • CMakeList верхнего уровня
    • Этот файл содержит множество параметров, в основном используемых для управления компиляцией некоторых модулей, например, открывать ли MGE_BUILD_WITH_ASAN для отладки проблем с памятью.
    • Этот файл содержит элементы управления адаптацией для различных ARCH, например, следует ли компилировать X86_64 или AARCH64 и т. д.
    • Этот файл содержит адаптации для различных ОС, например, для компиляции Linux, Android или Windows и т. д.
    • И некоторые другие настройки, такие как уровни оптимизации, такие как конфигурация CUDA SM и т. д.
  • src CMakelist
    • Этот файл содержит управление компиляцией всех исходных кодов основного кода MegEngine MegBrain layer.
* [dnn CMakelist](https://github.com/MegEngine/MegEngine/blob/master/dnn/src/CMakeLists.txt)
  * 此文件包含了 MegEngine 核心代码 dnn (主要实现各种 backends)层所有源代码的编译管理
* 一些杂项 CMakelist
  * 各种 example,比如 [lite example](https://github.com/MegEngine/MegEngine/blob/master/lite/example/CMakeLists.txt)
  * 各种 test,比如 [megbrain test](https://github.com/MegEngine/MegEngine/blob/master/test/CMakeLists.txt)
  * 各种 helper,见 [helper module](https://github.com/MegEngine/MegEngine/tree/master/cmake)
  • Среди них Ninja предоставляет богатые визуальные функции отладки.Ниже приведен пример того, как просмотреть зависимости сборки некоторых модулей MegEngine через сервер отладки Ninja.

    • воплощать в жизньhost_build.shдля размещения компиляции, и он сгенерирует весь файл описания зависимостей сборки build.ninja в каталоге сборки

      • Для получения дополнительной поддержки компиляции см.BUILD_README.md
      • Аналогично работает файл build.ninja.GNU Makefile
    • Когда у вас есть build.ninja, вы можете заняться отладкой.

      $ ninja -t list
      ninja subtools:
          browse  browse dependency graph in a web browser
           clean  clean built files
        commands  list all commands required to rebuild given targets
            deps  show dependencies stored in the deps log
           graph  output graphviz dot file for targets
           query  show inputs/outputs for a path
         targets  list targets by their rule or depth in the DAG
          compdb  dump JSON compilation database to stdout
       recompact  recompacts ninja-internal data structures
          restat  restats all outputs in the build log
           rules  list all rules
       cleandead  clean built files that are no longer produced by the manifest
      
      
      • Вы можете запустить визуальный сервер с помощью следующей команды (конечно, вы также можете использовать другие параметры командной строки для отладки после того, как вы ознакомитесь с ней. Естественно, вы также можете напрямую просмотреть CMakeLists.txt, чтобы найти взаимосвязь)
      ninja -t browse --port you_port --hostname you_ip --no-browser
      
      • Затем визуально просмотрите, введя: you_ip:you_port в браузере

      • Вы также можете просмотреть его, сгенерировав png с помощью точки

      ninja -t graph > 1.dot
      dot -Tpng 1.dot > output.png
      
      • Например, мегенгинелит является целью верхнего уровня:liblite_shared_whl.soЕго график зависимости выглядит следующим образом

      • Зависит от некоторых .o в lite/CMakeFiles
      • Зависит от некоторых сторонних .a, таких как cpuinfo.a
      • Зависит от libmegbrain_shared.so Эта библиотека содержит все выходные данные компиляции megbrain/dnn Конечно, вы можете нажать на сервер из ниндзя, чтобы развернуть любую цель и просмотреть ее зависимости

Процесс сборки колеса MegEngine

  • С различными библиотеками, скомпилированными Ninja выше, мы можем упаковать их с помощью py src в MegEngine и, наконец, сгенерировать устанавливаемый и распространяемый пакет колеса Python.

  • Процесс строительства в основном описан вBUILD_PYTHON_WHL_README

    • Описывает текущее состояние поддержки колеса питона MegEngine.
    • Некоторая подготовка среды, необходимая для вашей собственной локальной сборки
    • некоторые инструкции по использованию
    • Соответствие пакета колес MegEnginepep-0571
      • Запись установки пакета находится по адресуpython wheel setup
      • Подготовка каждой ОС перед вызовом установки различается вwheel scripts
      • Можно спросить, а почему бы не использоватьauditwheelавтоматически управлять зависимостями so в пакете wheel по двум причинам.
        • Auditwheel поддерживает не все операционные системы, например Windows
        • Auditwheel не поддерживает использование dlopen в зависимых библиотеках.
        • Auditwheel не поддерживает пакеты колес подпакетов
          • Когда вы выполняете python3 -m pip install megaengine -fМег engine.org.cai/cultureroad/every.htm…После этого вы можете импортировать megengine или megenginelite, потому что в установленном пакете будут существовать и megengine, и megenginelite, и они будут переиспользовать огромную библиотеку megengine_shared.

Как адаптировать MegEngine к Windows

Вышеизложенное представляет построение MegEngine на основе CMake и использование собственной функции отладки Ninja, а также помогает нам понять зависимости компиляции проекта и выполнить некоторую общую отладку с точки зрения макросов.Давайте представим, как MegEngine адаптируется к платформе Windows.

  • Во-первых, большая часть исходного кода MegEngine написана на C++, а требования к выводу cpp таковы:c++14, требования к обучению компиляции Pythonc++17

    • На самом деле различные компиляторы не полностью соответствуют этим стандартным реализациям, за исключением системных, таких какPOSIXКроме того, на самом деле существует множество базовых применений верхнего уровня, с которыми несовместимы различные компиляторы, особенно код, который может быть скомпилирован с помощью gcc и clang, но Windows cl.exe на самом деле не может быть скомпилирован.
  • Для решения двух упомянутых выше проблем

    • Откажитесь от cl.exe как можно чаще и используйте его в Windowsllvm-clang-clстроить
    cmake ...
    -DCMAKE_C_COMPILER=clang-cl.exe
    -DCMAKE_CXX_COMPILER=clang-cl.exe
    ...
    
    • Конечно, из-за CUDA полностью отказаться от cl.exe невозможно, при компиляции хост-кода CUDA по-прежнему используется cl.exe.

    • Различайте реализации функций, связанных с системой, поэтому вы увидите много кода, похожего на следующий, в коде MegEngine.

    #if WIN32
    ....
    #else
    ....
    #endif
    
  • Кроме того, вышеупомянутые CMake и Ninja являются кроссплатформенными.При таком сочетании MegEngine изначально поддерживает Windows.Обратите внимание, что он не основан наWSLой

простой анализ проблемы

В разделе «Процесс сборки MegEngine CMake» выше мы упомянули, что сервер отладки Ninja может помочь визуализировать зависимости всего компонента сборки. Давайте добавим это в Windows и Linux, прежде чем проблема будет устранена.imperativeЗависимые результаты визуализации.

под линукс

под Windows

Самые большие различия между Windows и Linux заключаются в следующем:

  • Императив под Linux зависит от libmegengine_shared.so
  • В Windows императив зависит от megbrain и megdnn, а поскольку megbrain и dnn на самом деле являются ОБЪЕКТАМИ на стороне CMake,Так что это эквивалентно прямой зависимости от их .obj

Предварительные выводы:

  • Пакет колес MegEngine с двумя интерфейсами модуля Python

    • Базовый интерфейс MegEngine для обучения работе с Python
      • императив: когда выУстановитьПри завершении MegEngine при вводе import megengine в Python. Загрузка завершена, мы предоставляем некоторыеначинаяУчебники для быстрого начала работы с MegEngine
    • MegEngineLite: простой в использовании cpp, интерфейс вывода Python
      • После того, как вы закончите обучение модели с помощью MegEngine, вы можете обратиться кразвертыватьВ документации используется MegEngineLite для быстрого развертывания вашей модели на земле.
  • Проблема возникает из-за существования этих двух целей сборки верхнего уровня, и они зависят от разных базовых целей в Windows и других ОС.

  • Почему разные зависимости дают такую ​​большую разницу в объеме?Давайте сначала посмотрим на диаграмму архитектуры MegEngine.

    • Снизу вверх идут:

      • Дифференцированная реализация различных серверных частей MegEngine инкапсулирована в dnn (соответствует «аппаратному уровню» на приведенном выше рисунке и соответствует модулю megdnn в CMakeList), а серверные части CUDA имеют большое количество ядер и поддержку большего количества SM. весь объем библиотеки или исполняемой программы дает большой вклад в объем
      • «Уровень аппаратной абстракции» на рисунке, часть «уровня основных компонентов», соответствует модулю megbrain в CMakeList.
      • В верхнем «интерфейсном слое» ему соответствуют модули императива и MegEngineLite в CMakeList.
    • По вышеуказанным причинам на платформе Windows императивный модуль и модуль MegEngineLite будут статически зависеть как от кода dnn, так и от кода megbrain.почти вдвое больше объема. График зависимостей до исправления ошибки:

возможные решения

Благодаря вышеприведенному анализу причина проблемы была найдена, давайте предположим

  • Почему целевые зависимости различаются в Windows и других платформах?

    • Члены класса Windows не могут быть экспортированы неявно, и им необходимо явно использовать dllexport и dllimport.Microsoft Specific
  • dnn, уровень мегамозга имеет большой объем данных, доступ к данным не абстрагируется в функции, а требует прямого доступа к элементам данных.

Давайте возьмем каштан, чтобы проиллюстрировать разницу в способе доступа к членам данных в динамических библиотеках dll, в основном включающих три файла. api.h и api.c реализуют функцию func_a и определяют переменную a, которая компилируется в динамическую библиотеку dll. client.c вызовет указанный выше метод реализации api.c и переменную доступа a

  • Прямой доступ к элементам данных в динамических библиотеках dll

    ////api.h
    #ifdef DLL_EXPORT
    #define DECLSPEC_FUC __declspec(dllexport)
    #define DECLSPEC_DATA __declspec(dllexport)
    #else
    #define DECLSPEC_FUC
    #define DECLSPEC_DATA __declspec(dllimport)
    #endif
    DECLSPEC_DATA extern int a;
    DECLSPEC_FUC void func_a();
    /////api.c
    #include "api.h"
    int a = 0;
    void func_a() {}
    # build api.c with define DLL_EXPORT
    
    ////client.c
    #include "api.h"
    int main(){func_a();a = 1;}
    # build client.c without define DLL_EXPORT
    
  • Доступ к членам данных через функции динамических библиотек dll

    Измените приведенный выше пример кода, чтобы инкапсулировать доступ к переменной a в функцию.

    ////api.h
    #ifdef DLL_EXPORT
    #define DECLSPEC_FUC __declspec(dllexport)
    #else
    #define DECLSPEC_FUC
    #endif
    extern int a;
    DECLSPEC_FUC void func_a();
    DECLSPEC_FUC int * get_a();
    /////api.c
    #include "api.h"
    int a = 0;
    void func_a() {}
    int * get_a() {return &a;}
    # build api.c with define DLL_EXPORT
    
    ////client.c
    #include "api.h"
    int main(){func_a();int *a = get_a(); *a = 1; return 0;}
    # build client.c without define DLL_EXPORT
    
  • Можно видеть, что символ функции и экспорт символа члена данных эквивалентны в Windows,Но требования к импортным данным намного строже

  • В Linux и MacOS символ функции и атрибут экспорта символа члена данных эквивалентны.

Из-за упомянутых выше ограничений при первоначальной поддержке платформы Windows все цели верхнего уровня (MegEngine, MegEngineLite) должны статически зависеть от megbrain и dnn.

Теперь, когда причина проблемы найдена, цель ее устранения становится предельно ясной:Сделайте динамическую библиотеку (dll) megengine_shared доступной на платформах Windows.

Перечислите возможные решения:

  • Вариант 1: WINDOWS_EXPORT_ALL_SYMBOLS, поставляемый с CMake
    • Вывод: не подходит для «больших» проектов, таких как MegEngine.
    • Причина: слишком много символов MegEngine, превышающее максимальное количество символов link.exe 65536 (около 1,7 Вт символов при включении CUDA)
    • Проанализируйте принцип работы CMake WINDOWS_EXPORT_ALL_SYMBOLS, можете добавить несколько хуков посередине для фильтрации символов, которые не нуждаются в экспорте, чтобы добиться эффекта, аналогичного gcc/clang -Wl, --version-script, логика обработки cmake для него:
      • (стадия а): сгенерируйте CMakeFiles/megengine_export.dir/exports.def.objs, который по сути представляет собой набор объектов obj.
      • (этап b): вставьте этап PRE_LINK для создания CMakeFiles/megengine_export.dir/exports.def (этот файл аналогичен gcc/clang -Wl, --version-script)
      • (этап c): LINK_FLAG автовставка /DEF exports.def
      • CMake предоставляет ловушку для этапа вывода, что означает, что exports.def.objs можно изменить, но нет возможности изменить export.def.
        • Добавьте команду ловушки, удалите все объекты DNN в exports.def.objs, все должно быть в порядке
        • Но imperive и megenginelite имеют дело не только с мегамозгом, но и со многими интерфейсами и элементами данных, которые напрямую используют dnn.
  • Сценарий 2: «Оптимизированная» версия WINDOWS_EXPORT_ALL_SYMBOLS
    • Как проанализировано выше, WINDOWS_EXPORT_ALL_SYMBOLS имеет определенные дефекты, и все символы obj будут экспортированы. Можно ли вручную изменить exports.def, сгенерированный WINDOWS_EXPORT_ALL_SYMBOLS?
      • сохранить необходимые символы
      • Модифицированный exports.def целевых зависимостей в CMakeList (пусть его символы не превышают 65536)
      • Вывод: невозможно
        • Windows cl linker.exe не поддерживает подстановочный знак * и не поддерживает сохранение несуществующего символа, в результате после размещения фиксированного export.def немного изменен параметр компиляции или добавлен какой-либо код, не будет компилироваться.
  • Решение 3: В конце концов выясняется, что нет «ленивого» способа решить эту проблему, и мы возвращаемся к самому наивному пути.
    • Явно добавляйте описания атрибутов declspec(dllexport) и declspec(dllimport) ко всем зависимым от API символам членов, предоставляемым megbrain, dnn и megenginelite.
    Пример исправления, см. полную версиюPR
diff --git a/dnn/include/megdnn/basic_types.h b/dnn/include/megdnn/basic_types.h
index 53f22c9af..44831f6d7 100644
--- a/dnn/include/megdnn/basic_types.h
+++ b/dnn/include/megdnn/basic_types.h
@@ -104,22 +104,22 @@ struct TensorShape {
 #if MEGDNN_CC_HOST
     TensorShape() = default;
     TensorShape(const TensorShape& rhs) = default;
-    TensorShape(const SmallVector<size_t>& init_shape);
-    TensorShape(std::initializer_list<size_t> init_shape);
-    std::string to_string() const;
+    MGE_WIN_DECLSPEC_FUC TensorShape(const SmallVector<size_t>& init_shape);
+    MGE_WIN_DECLSPEC_FUC TensorShape(std::initializer_list<size_t> init_shape);
+    MGE_WIN_DECLSPEC_FUC std::string to_string() const;
 #endif
 
     //! total number of elements
-    size_t total_nr_elems() const;
+    MGE_WIN_DECLSPEC_FUC size_t total_nr_elems() const;
 
     //! check whether two shapes are equal
-    bool eq_shape(const TensorShape& rhs) const;
+    MGE_WIN_DECLSPEC_FUC bool eq_shape(const TensorShape& rhs) const;
 
     //! check whether the shape can be treated as a scalar
     bool is_scalar() const { return ndim == 1 && shape[0] == 1; }
 
     //! check whether ndim != 0 and at least one shape is 0
-    bool is_empty() const;
+    MGE_WIN_DECLSPEC_FUC bool is_empty() const;
 
     //! access single element, without boundary check
     size_t& operator[](size_t i) { return shape[i]; }
@@ -168,8 +168,8 @@ struct TensorLayout : public TensorShape {
         class ImplBase;
 
 #if MEGDNN_CC_HOST
-        Format();
-        Format(DType dtype);
+        MGE_WIN_DECLSPEC_FUC Format();
+        MGE_WIN_DECLSPEC_FUC Format(DType dtype);

Представьте лучшие решения в будущем:

  • Измените исходный код самого CMake, пусть флаг WINDOWS_EXPORT_ALL_SYMBOLS поддерживает определяемые пользователем фильтры, а сам сгенерированный файл exports.def имеет параметры пользовательского фильтра
    • Конечно, поскольку элементы данных Windows должны явно добавлять dllimport в раздел импорта, кажется, что CMake ничего не может с этим поделать.
      • Можно считать, что при первом проектировании проекта API не должен иметь неявного доступа между элементами данных, насколько это возможно, и максимально преобразовать его в API функций.

Прикрепил: