Introduction à l'écriture de Cython [Notes]

28/08/2017: La partie signalée dans le commentaire a été corrigée.

Aperçu

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 **

Qu'est-ce que Cython

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.

Comment installer

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/

Mac, ubuntu, etc.

Je pense que vous pouvez l'installer avec $ pip install cython.

Fichiers nécessaires pour envelopper C

Que faire cette fois

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.

Contenu écrit en langage C

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;
}

Comment écrire un fichier pyx

É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].

Comment écrire setup.py

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, passeznp.get_include () à ʻinclude_dirs.

compiler

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.

Essayez de courir avec Python

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.

Au fait

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.

à la fin

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

Introduction à l'écriture de Cython [Notes]
Introduction à la rédaction de notes d'apprentissage automatique
Introduction à MQTT (Introduction)
Introduction à Scrapy (1)
Introduction à Scrapy (3)
Premiers pas avec Supervisor
Introduction à Tkinter 1: Introduction
Introduction à PyQt
Introduction à Scrapy (2)
[Linux] Introduction à Linux
Introduction à Scrapy (4)
Introduction à discord.py (2)
Une introduction à Cython sans aller plus loin
Introduction à Cython sans approfondir -2-
Introduction à Lightning Pytorch
Premiers pas avec le Web Scraping
Introduction aux baies non paramétriques
Introduction à EV3 / MicroPython
Introduction au langage Python
Introduction à la reconnaissance d'image TensorFlow
Introduction à OpenCV (python) - (2)
Introduction à PyQt4 Partie 1
Introduction à l'injection de dépendances
Introduction à Private Chainer
Introduction à l'apprentissage automatique
AOJ Introduction à la programmation Sujet 1, Sujet 2, Sujet 3, Sujet 4
Introduction au module de papier électronique
Introduction à l'algorithme de recherche de dictionnaire
[Mémorandum d'apprentissage] Introduction à vim
Introduction à PyTorch (1) Différenciation automatique
opencv-python Introduction au traitement d'image
Introduction à Python Django (2) Win
Introduction à Private TensorFlow
Une introduction à l'apprentissage automatique
[Introduction à cx_Oracle] Présentation de cx_Oracle
Une super introduction à Linux
AOJ Introduction à la programmation Sujet n ° 7, Sujet n ° 8
Notes Python à oublier bientôt
Cython arrive sur AtCoder !!
Introduction à la détection des anomalies 1 principes de base
Introduction à RDB avec sqlalchemy Ⅰ
[Introduction au système] Retracement de Fibonacci ♬
Introduction à l'optimisation non linéaire (I)
Introduction à la communication série [Python]
AOJ Introduction à la programmation Sujet n ° 5, Sujet n ° 6
Introduction au Deep Learning ~ Règles d'apprentissage ~
[Introduction à Python] <liste> [modifier le 22/02/2020]
Introduction à Python (version Python APG4b)
Une introduction à la programmation Python
[Introduction à cx_Oracle] (8e) version de cx_Oracle 8.0
Introduction à discord.py (3) Utilisation de la voix
Introduction à l'optimisation bayésienne
Apprentissage par renforcement profond 1 Introduction au renforcement de l'apprentissage
Super introduction à l'apprentissage automatique
Introduction à Ansible Part «Inventaire»
Série: Introduction à cx_Oracle Contents
[Introduction] Comment utiliser open3d