28/08/2017: La partie signalée dans le commentaire a été corrigée.
Récemment, j'ai essayé d'augmenter la vitesse en implémentant un traitement lent en Python en C en utilisant Cython, donc je vais le résumer pour que je puisse regarder en arrière quand j'oublie comment le faire. ** Seules les choses super rudimentaires et superficielles sont écrites **
Cython reconnaît que c'est comme la médiation d'une fonction écrite en C afin qu'elle puisse être appelée à partir de Python. En d'autres termes, c'est une image que vous pouvez utiliser chaque bon point en implémentant la partie d'exécution lente en C et en dessinant facilement l'autre partie en Python.
windows
Allez sur ce site et téléchargez la version correspondante. (Si la partie de cp ○○ est Python 3.6, sélectionnez celle qui est 36)
À l'invite de commande, entrez pip install le chemin du fichier téléchargé
pour démarrer l'installation.
Si vous obtenez une erreur telle que ʻerror: Impossible de trouver vcvarsall.bat` à l'avenir, accédez au site de référence suivant [[Paramètres pour utiliser une autre version de VC ++]](https://www.regentechlog.com/2014/04/13 Il a été résolu en faisant référence à / build-python-package-on-windows / # VC-2)
Site de référence https://www.regentechlog.com/2014/04/13/build-python-package-on-windows/
Je pense que vous pouvez l'installer avec $ pip install cython
.
Afin de se rendre compte que c'est plus rapide, nous utiliserons cette fois le code suivant avec une boucle double for.
python_code.py
# -*-encode: utf-8-*-
import time
if __name__ == "__main__":
start_t = time.time()
arr_a = [i for i in range(1000)]
arr_b = [i for i in range(1000)]
res = 0
for elem_a in arr_a:
for elem_b in arr_b:
res = res + elem_a + elem_b
print(res)
all_time = time.time() - start_t
print("Execution time:{0} [sec]".format(all_time))
Tout ce que nous faisons est de trouver la somme des sommes de toutes les combinaisons des séquences ʻarr_a et ʻarr_b
.
Quand tu fais ça,
out[1]
999000000
Execution time:0.24517321586608887 [sec]
Il devient.
Python semble prendre beaucoup de temps pour la boucle, donc
for elem_a in arr_a:
for elem_b in arr_b:
res = res + elem_a + elem_b
Cette partie est implémentée en langage C. Le fichier d'en-tête et le code source sont les suivants.
cython_c_code.h
#ifndef CYTHON_CODE
#define CYTHON_CODE
int c_algo(int*, int*, int, int);
#endif
cython_c_code.c
#include "cython_c_code.h"
int c_algo(int *arr_a, int *arr_b, int size_a, int size_b){
int res = 0;
for(int i=0; i < size_a; i++){
for(int j=0; j < size_b; j++){
res = res + arr_a[i]+arr_b[j];
}
}
return res;
}
Écrivez la fonction C ci-dessus c_algo ()
dans .pyx afin qu'elle puisse être appelée en Python.
cython_pyx_code.pyx
cimport numpy as np
cdef extern from "cython_c_code.h":
int c_algo(int *arr_a, int *arr_b, int size_a, int size_b)
def cy_algo(np.ndarray[int, ndim=1] arr_a, np.ndarray[int, ndim=1] arr_b):
return c_algo(&arr_a[0], &arr_b[0], len(arr_a), len(arr_b))
Le cimport
qui apparaît ici est une instruction pour lire la version cython du fichier d'en-tête.
En d'autres termes, en écrivant python cimport numpy as np
, il est possible de déclarer des types tels que np.ndarray
.
En dehors de cela, si vous avez également besoin d'une fonction numpy, vous avez également besoin de ʻimport numpy`.
Ensuite, décrivez la fonction à utiliser à partir du fichier d'en-tête C.
cdef extern du nom du fichier d'en-tête:
Fonction à utiliser
Décrivez de cette manière.
Enfin, décrivez la fonction à appeler du côté Python.
Il est décrit dans def
comme Python, mais le type est spécifié dans la partie argument.
Le type lors de la réception d'un tableau numpy comme argument est np.ndarray [type d'élément, ndim = nombre de dimensions]
.
De plus, lorsque vous passez un pointeur de tableau à une fonction C, il peut être passé avec & nom de tableau [0]
.
setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize
import numpy as np
sourcefiles = ['cython_pyx_code.pyx','cython_c_code.c']
setup(
cmdclass = {'build_ext': build_ext},
ext_modules = [Extension("cython_code", sourcefiles, include_dirs=[np.get_include()])],
)
Importez la bibliothèque requise et écrivez ce qui suit dans l'argument ʻext_modules de
setup () .
[Extension (" nom de cette bibliothèque ", [fichiers source à utiliser], include_dirs = [en-tête de bibliothèque à utiliser])] Puisque nous utilisons numpy cette fois, passez
np.get_include () à ʻinclude_dirs
.
Compilez avec $ python setup.py build_ext -i
.
Si tout se passe bien, vous aurez un répertoire de construction et un nom de bibliothèque.so
( .pyd
pour Windows). Vous pouvez l'importer et l'utiliser comme une bibliothèque Python normale.
cython_py_code.py
# -*-encode: utf-8-*-
import time
import numpy as np
import cython_code
if __name__ == "__main__":
start_t = time.time()
arr_a = [i for i in range(1000)]
arr_b = [i for i in range(1000)]
res = cython_code.cy_algo(np.array(arr_a), np.array(arr_b))
print(res)
all_time = time.time() - start_t
print("Execution time:{0} [sec]".format(all_time))
out[2]
999000000
Execution time:0.0010039806365966797 [sec]
La vitesse s'est améliorée d'environ 245 fois.
Vous pouvez également écrire le code directement dans le fichier .pyx en spécifiant le type.
cy_only.pyx
cimport numpy as np
def cy_algo(np.ndarray[int, ndim=1] arr_a, np.ndarray[int, ndim=1] arr_b):
cdef int res
cdef int elem_a
cdef int elem_b
res = 0
for elem_a in arr_a:
for elem_b in arr_b:
res = res + elem_a +elem_b
return res
Toutes les variables utilisées de cette manière sont définies par nom de variable de nom de type cdef
.
Notez que si vous oubliez de définir ʻelem_a et elem_b` dans un premier temps, la vitesse d'exécution ralentira.
Si vous compilez ceci et appelez-le depuis Python,
out[3]
999000000
Execution time:0.10053086280822754 [sec]
C'est environ deux fois plus rapide, mais cela semble être plus lent que d'écrire en C car il est facile d'écrire comme Python.
Comme cela a été signalé dans le commentaire, j'ai modifié le code comme suit.
cy_only.pyx
cimport numpy as np
def cy_algo(np.ndarray[int, ndim=1] arr_a, np.ndarray[int, ndim=1] arr_b):
cdef int res = 0
cdef size_t len_a = len(arr_a)
cdef size_t len_b = len(arr_b)
for i in range(len_a):
for j in range(len_b):
res = res + arr_a[i] +arr_b[j]
return res
Puis,
out[4]
999000000
Execution time:0.0019919872283935547 [sec]
La vitesse était proche de celle écrite en C. Cython Il s'est avéré que même une écriture solide serait plus rapide si elle était correctement écrite.
Je commence tout juste à toucher Cython et je ne comprends que le niveau d'introduction, donc j'ai peut-être écrit quelque chose d'approprié. Je vous serais reconnaissant si vous pouviez indiquer dans ce cas.
Il semble que la vitesse soit encore améliorée en désactivant les options inutiles dans setup.py
.
Une liste peut être trouvée à ici.
Si vous pouvez le comprendre un peu plus, je peux réécrire cet article pour le rendre plus facile à lire.
Recommended Posts