Creare un'estensione C++ per Python in Visual Studio

In questo articolo si compila un modulo di estensione C++ per CPython per calcolare una tangente iperbolica e chiamarla dal codice Python. La routine viene implementata prima in Python per illustrare il miglioramento relativo delle prestazioni dell'implementazione della routine stessa in C++.

I moduli di codice scritti in C++ (o C) vengono comunemente usati per estendere le funzionalità di un interprete Python. Esistono tre tipi principali di moduli di estensione:

  • Moduli di acceleratore: abilitare le prestazioni accelerate. Poiché Python è un linguaggio interpretato, è possibile scrivere un modulo di acceleratore in C++ per ottenere prestazioni migliori.
  • Moduli wrapper: esporre le interfacce C/C++ esistenti al codice Python o esporre un'API più simile a Python facile da usare da Python.
  • Moduli di accesso al sistema di basso livello: creare moduli di accesso al sistema per raggiungere funzionalità di livello inferiore del CPython runtime, del sistema operativo o dell'hardware sottostante.

Questo articolo illustra due modi per rendere disponibile un modulo di estensione C++ per Python:

  • Usare le estensioni standard CPython , come descritto nella documentazione di Python.
  • Usare PyBind11, consigliato per C++11 a causa della sua semplicità. Per garantire la compatibilità, assicurarsi di usare una delle versioni più recenti di Python.

L'esempio completo per questa procedura dettagliata è disponibile in GitHub in python-samples-vs-cpp-extension.

Prerequisiti

  • Visual Studio 2017 o versione successiva, con il carico di lavoro Sviluppo Python installato. Il carico di lavoro include gli strumenti di sviluppo nativi Python, che aggiungono i set di strumenti e il carico di lavoro C++ necessari per le estensioni native.

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

    Per altre informazioni sulle opzioni di installazione, vedere Installare il supporto python per Visual Studio.

    Nota

    Quando si installa il carico di lavoro Applicazioni analitiche e di data science, python e l'opzione strumenti di sviluppo nativi Python vengono installati per impostazione predefinita.

  • Se si installa Python separatamente, assicurarsi di selezionare Scarica simboli di debug in Opzioni avanzate nel programma di installazione di Python. Questa opzione è necessaria per usare il debug in modalità mista tra il codice Python e il codice nativo.

Creare un'applicazione Python

Seguire questa procedura per creare l'applicazione Python.

  1. Creare un nuovo progetto di Python in Visual Studio selezionando File>Nuovo>Progetto.

  2. Nella finestra di dialogo Crea un nuovo progetto cercare Python. Selezionare il modello Applicazione Python e selezionare Avanti.

  3. Immettere un nome di progetto e un percorso e selezionare Crea.

    Visual Studio crea il nuovo progetto. Il progetto viene aperto in Esplora soluzioni e il file di progetto (.py) viene aperto nell'editor di codice.

  4. Nel file .py incollare il codice seguente. Per sperimentare alcune delle funzionalità di modifica di Python, provare a immettere manualmente il codice.

    Questo codice calcola una tangente iperbolica senza usare la libreria matematica ed è ciò che si accelera in un secondo momento con le estensioni native Python.

    Suggerimento

    Scrivere il codice in Python puro prima di riscriverlo in C++. In questo modo, è possibile verificare più facilmente che il codice Python nativo sia corretto.

    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. Eseguire il programma selezionando Avvia debug>senza eseguire debug o selezionare i tasti di scelta rapida CTRL+F5.

    Viene visualizzata una finestra di comando per visualizzare l'output del programma.

  6. Nell'output notare la quantità di tempo segnalata per il processo di benchmark.

    Per questa procedura dettagliata, il processo di benchmark richiede circa 2 secondi.

  7. Se necessario, modificare il valore della COUNT variabile nel codice per consentire il completamento del benchmark in circa 2 secondi nel computer.

  8. Eseguire di nuovo il programma e verificare che il valore modificato COUNT produa il benchmark in circa 2 secondi.

Suggerimento

Quando si eseguono benchmark, usare sempre l'opzione Debug>Start senza debug. Questo metodo consente di evitare il sovraccarico che può comportare quando si esegue il codice all'interno del debugger di Visual Studio.

Creare i progetti C++ di base

Seguire questa procedura per creare due progetti C++ identici, superfastcode e superfastcode2. Successivamente, si usa un approccio diverso in ogni progetto per esporre il codice C++ in Python.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nome della soluzione e scegliere Aggiungi>nuovo progetto.

    Una soluzione di Visual Studio può contenere sia progetti Python che C++, uno dei vantaggi dell'uso dello sviluppo di Visual Studio per Python.

  2. Nella finestra di dialogo Aggiungi un nuovo progetto impostare Il filtro lingua su C++, quindi immettere vuoto nella casella Cerca.

  3. Nell'elenco dei risultati del modello di progetto selezionare Progetto vuoto e selezionare Avanti.

  4. Nella finestra di dialogo Configura il nuovo progetto immettere il nome del progetto:

    • Per il primo progetto immettere il nome superfastcode.
    • Per il secondo progetto immettere il nome superfastcode2.
  5. Seleziona Crea.

Assicurarsi di ripetere questi passaggi e creare due progetti.

Suggerimento

Un approccio alternativo è disponibile quando sono installati gli strumenti di sviluppo nativi Python in Visual Studio. È possibile iniziare con il modello Modulo di estensione Python, che pre-completa molti dei passaggi descritti in questo articolo.

Per la procedura dettagliata descritta in questo articolo, a partire da un progetto vuoto viene illustrato come compilare il modulo di estensione in modo dettagliato. Dopo aver compreso il processo, è possibile usare il modello alternativo per risparmiare tempo quando si scrivono estensioni personalizzate.

Aggiungere un file C++ al progetto

Aggiungere quindi un file C++ a ogni progetto.

  1. In Esplora soluzioni espandere il progetto, fare clic con il pulsante destro del mouse sul nodo File di origine e scegliere Aggiungi>nuovo elemento.

  2. Nell'elenco dei modelli di file selezionare File C++ (.cpp).

  3. Immettere il nome del file come module.cpp e quindi selezionare Aggiungi.

    Importante

    Assicurarsi che il nome del file includa l'estensione .cpp . Visual Studio cerca un file con l'estensione .cpp per abilitare la visualizzazione delle pagine delle proprietà del progetto C++.

  4. Sulla barra degli strumenti espandere il menu a discesa Configurazione e selezionare il tipo di configurazione di destinazione:

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

    • Per un runtime Python a 64 bit, attivare la configurazione x64 .
    • Per un runtime Python a 32 bit, attivare la configurazione Win32 .

Assicurarsi di ripetere questi passaggi per entrambi i progetti.

Configurare le proprietà del progetto

Prima di aggiungere codice ai nuovi file C++, configurare le proprietà per ogni progetto di modulo C++ e testare le configurazioni per assicurarsi che tutto funzioni.

È necessario impostare le proprietà del progetto per le configurazioni di compilazione di debug e versione di ogni modulo.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto del modulo C++ (superfastcode o superfastcode2) e scegliere Proprietà.

  2. Configurare le proprietà per la compilazione di debug del modulo e quindi configurare le stesse proprietà per la build di versione :

    Nella parte superiore della finestra di dialogo Pagine delle proprietà del progetto configurare le opzioni di configurazione del file seguenti:

    Screenshot that shows how to set the project build and platform options for the C++ file in Visual Studio.

    1. Per Configurazione selezionare Debug o Versione. Queste opzioni potrebbero essere visualizzate con Prefisso attivo .

    2. Per Piattaforma selezionare Attivo (x64) o Attivo (Win32), a seconda della selezione nel passaggio precedente.

      Nota

      Quando si creano progetti personalizzati, è necessario configurare separatamente le configurazioni di debug e rilascio in base ai requisiti specifici dello scenario. In questo esercizio si impostano le configurazioni per usare una build di versione di CPython. Questa configurazione disabilita alcune funzionalità di debug del runtime C++, incluse le asserzioni. L'uso di file binari di debug CPython (python_d.exe) richiede impostazioni diverse.

    3. Impostare altre proprietà del progetto come descritto nella tabella seguente.

      Per modificare un valore della proprietà, immettere un valore nel campo della proprietà. Per alcuni campi, è possibile selezionare il valore corrente per espandere un menu a discesa di scelte o aprire una finestra di dialogo per definire il valore.

      Dopo aver aggiornato i valori in una scheda, selezionare Applica prima di passare a una scheda diversa. Questa azione consente di garantire che le modifiche rimangano.

      Scheda e sezione Proprietà valore
      Proprietà>di configurazione Generale Nome destinazione Specificare il nome del modulo per farvi riferimento da Python nelle from...import istruzioni, ad esempio superfastcode. Questo nome viene usato nel codice C++ quando si definisce il modulo per Python. Per usare il nome del progetto come nome del modulo, lasciare il valore predefinito $ProjectName>.< Per python_d.exe, aggiungere _d alla fine del nome.
      Tipo configurazione Libreria dinamica (.dll)
      Proprietà>di configurazione avanzate Estensione file di destinazione .pyd (modulo di estensione Python)
      C/C++>Generale Directory di inclusione aggiuntive Aggiungere la cartella include Python in base alle esigenze per l'installazione, ad esempio c:\Python36\include.
      C/C++>Preprocessore Definizioni del preprocessore Se è presente, modificare il valore _DEBUG in NDEBUG in modo che corrisponda alla versione nonebug di CPython. Quando si usa python_d.exe, lasciare invariato questo valore.
      C/C++>Generazione codice Libreria di runtime DLL multithread (/MD) in modo che corrisponda alla versione (non diebug) di CPython. Quando si usa python_d.exe, lasciare questo valore come DLL di debug a thread multipla (/MDd).When you use python_d.exe, leave this value as Multi-threaded Debug DLL (/MDd).
      Controlli di runtime di base Predefinita
      Linker>Generale Directory librerie aggiuntive Aggiungere la cartella libs Python che contiene i file lib, come appropriato per l'installazione, ad esempio c:\Python36\libs. Assicurarsi di puntare alla cartella libs che contiene file lib e non alla cartella Lib che contiene .py file.

      Importante

      Se la scheda C/C++ non viene visualizzata come opzione per le proprietà del progetto, il progetto non contiene file di codice identificati da Visual Studio come file di origine C/C++. Questa condizione può verificarsi se si crea un file di origine senza estensione c o .cpp .

      Se è stato immesso accidentalmente module.coo anziché module.cpp quando è stato creato il file C++, Visual Studio crea il file ma non imposta il tipo di file sul compilatore C/C+. Questo tipo di file è necessario per attivare la presenza della scheda proprietà C/C++ nella finestra di dialogo delle proprietà del progetto. L'identificazione errata rimane anche se si rinomina il file di codice con un'estensione di file .cpp .

      Per impostare correttamente il tipo di file di codice, in Esplora soluzioni fare clic con il pulsante destro del mouse sul file di codice e scegliere Proprietà. Per Tipo di elemento selezionare compilatore C/C++.

    4. Dopo aver aggiornato tutte le proprietà, selezionare OK.

    Ripetere i passaggi per l'altra configurazione di compilazione.

  3. Testare la configurazione corrente. Ripetere i passaggi seguenti per le build di debug e versione di entrambi i progetti C++.

    1. Sulla barra degli strumenti di Visual Studio impostare La configurazione di compilazione su Debug o Versione:

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

    2. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto C++ e scegliere Compila.

      I file con estensione pyd si trovano nella cartella della soluzione , in Debug e Versione e non nella cartella del progetto C++.

Aggiungere codice e configurazione di test

A questo momento si è pronti per aggiungere codice ai file C++ e testare la build di versione .

  1. Per il progetto C++ superfastcode aprire il file module.cpp nell'editor di codice.

  2. Nel file module.cpp incollare il codice seguente:

    #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. Salva le modifiche.

  4. Compilare la configurazione della versione per il progetto C++ per verificare che il codice sia corretto.

Ripetere i passaggi per aggiungere codice al file C++ per il progetto superfastcode2 e testare la build di versione .

Convertire progetti C++ in estensioni Python

Per rendere la DLL C++ un'estensione per Python, modificare prima di tutto i metodi esportati per interagire con i tipi Python. Aggiungere quindi una funzione per esportare il modulo, insieme alle definizioni per i metodi del modulo.

Le sezioni seguenti illustrano come creare le estensioni usando le estensioni CPython e PyBind11. Il progetto superfasctcode usa le estensioni CPython e il progetto superfasctcode2 implementa PyBind11.

Usare le estensioni CPython

Per altre informazioni sul codice presentato in questa sezione, vedere Manuale di riferimento api Python/C, in particolare la pagina Oggetti modulo. Quando si esamina il contenuto di riferimento, assicurarsi di selezionare la versione di Python nell'elenco a discesa in alto a destra.

  1. Per il progetto C++ superfastcode aprire il file module.cpp nell'editor di codice.

  2. Aggiungere un'istruzione all'inizio del file module.cpp per includere il file di intestazione Python.h :

    #include <Python.h>
    
  3. Sostituire il codice del tanh_impl metodo per accettare e restituire i tipi Python( ovvero un 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. Alla fine del file aggiungere una struttura per definire come presentare la funzione C++ tanh_impl a 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. Aggiungere un'altra struttura per definire come fare riferimento al modulo nel codice Python, in particolare quando si usa l'istruzione from...import .

    Il nome importato in questo codice deve corrispondere al valore nelle proprietà del progetto in Proprietà>di configurazione Nome destinazione generale.>

    Nell'esempio seguente il "superfastcode" nome indica che è possibile usare l'istruzione from superfastcode import fast_tanh in Python perché fast_tanh è definita all'interno superfastcode_methodsdi . I nomi di file interni al progetto C++, ad esempio module.cpp, sono inconequenti.

    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. Aggiungere un metodo che Python chiama quando carica il modulo. Il nome del metodo deve essere PyInit_<module-name>, dove< module-name> corrisponde esattamente alla proprietà Nome destinazione generale>del >progetto C++. Ovvero, il nome del metodo corrisponde al nome file del file con estensione pyd compilato dal progetto.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Compilare il progetto C++ e verificare il codice. Se si verificano errori, vedere la sezione "Risolvere gli errori di compilazione".

Usare PyBind11

Se si completano i passaggi della sezione precedente per il progetto superfastcode , è possibile notare che l'esercizio richiede codice boilerplate per creare le strutture del modulo per le estensioni CPython C++. In questo esercizio si scopre che PyBind11 semplifica il processo di codifica. Le macro vengono usate in un file di intestazione C++ per ottenere lo stesso risultato, ma con molto meno codice. Tuttavia, sono necessari passaggi aggiuntivi per assicurarsi che Visual Studio possa individuare le librerie PyBind11 e includere i file. Per altre informazioni sul codice in questa sezione, vedere Nozioni di base su PyBind11.

Installare PyBind11

Il primo passaggio consiste nell'installare PyBind11 nella configurazione del progetto. In questo esercizio si usa la finestra Developer PowerShell .

  1. Aprire la finestra strumenti>della riga di comando>per sviluppatori di PowerShell.

  2. Nella finestra Developer PowerShell installare PyBind11 usando il comando pip install pybind11 pip o py -m pip install pybind11.

    Visual Studio installa PyBind11 e i relativi pacchetti dipendenti.

Aggiungere percorsi PyBind11 al progetto

Dopo l'installazione di PyBind11, è necessario aggiungere i percorsi PyBind11 alla proprietà Directory di inclusione aggiuntive per il progetto.

  1. In Developer PowerShell la finestra eseguire il comando python -m pybind11 --includes o py -m pybind11 --includes.

    Questa azione stampa un elenco di percorsi PyBind11 che è necessario aggiungere alle proprietà del progetto.

  2. Evidenziare l'elenco dei percorsi nella finestra e selezionare Copia (pagina doppia) sulla barra degli strumenti della finestra.

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

    L'elenco dei percorsi concatenati viene aggiunto agli Appunti.

  3. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto superfastcode2 e scegliere Proprietà.

  4. Nella parte superiore della finestra di dialogo Pagine delle proprietà selezionare Rilascio per il campo Configurazione. Questa opzione potrebbe essere visualizzata con Prefisso attivo .

  5. Nella scheda C/C++>Generale della finestra di dialogo espandere il menu a discesa per la proprietà Directory di inclusione aggiuntive e selezionare Modifica.

  6. Nella finestra di dialogo popup aggiungere l'elenco dei percorsi copiati:

    Ripetere questi passaggi per ogni percorso nell'elenco concatenato copiato dalla finestra di PowerShell per sviluppatori:

    1. Selezionare Nuova riga (cartella con segno più) sulla barra degli strumenti della finestra di dialogo popup.

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

      Visual Studio aggiunge una riga vuota all'inizio dell'elenco dei percorsi e posiziona il cursore di inserimento all'inizio.

    2. Incollare il percorso PyBind11 nella riga vuota.

      È anche possibile selezionare Altre opzioni (...) e usare una finestra di dialogo di Esplora file popup per passare al percorso.

      Importante

      • Se il percorso contiene il -I prefisso, rimuovere il prefisso dal percorso.
      • Affinché Visual Studio riconosca un percorso, il percorso deve trovarsi in una riga separata.

      Dopo aver aggiunto un nuovo percorso, Visual Studio mostra il percorso confermato nel campo Valore valutato .

  7. Selezionare OK per uscire dalla finestra di dialogo popup.

  8. Nella parte superiore della finestra di dialogo Pagine delle proprietà passare il puntatore del mouse sul valore per la proprietà Directory di inclusione aggiuntive e verificare che siano presenti i percorsi PyBind11.

  9. Selezionare OK per applicare le modifiche alle proprietà.

Aggiornare il file module.cpp

L'ultimo passaggio consiste nell'aggiungere il file di intestazione PyBind11 e il codice macro al file C++ del progetto.

  1. Per il progetto C++ superfastcode2 , aprire il file module.cpp nell'editor di codice.

  2. Aggiungere un'istruzione all'inizio del file module.cpp per includere il file di intestazione pybind11.h :

    #include <pybind11/pybind11.h>
    
  3. Alla fine del file module.cpp aggiungere codice per la PYBIND11_MODULE macro per definire il punto di ingresso alla funzione 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. Compilare il progetto C++ e verificare il codice. Se si verificano errori, vedere la sezione successiva Risolvere gli errori di compilazione.

Risolvere gli errori di compilazione

Esaminare le sezioni seguenti per i possibili problemi che possono causare l'esito negativo della compilazione del modulo C++.

Errore: Impossibile individuare il file di intestazione

Visual Studio restituisce un messaggio di errore simile a E1696: Impossibile aprire il file open source "Python.h" o C1083: Impossibile aprire il file di inclusione: "Python.h": Nessun file o directory di questo tipo.

Questo errore indica che il conforme non è in grado di individuare un file di intestazione (h) obbligatorio per il progetto.

  • Per il progetto superfastcode, verificare che la proprietà del progetto C/C++>General>Additional Include Directories contenga il percorso della cartella di inclusione per l'installazione di Python. Esaminare i passaggi descritti in Configurare le proprietà del progetto.

  • Per il progetto superfastcode2, verificare che la stessa proprietà del progetto contenga il percorso della cartella di inclusione per l'installazione di PyBind11. Esaminare i passaggi per i percorsi di Ad PyBind da proiettare.

Per altre informazioni sull'accesso alle informazioni di configurazione dell'installazione di Python, vedere la documentazione di Python.

Errore: Impossibile individuare le librerie Python

Visual Studio restituisce un errore che indica che il conforme non è in grado di individuare i file della libreria (DLL) necessari per il progetto.

  • Per il progetto C++ (superfastcode o superfastcode2), verificare che la proprietà Directory aggiuntive della libreria generale>del linker>contenga il percorso della cartella libs per l'installazione di Python. Esaminare i passaggi descritti in Configurare le proprietà del progetto.

Per altre informazioni sull'accesso alle informazioni di configurazione dell'installazione di Python, vedere la documentazione di Python.

Visual Studio segnala errori del linker correlati alla configurazione dell'architettura di destinazione per il progetto, ad esempio x64 o Win32.

  • Per il progetto C++ (superfastcode o superfastcode2), modificare la configurazione di destinazione in modo che corrisponda all'installazione di Python. Ad esempio, se la configurazione di destinazione del progetto C++ è Win32, ma l'installazione di Python è a 64 bit, modificare la configurazione di destinazione del progetto C++ in x64.

Testare il codice e confrontare i risultati

Ora che la libreria è strutturata con le estensioni per Python, è possibile fare riferimento a tali estensioni dal progetto Python, importare il modulo e usare i metodi delle estensioni.

Rendere disponibile la DLL per Python

È possibile rendere disponibile la DLL per Python in diversi modi. Ecco due opzioni da considerare:

Se il progetto Python e il progetto C++ si trovano nella stessa soluzione, è possibile usare l'approccio seguente:

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo Riferimenti nel progetto Python e scegliere Aggiungi riferimento.

    Assicurarsi di eseguire questa azione per il progetto Python e non per il progetto C++.

  2. Nella finestra di dialogo Aggiungi riferimento espandere la scheda Progetti.

  3. Selezionare le caselle di controllo per i progetti superfastcode e superfastcode2 e selezionare OK.

    Screenshot that shows how to add a reference to the super fast code project in Visual Studio.

Un approccio alternativo consiste nell'installare il modulo di estensione C++ nell'ambiente Python. Questo metodo rende il modulo disponibile per altri progetti Python. Per altre informazioni, vedere la documentazione del progetto setuptools.

Completare i passaggi seguenti per installare il modulo di estensione C++ nell'ambiente Python:

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto C++ e scegliere Aggiungi>nuovo elemento.

  2. Nell'elenco dei modelli di file selezionare File C++ (.cpp).

  3. Immettere il nome del file come setup.py e quindi selezionare Aggiungi.

    Assicurarsi di immettere il nome del file con l'estensione Python (.py). Visual Studio riconosce il file come codice Python nonostante l'uso del modello di file C++.

    Visual Studio apre il nuovo file nell'editor di codice.

  4. Incollare il codice seguente nel nuovo file. Scegliere la versione del codice corrispondente al metodo di estensione:

    • Estensioni CPython (progetto superfastcode ):

      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 (progetto 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. Nel progetto C++ creare un secondo file denominato pyproject.toml e incollare il codice seguente:

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

    Il file TOML (con estensione toml) usa il formato del linguaggio ovvio e minimo di Tom per i file di configurazione.

  6. Per compilare l'estensione, fare clic con il pulsante destro del mouse sul nome file pyproject.toml nella scheda della finestra del codice e selezionare Copia percorso completo.

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

    Eliminare il nome pyproject.toml dal percorso prima di usarlo.

  7. In Esplora soluzioni espandere il nodo Ambienti Python per la soluzione.

  8. Fare clic con il pulsante destro del mouse sull'ambiente Python attivo (visualizzato in grassetto) e scegliere Gestisci pacchetti Python.

    Viene visualizzato il riquadro Ambienti Python.

    Se il pacchetto necessario è già installato, viene visualizzato in questo riquadro.

    • Prima di continuare, selezionare la X accanto al nome del pacchetto per disinstallarla.

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

  9. Nella casella di ricerca per il riquadro Ambienti Python incollare il percorso copiato ed eliminare il nome file pyproject.toml dalla fine del percorso.

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

  10. Selezionare Invio per installare il modulo dal percorso del percorso copiato.

    Suggerimento

    Se l'installazione non riesce a causa di un errore di autorizzazione, aggiungere l'argomento --user alla fine del comando e ritentare l'installazione.

Chiamare la DLL da Python

Dopo aver reso disponibile la DLL per Python, come descritto nella sezione precedente, è possibile chiamare le superfastcode.fast_tanh funzioni e superfastcode2.fast_tanh2 da Python. È quindi possibile confrontare le prestazioni della funzione con l'implementazione di Python.

Seguire questa procedura per chiamare la DLL del modulo di estensione da Python:

  1. Aprire il file .py per il progetto Python nell'editor di codice.

  2. Alla fine del file aggiungere il codice seguente per chiamare i metodi esportati dalle DLL e visualizzare il relativo output:

    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. Eseguire il programma Python selezionando Avvia debug>senza eseguire debug oppure usare il tasto di scelta rapida CTRL+F5.

    Nota

    Se il comando Avvia senza debug non è disponibile, in Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto Python e quindi scegliere Imposta come progetto di avvio.

    Quando il programma viene eseguito, si noti che le routine C++ vengono eseguite circa da 5 a 20 volte più velocemente rispetto all'implementazione di Python.

    Ecco un esempio di output tipico del programma:

    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. Provare ad aumentare la COUNT variabile in modo che le differenze temporali siano più pronunciate.

    Anche una build di debug del modulo C++ viene eseguita più lentamente rispetto a una build di rilascio perché la compilazione di debug è meno ottimizzata e contiene vari controlli degli errori. Provare a passare tra le configurazioni di compilazione per il confronto, ma ricordarsi di aggiornare le proprietà impostate in precedenza per la configurazione della versione.

Velocità e sovraccarico del processo di gestione

Nell'output si potrebbe notare che l'estensione PyBind11 non è veloce quanto l'estensione CPython, anche se dovrebbe essere più veloce rispetto all'implementazione di Python pura. Il motivo principale della differenza è dovuto all'uso del flag METH_O. Questo flag non supporta più parametri, nomi di parametri o argomenti di parole chiave. PyBind11 genera codice leggermente più complesso per fornire un'interfaccia più simile a Python ai chiamanti. Poiché il codice di test chiama la funzione 500.000 volte, i risultati possono amplificare notevolmente l'overhead.

È possibile ridurre ulteriormente il sovraccarico spostando il for ciclo nel codice Python nativo. Questo approccio prevede l'uso del protocollo iteratore (o del tipo PyBind11 py::iterable per il parametro della funzione) per elaborare ogni elemento. La rimozione delle transizioni ripetute tra Python e C++ è un modo efficace per ridurre il tempo necessario per elaborare la sequenza.

Risolvere gli errori di importazione

Se viene visualizzato un ImportError messaggio quando si tenta di importare il modulo, è possibile risolverlo in uno dei modi seguenti:

  • Quando si esegue la compilazione tramite un riferimento al progetto, assicurarsi che le proprietà del progetto C++ corrispondano all'ambiente Python attivato per il progetto Python. Verificare che le stesse posizioni delle cartelle siano in uso per i file Include (.h) e Library (DLL).

  • Verificare che il file di output sia denominato correttamente, ad esempio superfastcode.pyd. Un nome o un'estensione non corretti impedisce l'importazione del file necessario.

  • Se si installa il modulo usando il file setup.py , assicurarsi di eseguire il pip comando nell'ambiente Python attivato per il progetto Python. Quando si espande l'ambiente Python attivo per il progetto in Esplora soluzioni, verrà visualizzata una voce per il progetto C++, ad esempio superfastcode.

Eseguire il debug del codice C++

Visual Studio supporta il debug di codice Python e C++ insieme. I passaggi seguenti illustrano il processo di debug per il progetto C++ superfastcode , ma il processo è lo stesso per il progetto superfastcode2 .

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto Python e scegliere Proprietà.

  2. Nel riquadro Proprietà selezionare la scheda Debug e quindi selezionare l'opzione Debug>Abilita debug del codice nativo.

    Suggerimento

    Quando si abilita il debug del codice nativo, la finestra di output di Python potrebbe chiudersi immediatamente dopo il completamento del programma senza sospendere e visualizzare il tasto Press any key per continuare . Per forzare la sospensione e la richiesta dopo aver abilitato il debug del codice nativo, aggiungere l'argomento al campo Argomenti dell'interprete -idi esecuzione>nella scheda Debug. Questo argomento inserisce l'interprete Python in modalità interattiva dopo l'esecuzione del codice. Il programma attende di selezionare CTRL+Z+INVIO per chiudere la finestra. Un approccio alternativo consiste nell'aggiungere import os istruzioni e os.system("pause") alla fine del programma Python. Questo codice duplica la richiesta di sospensione originale.

  3. Selezionare Salva file>(o CTRL+S) per salvare le modifiche alle proprietà.

  4. Sulla barra degli strumenti di Visual Studio impostare La configurazione di compilazione su Debug.

  5. Poiché l'esecuzione del codice richiede in genere più tempo nel debugger, è possibile modificare la COUNT variabile nel progetto Python .py file in un valore di circa cinque volte inferiore al valore predefinito. Ad esempio, modificarlo da 500000 a 100000.

  6. Nel codice C++ impostare un punto di interruzione nella prima riga del tanh_impl metodo.

  7. Avviare il debugger selezionando Debug>Avvia debug o usare il tasto di scelta rapida F5.

    Il debugger si arresta quando viene chiamato il codice del punto di interruzione. Se il punto di interruzione non viene raggiunto, verificare che la configurazione sia impostata su Debug e che il progetto sia stato salvato, che non avviene automaticamente all'avvio del debugger.

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

  8. Nel punto di interruzione è possibile esaminare il codice C++, esaminare le variabili e così via. Per altre informazioni su queste funzionalità, vedere Eseguire il debug di Python e C++ insieme.

Approcci alternativi

È possibile creare estensioni Python in diversi modi, come descritto nella tabella seguente. Le prime due righe e CPythonPyBind11, sono descritte in questo articolo.

Approccio Vintage Utenti rappresentativi
Moduli di estensione C/C++ per CPython 1991 Libreria standard
PyBind11 (consigliato per C++) 2015
Cython (consigliato per C) 2007 gevent, kivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 cryptography, pypy
SWIG 1996 crfsuite
Boost.Python 2002
cppyy 2017