À mon avis, la communauté python est divisée en trois écoles, à savoir l'organisation python 2.x, l'organisation 3.x et l'organisation PyPy. Cette classification se résume essentiellement à la compatibilité et à la vitesse des bibliothèques. Cet article se concentrera sur certaines techniques courantes d'optimisation du code et sur l'amélioration significative des performances après la compilation en C. Bien sûr, je donnerai également la durée d'exécution des trois principaux genres Python. Mon objectif n'est pas de prouver que l'un est meilleur que l'autre, juste de vous donner une idée de comment comparer en utilisant ces exemples spécifiques dans différents contextes.
Utilisation du générateur
Une optimisation de la mémoire souvent négligée est l'utilisation de générateurs. Les générateurs nous permettent de créer une fonction qui renvoie un seul enregistrement à la fois au lieu de tous les enregistrements à la fois. Si vous utilisez python2.x, c'est pourquoi vous utilisez xrange au lieu de range ou ifilter au lieu de filter. Un bon exemple consiste à créer une grande liste et à les assembler.
import timeit import random def generate(num): while num: yield random.randrange(10) num -= 1 def create_list(num): numbers = [] while num: numbers.append(random.randrange(10)) num -= 1 return numbers print(timeit.timeit("sum(generate(999))", setup="from __main__ import generate", number=1000)) >>> 0.88098192215 #Python 2.7 >>> 1.416813850402832 #Python 3.2 print(timeit.timeit("sum(create_list(999))", setup="from __main__ import create_list", number=1000)) >>> 0.924163103104 #Python 2.7 >>> 1.5026731491088867 #Python 3.2
Non seulement c'est un peu plus rapide, mais cela vous évite également de stocker toute la liste en mémoire
! Introduction aux Ctypes
Pour les codes de performances clés, python lui-même nous fournit également une API pour appeler des méthodes C, principalement via des ctypes. Vous pouvez utiliser des ctypes sans écrire de code C. Par défaut, python fournit une bibliothèque c standard précompilée. Revenons à l'exemple du générateur et voyons combien de temps il faut pour l'implémenter à l'aide de ctypes.
import timeit from ctypes import cdll def generate_c(num): #Load standard C library libc = cdll.LoadLibrary("libc.so.6") #Linux #libc = cdll.msvcrt #Windows while num: yield libc.rand() % 10 num -= 1 print(timeit.timeit("sum(generate_c(999))", setup="from __main__ import generate_c", number=1000)) >>> 0.434374809265 #Python 2.7 >>> 0.7084300518035889 #Python 3.2
Je viens de le remplacer par une fonction aléatoire de c, et le temps d'exécution a été réduit de plus de moitié ! Maintenant, si je vous dis que nous pouvons faire mieux, le croiriez-vous ?
Introduction à Cython
Cython est un sur-ensemble de Python qui nous permet d'appeler des fonctions C et de déclarer des variables pour améliorer les performances. Avant d'essayer de l'utiliser, nous devons installer Cython.
sudo pip install cython
Cython est essentiellement un fork d'une autre bibliothèque de type Pyrex qui n'est plus en développement. Il compile notre code de type Python dans une bibliothèque C que nous pouvons appeler dans un fichier python. Utilisez le suffixe .pyx au lieu du suffixe .py pour vos fichiers Python. Voyons comment exécuter notre code générateur à l'aide de Cython.
#cython_generator.pyx import random def generate(num): while num: yield random.randrange(10) num -= 1
Nous devons créer un setup.py pour que Cython puisse compiler notre fonction.
from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext setup( cmdclass = {'build_ext': build_ext}, ext_modules = [Extension("generator", ["cython_generator.pyx"])] )
Compiler en utilisant :
python setup.py build_ext --inplace
Vous devriez pouvoir voir deux fichiers : le fichier cython_generator.c et le fichier generator.so. Nous utilisons la méthode suivante pour tester notre programme :
import timeit print(timeit.timeit("sum(generator.generate(999))", setup="import generator", number=1000)) >>> 0.835658073425
. Pas mal, voyons s'il y a quelque chose que nous pouvons améliorer. Nous pouvons d'abord déclarer "num" comme un entier, puis importer la bibliothèque C standard pour être responsable de notre fonction aléatoire.
#cython_generator.pyx cdef extern from "stdlib.h": int c_libc_rand "rand"() def generate(int num): while num: yield c_libc_rand() % 10 num -= 1
Si nous compilons et réexécutons, nous verrons ce nombre incroyable.
>>> 0.033586025238
Quelques changements seulement ont donné des résultats décents. Cependant, ce changement est parfois fastidieux, voyons donc comment le faire en utilisant Python standard.
Introduction à PyPy PyPy est un compilateur juste à temps pour Python 2.7.3 En termes simples, cela signifie rendre votre code plus rapide. Quora utilise PyPy en production. PyPy propose des instructions d'installation sur sa page de téléchargement, mais si vous utilisez Ubuntu, vous pouvez l'installer via apt-get. La façon dont cela fonctionne est prête à l'emploi, donc pas de bash fou ni d'exécution de scripts, il suffit de télécharger et d'exécuter. Voyons comment notre code générateur d'origine fonctionne sous PyPy.
import timeit import random def generate(num): while num: yield random.randrange(10) num -= 1 def create_list(num): numbers = [] while num: numbers.append(random.randrange(10)) num -= 1 return numbers print(timeit.timeit("sum(generate(999))", setup="from __main__ import generate", number=1000)) >>> 0.115154981613 #PyPy 1.9 >>> 0.118431091309 #PyPy 2.0b1 print(timeit.timeit("sum(create_list(999))", setup="from __main__ import create_list", number=1000)) >>> 0.140175104141 #PyPy 1.9 >>> 0.140514850616 #PyPy 2.0b1
Ouah! Sans modifier une seule ligne de code, la vitesse d'exécution est 8 fois plus rapide que l'implémentation Python pure.
Des tests plus approfondis Pourquoi des recherches plus approfondies ? PyPy est le champion ! Pas tout à fait vrai. Bien que la plupart des programmes puissent fonctionner sur PyPy, certaines bibliothèques ne sont pas entièrement prises en charge. De plus, il est plus facile d’écrire des extensions C pour votre projet que de changer de compilateur. Creusons un peu plus et voyons comment les ctypes nous permettent d'écrire des bibliothèques en C. Testons la vitesse du tri par fusion et du calcul de la séquence de Fibonacci. Voici le code C (functions.c) que nous utiliserons :
/* functions.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* http://rosettacode.org/wiki/Sorting_algorithms/Merge_sort#C */ inline void merge (int *left, int l_len, int *right, int r_len, int *out) { int i, j, k; for (i = j = k = 0; i < l_len && j < r_len;) out[k++] = left[i] < right[j] ? left[i++] : right[j++]; while (i < l_len) out[k++] = left[i++]; while (j < r_len) out[k++] = right[j++]; } /* inner recursion of merge sort */ void recur (int *buf, int *tmp, int len) { int l = len / 2; if (len <= 1) return; /* note that buf and tmp are swapped */ recur (tmp, buf, l); recur (tmp + l, buf + l, len - l); merge (tmp, l, tmp + l, len - l, buf); } /* preparation work before recursion */ void merge_sort (int *buf, int len) { /* call alloc, copy and free only once */ int *tmp = malloc (sizeof (int) * len); memcpy (tmp, buf, sizeof (int) * len); recur (buf, tmp, len); free (tmp); } int fibRec (int n) { if (n < 2) return n; else return fibRec (n - 1) + fibRec (n - 2); }
Sur la plateforme Linux, nous pouvons le compiler dans une bibliothèque partagée en utilisant la méthode suivante :
gcc -Wall -fPIC -c functions.c gcc -shared -o libfunctions.so functions.o
En utilisant les ctypes, vous pouvez utiliser cette bibliothèque en chargeant la bibliothèque partagée "libfunctions.so", tout comme nous l'avons fait avec la bibliothèque C standard plus tôt. Ici, nous allons comparer l'implémentation Python et l'implémentation C. Commençons maintenant à calculer la suite de Fibonacci :
# functions.py from ctypes import * import time libfunctions = cdll.LoadLibrary("./libfunctions.so") def fibRec(n): if n < 2: return n else: return fibRec(n-1) + fibRec(n-2) start = time.time() fibRec(32) finish = time.time() print("Python: " + str(finish - start)) # C Fibonacci start = time.time() x = libfunctions.fibRec(32) finish = time.time() print("C: " + str(finish - start))
Comme nous nous y attendions, C est plus rapide que Python et PyPy. Nous pouvons également comparer les tris par fusion de la même manière.
Nous n'avons pas encore exploré la bibliothèque Cypes, donc ces exemples ne reflètent pas le côté puissant de Python. La bibliothèque Cypes n'a que quelques restrictions de type standard, telles que int, char array, float, bytes, etc. Par défaut, il n'y a pas de tableau d'entiers, cependant en multipliant par c_int (ctype est de type int) on peut obtenir un tel tableau indirectement. C’est également ce que montre la ligne 7 du code. Nous avons créé un tableau c_int, un tableau de nos nombres et les avons emballés dans un tableau c_int
主要的是c语言不能这样做,而且你也不想。我们用指针来修改函数体。为了通过我们的c_numbers的数列,我们必须通过引用传递merge_sort功能。运行merge_sort后,我们利用c_numbers数组进行排序,我已经把下面的代码加到我的functions.py文件中了。
#Python Merge Sort from random import shuffle, sample #Generate 9999 random numbers between 0 and 100000 numbers = sample(range(100000), 9999) shuffle(numbers) c_numbers = (c_int * len(numbers))(*numbers) from heapq import merge def merge_sort(m): if len(m) <= 1: return m middle = len(m) // 2 left = m[:middle] right = m[middle:] left = merge_sort(left) right = merge_sort(right) return list(merge(left, right)) start = time.time() numbers = merge_sort(numbers) finish = time.time() print("Python: " + str(finish - start)) #C Merge Sort start = time.time() libfunctions.merge_sort(byref(c_numbers), len(numbers)) finish = time.time() print("C: " + str(finish - start))
Python: 0.190635919571 #Python 2.7 Python: 0.11785483360290527 #Python 3.2 Python: 0.266992092133 #PyPy 1.9 Python: 0.265724897385 #PyPy 2.0b1 C: 0.00201296806335 #Python 2.7 + ctypes C: 0.0019741058349609375 #Python 3.2 + ctypes C: 0.0029308795929 #PyPy 1.9 + ctypes C: 0.00287103652954 #PyPy 2.0b1 + ctypes
这儿通过表格和图标来比较不同的结果。
Merge Sort | Fibonacci | |
---|---|---|
Python 2.7 | 0.191 | 1.187 |
Python 2.7 + ctypes | 0.002 | 0.044 |
Python 3.2 | 0.118 | 1.272 |
Python 3.2 + ctypes | 0.002 | 0.046 |
PyPy 1.9 | 0.267 | 0.564 |
PyPy 1.9 + ctypes | 0.003 | 0.048 |
PyPy 2.0b1 | 0.266 | 0.567 |
PyPy 2.0b1 + ctypes | 0.003 | 0.046 |
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!