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

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

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

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

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

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

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

Необходимые компоненты

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

    Screenshot of a list of Python development options, highlighting the Python native development tools option.

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

    Примечание.

    При установке рабочей нагрузки Приложения для обработки и анализа данных и аналитические приложения среда 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. На панели инструментов разверните раскрывающееся меню "Конфигурация" и выберите тип целевой конфигурации:

    Screenshot that shows how to set the target configuration type for the C++ project in Visual Studio.

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

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

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

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

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

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

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

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

    Screenshot that shows how to set the project build and platform options for the C++ file in 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++>Общие Дополнительные каталоги включаемых файлов Добавьте папку "Включить Python" в соответствии с вашей установкой (например, c:\Python36\include).
      C/C++>Препроцессор Определения препроцессора Если он присутствует, измените значение _DEBUG на NDEBUG , чтобы оно соответствовало версии CPython. При использовании python_d.exe оставьте это значение неизменным.
      C/C++>Создание кода Библиотека времени выполнения Многопоточные библиотеки DLL (/MD) для соответствия версии выпуска (nondebug) CPython. При использовании python_d.exe оставьте это значение как многопоточная библиотека DLL отладки (/MDd).
      Базовые проверки среды выполнения По умолчанию
      Компоновщик>Общие Дополнительные каталоги библиотек Добавьте подходящую для вашей установки папку Python libs с файлами .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 задайте конфигурацию сборки для отладки или выпуска:

      Screenshot that shows how to set the build configuration for the C++ project in 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 .

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

    В следующем примере имя означает, что вы можете использовать инструкцию from superfastcode import fast_tanh в Python, "superfastcode" так как 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++ 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. Выделите список путей в окне и выберите " Копировать " (двойная страница) на панели инструментов окна.

    Screenshot that shows how to highlight and copy the list of paths from the Developer PowerShell window in Visual Studio.

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

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

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

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

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

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

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

      Screenshot that shows how to add a PyBind11 path to the Additional Include Directories property.

      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) для проекта.

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

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

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

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

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

  • Для проекта C++ (superfastcode или superfastcode2) убедитесь, что свойство linker>General>Library Directoryies содержит путь к папке 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. Выберите проверка boxes для проектов superfastcode и superfastcode2 и нажмите кнопку "ОК".

    Screenshot that shows how to add a reference to the super fast code project in 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 на вкладке окна кода и выберите "Копировать полный путь".

    Screenshot that shows how to copy the full path to the py project toml file in Visual Studio.

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

  7. В Обозреватель решений разверните узел сред Python для решения.

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

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

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

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

    Screenshot that shows how to uninstall a package in the Python Environments pane.

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

    Screenshot that shows how to enter the path in the Python Environments pane to install the extension module.

  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. Этот подход предполагает использование протокола итератора (или типа py::iterable из PyBind11 в качестве параметра функции) для обработки каждого элемента. Удаление повторяющихся переходов между 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.

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

    Screenshot of C++ code that contains a breakpoint in Visual Studio.

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

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

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

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