C-API in Python 3

Warum Imasara API?

Ich denke, es gibt viele Möglichkeiten, Python zu beschleunigen.

Es kann andere Gründe geben, aber den Grund, C zu einer API zu machen.

Insbesondere für das letzte Element haben sich die neuesten Setup-Tools weiterentwickelt und es ist einfacher geworden, sie zu erstellen und zu drehen. Daher scheint es Raum zu geben, die API zu überdenken.

Ich habe das Gefühl, dass in Nischenbereichen Nachfrage besteht, beispielsweise wenn eine Person wie ich, die überhaupt kein C schreiben kann, ein kleines C-Programm eines Experten effektiv nutzen möchte.

Gegenstand

Verwenden wir als Beispiel einen Algorithmus namens [Eratostenes Sieve](https://ja.wikipedia.org/wiki/Eratostenes Sieve). Ich kenne die Details nicht, beziehen Sie sich daher bitte auf das Linkziel und suchen Sie mit einem der Algorithmen zur Beurteilung der Primzahl nach Primzahlen, die kleiner oder gleich der angegebenen Ganzzahl sind. Wenn die angegebene Ganzzahl beispielsweise 10 ist, wird [2, 3, 5, 7] gesucht.

Für den C-Code habe ich auf [hier] verwiesen (http://d.hatena.ne.jp/mizchi/20100802/1280705734#20100802f1).

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

#define MAX 1000000

double gettimeofday_sec()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + (double)tv.tv_usec*1e-6;
}


double prim(int n)
{
  double start,end;
  start = gettimeofday_sec();

  int i, j;
  int p[n];

  for(i=0 ; i<n ; i++) p[i] = 0;
  p[0] = 1;
  
  for(i=2 ; i<=n/2 ; i++){
    for(j=2 ; i*j<=n ; j++){
      if(p[i*j-1] == 0)
        p[i*j-1] = 1;
    }
  }
  
  end = gettimeofday_sec();
  return end - start;
}

Da es zu viel ist, alle Suchergebnisse auszugeben, habe ich es zu einer Funktion gemacht, die die Verarbeitungszeit zurückgibt.

Vorbereitung

virtualenv Es wird dringend empfohlen, im Voraus eine virtuelle Umgebung zu erstellen. Dieses Mal gehen wir davon aus, dass "virtualenvwrapper" installiert ist. Der Name der virtuellen Umgebung lautet "api_test". Geben Sie die Version von Python, die Sie erstellen möchten, im Schlüsselwortargument --python = von mkvirtualenv an. Dies hat große Auswirkungen auf das später beschriebene Erstellen des Pakets (Erstellen des Rads). Geben Sie daher den Python an, an den das Paket verteilt wird. In meinem Fall lautet der Pfad "/ usr / bin / python3.5", aber passen Sie ihn bitte an Ihre eigene Umgebung an.

mkvirtualenv api_test --python=/usr/bin/python3.5

Verzeichnisaufbau

Erstellen Sie ein beliebiges Verzeichnis, und die endgültige Konfiguration lautet wie folgt.

.
├── api_sample
│   ├── __init__.py
│   └── py
│       └── __init__.py
├── prim.c
└── setup.py

Wenn Sie mit dem Erstellen von Dateien und Verzeichnissen nicht vertraut sind, können Sie auch aus meinem Github-Repository klonen.

git clone https://github.com/drillan/python3_c_api_sample.git

Konvertieren Sie C-Code in die Python-API

Erstellen und bearbeiten Sie prim.c direkt unter dem Verzeichnis.

Header

Integrieren Sie die Python-API.

#include <Python.h>

Alle vom Benutzer sichtbaren Symbole, die in "Python.h" definiert sind, scheinen das Präfix "Py" oder "PY" zu haben. Einige Systeme haben Präprozessordefinitionen, die sich auf die Definition von Standardheadern auswirken. Daher muss Python.h anscheinend vor jedem Standardheader eingefügt werden.

C-Funktionen in Python-Objekte verwandeln

Dieses Mal werden wir die Funktion prim () des obigen C-Beispielcodes als Python-Modul behandeln.

static PyObject *
prim(PyObject *self, PyObject *args)
{
  int n;
  if(!PyArg_ParseTuple(args, "i", &n))
    return NULL;
  
  double start,end;
  start = gettimeofday_sec();
  
  int i, j;
  int p[n];

  for(i=0 ; i<n ; i++) p[i] = 0;
  p[0] = 1;
  
  for(i=2 ; i<=n/2 ; i++){
    for(j=2 ; i*j<=n ; j++){
      if(p[i*j-1] == 0)
        p[i*j-1] = 1;
    }
  }
  
  end = gettimeofday_sec();
  return Py_BuildValue("d", end - start);
}

Das Argument "self" wird dem Modul übergeben, wenn es sich um eine Funktion auf Modulebene handelt, und der Methode wird eine Objektinstanz übergeben. Das Argument "args" wird zu einem Zeiger auf das Python-Taple-Objekt, das das Argument enthält. Jedes Element im Taple entspricht jedem Argument in der Argumentliste zum Zeitpunkt des Aufrufs. Diese Art der Behandlung ist erforderlich, da die Argumente von Python angegeben werden. Ich verwende auch "PyArg_ParseTuple ()", weil das angegebene Argument in Typ C konvertiert werden muss. Das zweite Argument, "i", bezieht sich auf den int-Typ, "d" ist der Doppeltyp und "s" ist der char-Typ. Schließlich kehrt Py_BuildValue () zu dem Typ zurück, der von Python wieder empfangen werden kann.

Auf diese Weise ist es zum Konvertieren einer C-Funktion in eine Python-API erforderlich, den Ablauf von Python-> C-> Python zu kennen.

Registrierung der Methodentabelle

Registrieren Sie die oben erstellte Funktion prim () in der Methodentabelle, damit sie als Python-Methode aufgerufen werden kann.

static PyMethodDef methods[] = {
	{"prim", prim, METH_VARARGS},
	{NULL, NULL}
};

Die drei Einträge sind wiederum der Methodenname, ein Zeiger auf die C-Implementierung und ein Flag-Bit, das angibt, wie der Aufruf ausgeführt werden soll. METH_VARARGS ist eine aufrufende Konvention, die normalerweise in Methoden vom Typ PyCFunction verwendet wird, bei denen Argumente für die Funktion im Tupelformat angegeben werden. Wenn Sie Schlüsselwortargumente angeben möchten, geben Sie "METH_KEYWORDS" an.

docstring Normalerweise scheint es mit "PyDoc_STRVAR ()" generiert zu werden. Es kann als Dokumentzeichenfolge registriert werden, wenn ein später beschriebenes Modul registriert wird.

PyDoc_STRVAR(api_doc, "Python3 API sample.\n");

Modulregistrierung

Registrieren Sie den Modulnamen usw., damit er aus Python importiert werden kann. Geben Sie den Modulnamen, die Dokumentzeichenfolge, den Modulspeicherbereich und die Methode an.

static struct PyModuleDef cmodule = {
   PyModuleDef_HEAD_INIT,
   "c",   /* name of module */
   api_doc, /* module documentation, may be NULL */
   -1,       /* size of per-interpreter state of the module,
                or -1 if the module keeps state in global variables. */
   methods
};

Diesmal lautet der Modulname "c". Die Angabe des dritten "-1" scheint zu bedeuten, dass das Modul aufgrund seines globalen Status keine Subinterpreter unterstützt. Ich kenne mich selbst nicht. Wenn Sie mehr wissen möchten, lesen Sie bitte PEP 3121.

Initialisierungsfunktion

Die Funktion PyInit_c () wird aufgerufen, wenn das Modul c importiert wird. Die Funktion PyModule_Create () erstellt das oben definierte Modul und gibt es an die Initialisierungsfunktion zurück. (So etwas wie "__init __. Py"?) Beachten Sie, dass die Funktion Pyinit_name () je nach Modulname unterschiedliche Namen hat.

Endgültiger prim.c Inhalt

prim.c


#include <Python.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>

#define MAX 1000000

double gettimeofday_sec()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + (double)tv.tv_usec*1e-6;
}

static PyObject *
prim(PyObject *self, PyObject *args)
{
  int n;
  if(!PyArg_ParseTuple(args, "i", &n))
    return NULL;
  
  double start,end;
  start = gettimeofday_sec();
  
  int i, j;
  int p[n];

  for(i=0 ; i<n ; i++) p[i] = 0;
  p[0] = 1;
  
  for(i=2 ; i<=n/2 ; i++){
    for(j=2 ; i*j<=n ; j++){
      if(p[i*j-1] == 0)
        p[i*j-1] = 1;
    }
  }
  
  end = gettimeofday_sec();
  return Py_BuildValue("d", end - start);
}

static PyMethodDef methods[] = {
	{"prim", prim, METH_VARARGS},
	{NULL, NULL}
};

PyDoc_STRVAR(api_doc, "Python3 API sample.\n");

static struct PyModuleDef cmodule = {
   PyModuleDef_HEAD_INIT,
   "c",   /* name of module */
   api_doc, /* module documentation, may be NULL */
   -1,       /* size of per-interpreter state of the module,
                or -1 if the module keeps state in global variables. */
   methods
};

PyInit_c(void)
{
	return PyModule_Create(&cmodule);
}

Registriert in __init __. Py

Sie können es so erstellen, wie es ist, und es importieren. Beachten Sie jedoch das Paket und registrieren Sie es in `api_sample / __ init __. Py '.

api_sample/__init__.py


import c

Bonus, Python-Code schreiben

Da es eine große Sache ist, schreibe ich den gleichen Code in Python und vergleiche die Geschwindigkeiten. Schreiben und speichern Sie den Code, der "prim.c" entspricht, in "api_sample / py / __ init __. Py". Bitte beachten Sie, dass der Dateiname derselbe wie oben ist, die Hierarchie jedoch unterschiedlich ist.

api_sample/py/__init__.py


import time

MaxNum = 1000000


def prim(n):
    start = time.time()
    prime_box = [0 for i in range(n)]
    prime_box[0], prime_box[1] = 1, 1
    for i in range(n)[2:]:
        j = 1
        while i * (j + 1) < n:
            prime_box[i * (j + 1)] = 1
            j += 1
    end = time.time()
    return end - start

if __name__ == '__main__':
    print(prim(MaxNum))

Bauen

Es ist endlich ein Build, aber es scheint, dass setuptools es nur durch Schreiben in setup.py erstellen wird. Es ist eine günstige Zeit. Also erstelle setup.py.

setup.py


from setuptools import setup, Extension

c = Extension('c', sources=['prim.c'])
setup(name="api_sample", version="0.0.0",
      description="Python3 API Sample",
      packages=['api_sample'], ext_modules=[c])

Ist es der Punkt, an dem es als Erweiterung von Python erstellt wird, indem der Speicherort des C-Quellcodes mit "setuptools.Extension ()" angegeben wird? Durch Angabe des oben im Schlüsselwortargument `` ext_modules von setuptools.setup () `angegebenen Modulnamens kann es als Python-Modul aufgerufen werden.

python setup.py build

Wenn Sie die oben genannten Schritte ausführen, werden die erforderlichen Module erstellt und im Verzeichnis "build" gespeichert.

Installation

Lassen Sie es uns sofort installieren und verwenden.

python setup.py install
pip freeze

Wenn Sie es problemlos installieren können, lautet die Ausgabe wie folgt.

api-sample==0.0.0

Lauf

Beginnen wir mit dem Python-Code.

python -c "from api_sample import py; print(py.prim(1000000))"

Es ging darum in meiner Umgebung

5.6855926513671875

Dann der C-Code.

python -c "from api_sample import c; print(c.prim(1000000))"
0.0776968002319336

Es ist viel schneller!

Verteilung

Erstellen Sie ein Rad.

python setup.py bdist_wheel

In meiner Umgebung wurde eine Datei mit dem Namen "api_sample-0.0.0-cp35-cp35m-linux_x86_64.whl" erstellt.

Durch Ausführen der obigen Schritte wird eine Raddatei im Verzeichnis "dist" entsprechend der Ausführungsumgebung erstellt. Dies ist der Hauptgrund, warum wir virtualenv empfehlen, und es ist sehr bequem zu verteilen, da durch Umschalten der virtuellen Umgebung und Neuerstellen des Rads ein für die Umgebung geeignetes Paket erstellt wird.

Windows-Umgebung

Die Windows-Umgebung ist häufig der Engpass für Pakete, die erstellt werden müssen. Normalerweise ist Visual Studio erforderlich, aber Sie können es erstellen, indem Sie Visual C ++ Build Tools installieren. Durch Ausführen von python setup.py bdist_wheel mit dieser Installation wird das Rad für Windows erstellt und kann an Benutzer verteilt werden, die keine Build-Umgebung haben. Durch die Installation von 32-Bit- und 64-Bit-Python und die Erstellung in jeder virtuellen Umgebung können Sie außerdem ein Rad erstellen, das sowohl 32-Bit als auch 64-Bit unterstützt.

Wenn Sie das Rad für jede Plattform in PyPI registrieren, können Benutzer es einfach mit "pip install" installieren.

Referenzlink

Recommended Posts

C-API in Python 3
Evernote-API in Python
Weiter Python in C-Sprache
Erweitern Sie Python in C ++ (Boost.NumPy)
Hit Mastodons API in Python
Binäre Suche in Python / C ++
Blender Python API in Houdini (Python 3)
Abrufen der arXiv-API in Python
Erstellen Sie Awaitable mit der Python / C-API
Klicken Sie in Python auf die Sesami-API
ABC166 in Python A ~ C Problem
Erstellen Sie Google Mail in Python ohne Verwendung der API
Löse ABC036 A ~ C mit Python
Klicken Sie auf die Web-API in Python
So verpacken Sie C in Python
Implementieren Sie die REST-API schnell in Python
Löse ABC037 A ~ C mit Python
Greifen Sie mit Python auf die Twitter-API zu
Schreiben Sie einen C-Sprach-Unit-Test in Python
Python in der Optimierung
CURL in Python
Metaprogrammierung mit Python
Python 3.3 mit Anaconda
Geokodierung in Python
Metaanalyse in Python
Unittest in Python
Epoche in Python
Zwietracht in Python
Deutsch in Python
DCI in Python
Quicksort in Python
nCr in Python
N-Gramm in Python
Programmieren mit Python
Python C ++ Notizen
Plink in Python
Konstante in Python
Python, openFrameworks (c ++)
FizzBuzz in Python
Schritt AIC in Python
CSV in Python
Reverse Assembler mit Python
Reflexion in Python
Konstante in Python
nCr in Python.
Format in Python
Scons in Python 3
Puyopuyo in Python
Python in Virtualenv
PPAP in Python
Quad-Tree in Python
Reflexion in Python
Chemie mit Python
Hashbar in Python
DirectLiNGAM in Python
LiNGAM in Python
In Python reduzieren
In Python flach drücken
Mausbedienung mit Windows-API in Python