Поделиться через


Создание расширения C++ для Python в Visual Studio

В этой статье вы создадите модуль расширения C++ для CPython, чтобы вычислить гиперболический тангенс и вызвать его из кода Python. Подпрограмма реализуется сначала в Python, чтобы продемонстрировать относительную производительность реализации той же подпрограммы в C++.

Модули кода, написанные на языке C++ (или C), обычно используются для расширения возможностей интерпретатора Python. Существует три основных типа модулей расширения:

  • Модули акселератора: включение ускорения производительности. Так как Python является интерпретируемым языком, можно написать модуль акселератора в C++ для повышения производительности.
  • Модули оболочки: предоставление существующих интерфейсов C/C++ в коде Python или предоставление более удобного API python, который легко использовать из Python.
  • Низкоуровневые модули доступа к системе: создайте модули доступа к системе для работы с низкоуровневыми возможностями среды выполнения, операционной системы или основного оборудования.

В этой статье показано два способа сделать модуль расширения C++ доступным для Python:

  • Используйте стандартные CPython расширения, как описано в документации по Python.
  • Используйте PyBind11, который рекомендуется использовать для C++11 из-за простоты. Чтобы обеспечить совместимость, убедитесь, что вы работаете с одной из последних версий Python.

Полный пример этого пошагового руководства доступен на сайте GitHub в python-samples-vs-cpp-extension.

Предпосылки

  • Visual Studio 2017 или более поздней версии с установленной рабочей нагрузкой разработки Python. Рабочая нагрузка включает собственные средства разработки Python, которые добавляют рабочую нагрузку и наборы инструментов C++, необходимые для собственных расширений.

    Снимок экрана: список параметров разработки Python, в котором выделен вариант средств разработки на собственном языке Python.

    Дополнительные сведения о параметрах установки см. в разделе "Установка поддержки Python для Visual Studio".

    Замечание

    При установке рабочей нагрузки приложений для науки о данных и аналитики Python и параметр средств разработки Python, включая средства для разработки на Python устанавливаются по умолчанию.

  • Если вы устанавливаете Python отдельно, обязательно выберите "Скачать символы отладки" в разделе "Дополнительные параметры " в установщике Python. Этот параметр необходим для использования отладки в смешанном режиме между кодом Python и машинным кодом.

Создание приложения Python

Выполните следующие действия, чтобы создать приложение Python.

  1. Создайте новый проект Python в Visual Studio, выбрав Файл>Создать>Проект.

  2. В диалоговом окне "Создание проекта " найдите python. Выберите шаблон приложения Python и нажмите кнопку "Далее".

  3. Введите имя проекта и расположение и нажмите кнопку "Создать".

    Visual Studio создает новый проект. Проект открывается в обозревателе решений , а файл проекта (.py) открывается в редакторе кода.

  4. В файле .py вставьте следующий код. Чтобы воспользоваться некоторыми функциями редактирования Python, попробуйте ввести код вручную.

    Этот код вычисляет гиперболический тангенс без использования математической библиотеки, и это то, что вы ускоряете позже с помощью собственных расширений Python.

    Подсказка

    Напишите код в чистом Python, прежде чем перезаписать его в C++. Таким образом, можно проще проверить правильность собственного кода Python.

    from random import random
    from time import perf_counter
    
    # Change the value of COUNT according to the speed of your computer.
    # The value should enable the benchmark to complete in approximately 2 seconds.
    COUNT = 500000
    DATA = [(random() - 0.5) * 3 for _ in range(COUNT)]
    
    e = 2.7182818284590452353602874713527
    
    def sinh(x):
        return (1 - (e ** (-2 * x))) / (2 * (e ** -x))
    
    def cosh(x):
        return (1 + (e ** (-2 * x))) / (2 * (e ** -x))
    
    def tanh(x):
        tanh_x = sinh(x) / cosh(x)
        return tanh_x
    
    def test(fn, name):
        start = perf_counter()
        result = fn(DATA)
        duration = perf_counter() - start
        print('{} took {:.3f} seconds\n\n'.format(name, duration))
    
        for d in result:
            assert -1 <= d <= 1, " incorrect values"
    
    if __name__ == "__main__":
        print('Running benchmarks with COUNT = {}'.format(COUNT))
    
        test(lambda d: [tanh(x) for x in d], '[tanh(x) for x in d] (Python implementation)')
    
  5. Запустите программу, выбрав Отладка>Начать без отладки или нажав клавиши Ctrl+F5.

    Откроется окно командной строки для отображения выходных данных программы.

  6. В выходных данных обратите внимание на время, указанное для процесса тестирования.

    В этом пошаговом руководстве процесс теста должен занять около 2 секунд.

  7. При необходимости настройте значение переменной COUNT в коде, чтобы обеспечить выполнение теста примерно через 2 секунды на компьютере.

  8. Запустите программу еще раз и убедитесь, что измененное COUNT значение создает тест примерно за 2 секунды.

Подсказка

При выполнении тестов всегда используйте параметр "Запуск отладки>без отладки ". Этот метод помогает избежать затрат, которые могут возникнуть при запуске кода в отладчике Visual Studio.

Создание основных проектов C++

Выполните следующие действия, чтобы создать два идентичных проекта C++, superfastcode и superfastcode2. Позже вы используете другой подход в каждом из проектов для интеграции кода C++ в Python.

  1. В обозревателе решений щелкните правой кнопкой мыши имя решения и выберите "Добавить>новый проект".

    Решение Visual Studio может содержать проекты Python и C++, что является одним из преимуществ разработки Visual Studio для Python.

  2. В диалоговом окне "Добавление нового проекта " задайте для фильтра языказначение C++и введите пустое в поле поиска .

  3. В списке результатов шаблона проекта выберите "Пустой проект" и нажмите кнопку "Далее".

  4. В диалоговом окне "Настройка нового проекта " введите имя проекта:

    • Для первого проекта введите название суперфасткод.
    • Во втором проекте введите имя superfastcode2.
  5. Нажмите кнопку "Создать".

Обязательно повторите эти действия и создайте два проекта.

Подсказка

Альтернативный подход доступен при наличии собственных средств разработки Python в Visual Studio. Вы можете начать с шаблона модуля расширения Python , который предварительно завершает многие действия, описанные в этой статье.

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

Добавление файла C++ в проект

Затем добавьте файл C++ в каждый проект.

  1. В обозревателе решений разверните проект, щелкните правой кнопкой мыши узел "Исходные файлы " и выберите "Добавить>новый элемент".

  2. В списке шаблонов файлов выберите файл C++ (.cpp).

  3. Введите имя файла как module.cpp, а затем нажмите кнопку "Добавить".

    Это важно

    Убедитесь, что имя файла содержит расширение .cpp . Visual Studio ищет файл с расширением .cpp , чтобы включить отображение страниц свойств проекта C++.

  4. На панели инструментов разверните раскрывающееся меню "Конфигурация" и выберите тип целевой конфигурации:

    Снимок экрана, на котором показано, как задать тип целевой конфигурации для проекта C++ в Visual Studio.

    • Для 64-разрядной среды выполнения Python активируйте конфигурацию x64 .
    • Для 32-разрядной среды выполнения Python активируйте конфигурацию Win32 .

Не забудьте повторить эти действия для обоих проектов.

Настройка свойств проекта

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

Необходимо задать свойства проекта для конфигураций сборки отладки и выпуска каждого модуля.

  1. В обозревателе решений щелкните правой кнопкой мыши проект модуля C++ (суперфасткод или суперфасткод2) и выберите "Свойства".

  2. Настройте свойства для отладки сборки модуля, а затем настройте те же свойства для сборки выпуска :

    В верхней части диалогового окна "Страницы свойств проекта" настройте следующие параметры конфигурации файла:

    Снимок экрана: настройка параметров сборки и платформы проекта для файла C++ в Visual Studio.

    1. В разделе "Конфигурация" выберите "Отладка " или "Выпуск". (Эти параметры могут отображаться с активным префиксом.)

    2. Для платформы выберите "Активный" (x64) или "Активный" (Win32) в зависимости от выбранного варианта на предыдущем шаге.

      Замечание

      При создании собственных проектов необходимо настроить конфигурации отладки и выпуска отдельно в соответствии с вашими требованиями к конкретному сценарию. В этом упражнении вы задаете настройки конфигурации для использования финальной сборки CPython. Эта конфигурация отключает некоторые функции отладки среды выполнения C++, включая утверждения. Для использования отладочных бинарных файлов CPython (python_d.exe) требуются иные параметры.

    3. Задайте другие свойства проекта, как описано в следующей таблице.

      Чтобы изменить значение свойства, введите значение в поле свойства. Для некоторых полей можно выбрать текущее значение, чтобы развернуть раскрывающееся меню выбора или открыть диалоговое окно, чтобы определить значение.

      После обновления значений на вкладке нажмите кнопку "Применить " перед переходом на другую вкладку. Это действие помогает убедиться, что изменения остаются.

      Вкладка и раздел Недвижимость Ценность
      Свойства> конфигурацииОбщее Имя цели Укажите имя модуля, который будет ссылаться на него из Python в from...import инструкциях, таких как суперфасткод. Это же имя используется в коде C++ при определении модуля для Python. Чтобы использовать имя проекта в качестве имени модуля, оставьте значение по умолчанию $<ProjectName>. Для python_d.exe добавьте _d в конец имени.
      Тип конфигурации Динамическая библиотека (.dll)
      Свойства конфигурации>Дополнительно Расширение целевого файла .pyd (модуль расширения Python)
      C/C++>Общее Дополнительные каталоги включения Добавьте папку include в каталоге Python в соответствии с вашей установкой (например, c:\Python36\include).
      C/C++>Препроцессор Определения препроцессора Если оно присутствует, измените значение _DEBUG на NDEBUG, чтобы оно соответствовало неотладочной версии CPython. При использовании python_d.exeоставьте это значение неизменным.
      C/C++>Создание кода Библиотека среды выполнения DLL с поддержкой многопоточности (/MD) чтобы соответствовать не отладочной версии CPython. При использовании python_d.exe оставьте это значение в виде многопоточной отладочной DLL (/MDd).
      Базовые проверки среды выполнения По умолчанию
      Компоновщик>Общее Дополнительные каталоги библиотек Добавьте папку libs Python, содержащую файлы .lib, подходящие для вашей установки (например, c:\Python36\libs). Не забудьте указать папку libs , содержащую файлы LIB , а не папку Lib , содержащую файлы .py .

      Это важно

      Если вкладка C/C++ не отображается в качестве параметра свойств проекта, проект не содержит файлов кода, которые Visual Studio определяет как исходные файлы C/C++. Это условие может произойти, если создать исходный файл без расширения C или .cpp .

      Если вы случайно ввели module.coo вместо module.cpp при создании файла C++, Visual Studio создает файл, но не задает тип файла для компилятора C/C+. Этот тип файла необходим для активации вкладки свойств C/C++ в диалоговом окне свойств проекта. Неправильная идентификация остается даже при переименовании файла кода с расширением .cpp.

      Чтобы правильно задать тип файла кода, в обозревателе решений щелкните правой кнопкой мыши файл кода и выберите "Свойства". Для типа элемента выберите компилятор C/C++.

    4. После обновления всех свойств нажмите кнопку "ОК".

    Повторите шаги для другой конфигурации сборки.

  3. Проверьте текущую конфигурацию. Повторите следующие действия для сборок отладки и релизных сборок обоих проектов C++.

    1. На панели инструментов Visual Studio установите конфигурацию сборка на Отладка или Релиз:

      Снимок экрана: настройка конфигурации сборки для проекта C++ в Visual Studio.

    2. В Обозревателе решений щелкните правой кнопкой мыши проект C++ и выберите Сборка.

      Pyd-файлы находятся в папке решения в разделе "Отладка и выпуск", а не в самой папке проекта C++.

Добавление кода и конфигурации теста

Теперь вы готовы добавить код в файлы C++ и протестировать релизную сборку.

  1. Для проекта суперфасткода C++ откройте файл module.cpp в редакторе кода.

  2. В файле module.cpp вставьте следующий код:

    #include <Windows.h>
    #include <cmath>
    
    const double e = 2.7182818284590452353602874713527;
    
    double sinh_impl(double x) {
        return (1 - pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double cosh_impl(double x) {
        return (1 + pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double tanh_impl(double x) {
        return sinh_impl(x) / cosh_impl(x);
    }
    
  3. Сохраните ваши изменения.

  4. Создайте конфигурацию выпуска для проекта C++ для подтверждения правильности кода.

Повторите действия, чтобы добавить код в файл C++ для проекта superfastcode2 и протестировать сборку релиз.

Преобразование проектов C++ в расширения Python

Чтобы сделать библиотеку DLL C++ расширением для Python, сначала необходимо изменить экспортированные методы для взаимодействия с типами Python. Затем добавьте функцию для экспорта модуля вместе с определениями методов модуля.

В следующих разделах показано, как создать расширения с помощью расширений CPython и PyBind11. Проект superfasctcode использует расширения CPython, а проект superfasctcode2 реализует PyBind11.

Использование расширений CPython

Дополнительные сведения о коде, представленном в этом разделе, см. в руководстве по API Python/C, особенно на странице "Объекты модулей ". При просмотре ссылочного содержимого обязательно выберите свою версию Python в раскрывающемся списке в правом верхнем углу.

  1. Для проекта суперфасткода C++ откройте файл module.cpp в редакторе кода.

  2. Добавьте инструкцию в верхней части файла module.cpp , чтобы включить файл заголовка Python.h :

    #include <Python.h>
    
  3. Замените код метода tanh_impl, чтобы он принимал и возвращал типы Python (то есть PyObject*).

    PyObject* tanh_impl(PyObject* /* unused module reference */, PyObject* o) {
        double x = PyFloat_AsDouble(o);
        double tanh_x = sinh_impl(x) / cosh_impl(x);
        return PyFloat_FromDouble(tanh_x);
    }
    
  4. В конце файла добавьте структуру, чтобы определить, как представить функцию C++ tanh_impl в Python:

    static PyMethodDef superfastcode_methods[] = {
        // The first property is the name exposed to Python, fast_tanh
        // The second is the C++ function with the implementation
        // METH_O means it takes a single PyObject argument
        { "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr },
    
        // Terminate the array with an object containing nulls
        { nullptr, nullptr, 0, nullptr }
    };
    
  5. Добавьте другую структуру, чтобы определить, как ссылаться на модуль в коде Python, в частности при использовании инструкции from...import .

    Имя, импортированное в этом коде, должно соответствовать значению в свойствах проекта в разделе Свойства конфигурации>Общие>Целевое имя.

    В следующем примере "superfastcode" имя означает, что вы можете использовать инструкцию from superfastcode import fast_tanh в Python, потому что fast_tanh определено внутри superfastcode_methods. Имена файлов, которые являются внутренними для проекта C++, например module.cpp, являются незначительными.

    static PyModuleDef superfastcode_module = {
        PyModuleDef_HEAD_INIT,
        "superfastcode",                        // Module name to use with Python import statements
        "Provides some functions, but faster",  // Module description
        0,
        superfastcode_methods                   // Structure that defines the methods of the module
    };
    
  6. Добавьте метод, вызывающий Python при загрузке модуля. Имя метода должно бытьPyInit_<module-name>, где <имя> модуля точно соответствует свойству конфигурации> проекта C++General>Target Name. То есть имя метода соответствует имени файла PYD-файла , созданного проектом.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Создайте проект C++ и проверьте код. Если возникают ошибки, см. раздел "Устранение ошибок компиляции".

Использование PyBind11

Если вы выполните действия, описанные в предыдущем разделе для проекта суперфасткода, вы можете заметить, что упражнению требуется шаблонный код для создания структур модулей для расширений C++ CPython. В этом упражнении вы узнаете, что PyBind11 упрощает процесс написания кода. Макросы в файле заголовка C++ используются для достижения того же результата, но с гораздо меньшим кодом. Однако для поиска библиотек PyBind11 и включения файлов в Visual Studio требуются дополнительные действия. Дополнительные сведения о коде в этом разделе см. в основах PyBind11.

Установка PyBind11

Первым шагом является установка PyBind11 в конфигурации проекта. В этом упражнении используется окно PowerShell разработчика .

  1. Откройте окно ИнструментовКомандной строки>PowerShell для Разработчика>.

  2. В окне PowerShell для разработчиков установите PyBind11 с помощью команды pip install pybind11 pip или py -m pip install pybind11.

    Visual Studio устанавливает PyBind11 и его зависимые пакеты.

Добавить пути PyBind11 в проект

После установки PyBind11 необходимо добавить пути PyBind11 в свойство Дополнительные каталоги включения для проекта.

  1. В окне Разработчика PowerShell выполните команду python -m pybind11 --includes или py -m pybind11 --includes.

    Это действие выводит список путей PyBind11, которые необходимо добавить в свойства проекта.

  2. Выделите список путей в окне и выберите " Копировать " (двойная страница) на панели инструментов окна.

    Снимок экрана: выделение и копирование списка путей из окна PowerShell разработчика в Visual Studio.

    Список объединенных путей добавляется в буфер обмена.

  3. В обозревателе решений щелкните правой кнопкой мыши проект superfastcode2 и выберите "Свойства".

  4. В верхней части диалогового окна "Страницы свойств" в поле "Конфигурация " выберите "Выпуск". (Этот параметр может отображаться с активным префиксом.)

  5. Во диалоговом окне, во вкладке C/C++>General, раскройте меню для свойства "Дополнительные каталоги включения", и выберите "Изменить".

  6. В всплывающем диалоговом окне добавьте список скопированных путей:

    Повторите эти действия для каждого пути в сцепленном списке, скопированном из окна PowerShell для разработчиков:

    1. Выберите Новую строку (папку с символом плюса) на панели инструментов всплывающего окна.

      Снимок экрана, на котором показано, как добавить путь PyBind11 к свойству

      Visual Studio добавляет пустую строку в верхней части списка путей и помещает курсор вставки в начало.

    2. Вставьте путь PyBind11 в пустую строку.

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

      Это важно

      • Если путь содержит -I префикс, удалите префикс из пути.
      • Чтобы Visual Studio распознала путь, путь должен находиться в отдельной строке.

      После добавления нового пути Visual Studio отображает подтвержденный путь в поле "Вычисляемое значение ".

  7. Нажмите кнопку "ОК ", чтобы выйти из всплывающего окна.

  8. В верхней части диалогового окна "Страницы свойств" наведите указатель мыши на значение свойства "Дополнительные каталоги включения" и подтвердите наличие путей PyBind11.

  9. Нажмите кнопку "ОК ", чтобы применить изменения свойств.

Обновление файла module.cpp

Последним шагом является добавление файла заголовка PyBind11 и кода макроса в файл проекта C++.

  1. Для проекта superfastcode2 C++ откройте файл module.cpp в редакторе кода.

  2. Добавьте инструкцию в верхней части файла module.cpp , чтобы включить файл заголовка pybind11.h :

    #include <pybind11/pybind11.h>
    
  3. В конце файла module.cpp добавьте код для макроса, PYBIND11_MODULE чтобы определить точку входа в функцию C++:

    namespace py = pybind11;
    
    PYBIND11_MODULE(superfastcode2, m) {
        m.def("fast_tanh2", &tanh_impl, R"pbdoc(
            Compute a hyperbolic tangent of a single argument expressed in radians.
        )pbdoc");
    
    #ifdef VERSION_INFO
        m.attr("__version__") = VERSION_INFO;
    #else
        m.attr("__version__") = "dev";
    #endif
    }
    
  4. Создайте проект C++ и проверьте код. При возникновении ошибок см. следующий раздел: устранение ошибок компиляции.

Устранение ошибок компиляции

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

Ошибка: не удается найти файл заголовка

Visual Studio возвращает сообщение об ошибке, например E1696: не удается открыть файл "Python.h" или C1083: не удается открыть файл": "Python.h": нет такого файла или каталога.

Эта ошибка означает, что компилятор не может найти необходимый файл заголовка (H) для проекта.

  • Для проекта superfastcode убедитесь, что свойство проекта C/C++>General>Дополнительные каталоги для включения содержит путь к папке include для вашей установки Python. Просмотрите действия, описанные в разделе "Настройка свойств проекта".

  • Для проекта superfastcode2 убедитесь, что одно и то же свойство проекта содержит путь к папке включения для установки PyBind11. Ознакомьтесь с инструкциями по пути Ad PyBind к проекту.

Дополнительные сведения о доступе к сведениям о конфигурации установки Python см. в документации по Python.

Ошибка: не удается найти библиотеки Python

Visual Studio возвращает ошибку, указывающую, что компилятор не может найти необходимые файлы библиотеки (DLL) для проекта.

  • Для проекта C++ (superfastcode или superfastcode2) убедитесь, что свойство Linker>General>Additional Library Directories содержит путь к папке libs для установки Python. Просмотрите действия, описанные в разделе "Настройка свойств проекта".

Дополнительные сведения о доступе к сведениям о конфигурации установки Python см. в документации по Python.

Visual Studio сообщает об ошибках компоновщика, которые связаны с конфигурацией целевой архитектуры вашего проекта, такой как x64 или Win32.

  • Для проекта C++ (superfastcode или superfastcode2) измените целевую конфигурацию в соответствии с установкой Python. Например, если целевая конфигурация проекта C++ — Win32, но установка Python имеет 64-разрядную версию, измените целевую конфигурацию проекта C++ на x64.

Тестирование кода и сравнение результатов

Теперь, когда у вас есть библиотеки DLL, структурированные как расширения Python, можно ссылаться на них из проекта Python, импортировать модули и использовать их методы.

Сделать библиотеку DLL доступной для Python

Библиотеку DLL можно сделать доступной для Python несколькими способами. Ниже приведены два варианта.

Если проект Python и проект C++ находятся в одном решении, можно использовать следующий подход:

  1. В обозревателе решений щелкните правой кнопкой мыши узел "Ссылки" в проекте Python и выберите "Добавить ссылку".

    Не забудьте выполнить это действие для проекта Python, а не для проекта C++.

  2. В диалоговом окне "Добавить ссылку" разверните вкладку "Проекты ".

  3. Установите флажки для проектов superfastcode и superfastcode2 и нажмите кнопку "ОК".

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

Альтернативный подход — установить модуль расширения C++ в среде Python. Этот метод делает модуль доступным для других проектов Python. Дополнительные сведения см. в документации по проекту setuptools.

Выполните следующие действия, чтобы установить модуль расширения C++ в среде Python:

  1. В обозревателе решений щелкните правой кнопкой мыши проект C++ и выберите "Добавить>новый элемент".

  2. В списке шаблонов файлов выберите файл C++ (.cpp).

  3. Введите имя файла как setup.py, а затем нажмите кнопку "Добавить".

    Обязательно введите имя файла с расширением Python (.py). Visual Studio распознает файл как код Python, несмотря на использование шаблона файла C++.

    Visual Studio открывает новый файл в редакторе кода.

  4. Вставьте следующий код в новый файл. Выберите версию кода, соответствующую методу расширения:

    • Расширения CPython (проект суперфасткода ):

      from setuptools import setup, Extension
      
      sfc_module = Extension('superfastcode', sources = ['module.cpp'])
      
      setup(
          name='superfastcode',
          version='1.0',
          description='Python Package with superfastcode C++ extension',
          ext_modules=[sfc_module]
      )
      
    • PyBind11 (проект superfastcode2 ):

      from setuptools import setup, Extension
      import pybind11
      
      cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7']
      
      sfc_module = Extension(
          'superfastcode2',
          sources=['module.cpp'],
          include_dirs=[pybind11.get_include()],
          language='c++',
          extra_compile_args=cpp_args,
      )
      
      setup(
          name='superfastcode2',
          version='1.0',
          description='Python package with superfastcode2 C++ extension (PyBind11)',
          ext_modules=[sfc_module],
      )
      
  5. В проекте C++ создайте второй файл с именем pyproject.toml и вставьте следующий код:

    [build-system]
    requires = ["setuptools", "wheel", "pybind11"]
    build-backend = "setuptools.build_meta"
    

    В файле TOML (TOML) используется очевидный, минимальный языковой формат Tom для файлов конфигурации.

  6. Чтобы создать расширение, щелкните правой кнопкой мыши имя файла pyproject.toml на вкладке окна кода и выберите "Копировать полный путь".

    Снимок экрана, на котором показано, как скопировать полный путь к файлу toml проекта py в Visual Studio.

    Перед его использованием удалите имя pyproject.toml из пути.

  7. В Проводнике решений разверните узел Среды Python в решении.

  8. Щелкните правой кнопкой мыши активную среду Python (показано полужирным шрифтом) и выберите пункт "Управление пакетами Python".

    Откроется панель сред Python.

    Если необходимый пакет уже установлен, вы увидите его в этой области.

    • Прежде чем продолжить, выберите X рядом с именем пакета, чтобы удалить его.

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

  9. В поле поиска в области сред Python вставьте скопированный путь и удалите имя файла pyproject.toml из конца пути.

    Снимок экрана, на котором показано, как ввести путь в области сред Python для установки модуля расширения.

  10. Выберите ВВОД, чтобы установить модуль по указанному скопированному пути.

    Подсказка

    Если установка завершается сбоем из-за ошибки разрешения, добавьте --user аргумент в конец команды и повторите попытку установки.

Вызов библиотеки DLL из Python

После того как вы сделаете библиотеку DLL доступной для Python, как описано в предыдущем разделе, вы можете вызвать функции superfastcode.fast_tanh и superfastcode2.fast_tanh2 из Python. Затем можно сравнить производительность функции с реализацией Python.

Чтобы вызвать DLL-файл модуля расширения из Python, выполните следующие действия:

  1. Откройте файл .py для проекта Python в редакторе кода.

  2. В конце файла добавьте следующий код, чтобы вызвать методы, экспортированные из библиотек DLL, и отобразить их выходные данные:

    from superfastcode import fast_tanh
    test(lambda d: [fast_tanh(x) for x in d], '[fast_tanh(x) for x in d] (CPython C++ extension)')
    
    from superfastcode2 import fast_tanh2
    test(lambda d: [fast_tanh2(x) for x in d], '[fast_tanh2(x) for x in d] (PyBind11 C++ extension)')
    
  3. Запустите программу Python, выбрав "Начать без отладки>" или используйте сочетание клавиш Ctrl+F5.

    Замечание

    Если команда "Пуск без отладки " недоступна, в обозревателе решений щелкните правой кнопкой мыши проект Python и выберите "Задать в качестве запускаемого проекта".

    При выполнении программы обратите внимание, что подпрограммы C++ выполняются примерно в 5–20 раз быстрее, чем реализация Python.

    Ниже приведен пример типичных выходных данных программы:

    Running benchmarks with COUNT = 500000
    [tanh(x) for x in d] (Python implementation) took 0.758 seconds
    
    [fast_tanh(x) for x in d] (CPython C++ extension) took 0.076 seconds
    
    [fast_tanh2(x) for x in d] (PyBind11 C++ extension) took 0.204 seconds
    
  4. Попробуйте увеличить COUNT переменную, чтобы различия во времени были более выраженными.

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

Скорость обработки и затраты на обработку адресов

В выходных данных можно заметить, что расширение PyBind11 не так быстро, как расширение CPython, хотя оно должно быть быстрее, чем чистая реализация Python. Основная причина разницы заключается в использовании флага METH_O. Этот флаг не поддерживает несколько параметров, имен параметров или аргументов ключевых слов. PyBind11 создает немного более сложный код, чтобы предоставить более похожий на Python интерфейс вызывающим. Так как тестовый код вызывает функцию 500 000 раз, результаты могут значительно повысить нагрузку.

Дополнительные затраты можно сократить, переместив for цикл в код, написанный на Python. Этот подход включает использование протокола итератора (или типа PyBind11 py::iterable для параметра функции) для обработки каждого элемента. Удаление повторяющихся переходов между Python и C++ — эффективный способ сократить время, необходимое для обработки последовательности.

Устранение ошибок импорта

Если вы получите сообщение при попытке ImportError импортировать ваш модуль, это можно устранить одним из следующих способов:

  • При сборке с помощью ссылки на проект убедитесь, что свойства проекта C++ соответствуют среде Python, активированной для проекта Python. Убедитесь, что те же расположения папок используются для файлов Include (H) и Библиотеки (DLL).

  • Убедитесь, что выходной файл имеет правильное имя, например superfastcode.pyd. Неправильное имя или расширение запрещает импорт необходимого файла.

  • Если вы устанавливаете модуль с помощью файла setup.py , обязательно выполните pip команду в среде Python, активированной для проекта Python. При развертывании активной среды Python для проекта в обозревателе решений вы увидите запись для проекта C++, например суперфасткод.

Отладка кода C++

Visual Studio поддерживает отладку кода Python и C++ вместе. Следующие шаги демонстрируют процесс отладки для проекта C++ суперфасткода , но процесс совпадает с проектом superfastcode2 .

  1. В обозревателе решений щелкните правой кнопкой мыши проект Python и выберите "Свойства".

  2. В области "Свойства" выберите вкладку "Отладка " и выберите параметр отладки>"Включить отладку машинного кода ".

    Подсказка

    При включении отладки нативного кода окно вывода Python может закрыться сразу после завершения программы, не показывая запрос Нажмите любую клавишу, чтобы продолжить. Чтобы принудить приостановку и запрос после включения отладки нативного кода, добавьте аргумент -i в поле Запуск>Аргументы интерпретатора на вкладке Отладка. Этот аргумент переводит интерпретатор Python в интерактивный режим после выполнения кода. Программа ожидает, когда вы выберете Ctrl+Z+ВВОД, чтобы закрыть окно. Альтернативный подход — добавить import os и os.system("pause") инструкции в конце программы Python. Этот код дублирует исходный запрос приостановки.

  3. Нажмите кнопку"Сохранитьфайл>" (или CTRL+S), чтобы сохранить изменения свойств.

  4. На панели инструментов Visual Studio установите конфигурацию сборки на Отладка.

  5. Так как код обычно занимает больше времени для выполнения в отладчике, может потребоваться изменить COUNT переменную в проекте Python .py на значение, которое примерно в пять раз меньше значения по умолчанию. Например, измените его с 500000 на 100000.

  6. В коде C++ задайте точку останова в первой строке tanh_impl метода.

  7. Запустите отладчик, выбрав> "Начать отладку" или используйте сочетание клавиш F5.

    Отладчик останавливается при вызове кода точки останова. Если точка останова не включена, убедитесь, что конфигурация настроена как отладка и что вы сохранили проект, который не происходит автоматически при запуске отладчика .

    Снимок экрана: код C++, содержащий точку останова в Visual Studio.

  8. На точке останова можно пошагово пройтись по коду C++, просмотреть переменные и т. д. Дополнительные сведения об этих функциях см. в статье Отладка Python и C++ вместе.

Альтернативные подходы

Расширения Python можно создавать различными способами, как описано в следующей таблице. Первые две строки, CPython и PyBind11, обсуждаются в этой статье.

Подход Винтаж Представительные пользователи
Модули расширения C/C++ для CPython 1991 Стандартная библиотека
PyBind11 (рекомендуется для C++) 2015
Cython (рекомендуется для C) 2007 gevent, kivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 криптография, pypy
SWIG 1996 crfsuite
Boost.Python 2002
cppyy 2017