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.
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.
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
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
Erstellen und bearbeiten Sie prim.c
direkt unter dem Verzeichnis.
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.
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.
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");
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.
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.
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);
}
__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
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))
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.
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
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!
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.
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.
Recommended Posts