Utiliser des compilateurs JIT pour ralentir mes boucles Python ?

PHPz
Libérer: 2024-08-29 06:35:07
original
619 Les gens l'ont consulté

Si vous ne l'avez pas entendu, les boucles Python peuvent être lentes, en particulier lorsque vous travaillez avec de grands ensembles de données. Si vous essayez d'effectuer des calculs sur des millions de points de données, le temps d'exécution peut rapidement devenir un goulot d'étranglement. Heureusement pour nous, Numba dispose d'un compilateur Just-in-Time (JIT) que nous pouvons utiliser pour accélérer nos calculs numériques et nos boucles en Python.

L'autre jour, je me suis retrouvé dans le besoin d'une simple fonction de lissage exponentiel en Python. Cette fonction devait prendre en compte un tableau et renvoyer un tableau de même longueur avec les valeurs lissées. En règle générale, j'essaie d'éviter les boucles autant que possible en Python (en particulier lorsqu'il s'agit de Pandas DataFrames). À mon niveau actuel de capacités, je ne voyais pas comment éviter d'utiliser une boucle pour lisser de manière exponentielle un tableau de valeurs.

Je vais parcourir le processus de création de cette fonction de lissage exponentiel et la tester avec et sans la compilation JIT. J'aborderai brièvement JIT et comment je me suis assuré de coder la boucle d'une manière qui fonctionnait avec le mode nopython.

Qu’est-ce que le JIT ?

Les compilateurs JIT sont particulièrement utiles avec les langages de niveau supérieur comme Python, JavaScript et Java. Ces langages sont connus pour leur flexibilité et leur facilité d'utilisation, mais ils peuvent souffrir de vitesses d'exécution plus lentes que celles des langages de niveau inférieur comme C ou C++. La compilation JIT aide à combler cette lacune en optimisant l'exécution du code au moment de l'exécution, le rendant ainsi plus rapide sans sacrifier les avantages de ces langages de niveau supérieur.

Lors de l'utilisation du mode nopython=True dans le compilateur Numba JIT, l'interpréteur Python est entièrement contourné, obligeant Numba à tout compiler en code machine. Cela se traduit par une exécution encore plus rapide en éliminant la surcharge associée au typage dynamique de Python et à d'autres opérations liées à l'interpréteur.

Construire la fonction de lissage exponentiel rapide

Le lissage exponentiel est une technique utilisée pour lisser les données en appliquant une moyenne pondérée aux observations passées. La formule du lissage exponentiel est :

S t = α V t + ( 1 α ) S t 1 S_t = alpha cdot V_t + (1 - alpha) cdot S_{t-1} St=α⋅Vt+(1−α)⋅St−1

où :

  • S t S_t St: Représente la valeur lissée à l'instant t t t.
  • V t V_t Vt: Représente la valeur d'origine à l'heure t t tà partir du tableau de valeurs.
  • α alpha α: Le facteur de lissage, qui détermine le poids de la valeur actuelle V t V_t Vtdans le processus de lissage.
  • S t 1 S_{t-1} St−1: Représente la valeur lissée à l'instant t 1 t-1 t−1, c'est-à-dire la valeur lissée précédente.

La formule applique un lissage exponentiel, où :

  • La nouvelle valeur lissée S t S_t Stest une moyenne pondérée de la valeur actuelle V t V_t Vtet la valeur lissée précédente S t 1 S_{t-1} St−1.
  • Le facteur α alpha αdétermine l'influence de la valeur actuelle V t V_t Vta sur la valeur lissée par rapport à la valeur lissée précédente S t 1 S_{t-1} St−1.

Pour implémenter cela en Python et nous en tenir aux fonctionnalités qui fonctionnent avec le mode nopython=True, nous transmettrons un tableau de valeurs de données et le flottant alpha. Par défaut, l'alpha est 0,33333333 car cela correspond à mon cas d'utilisation actuel. Nous allons initialiser un tableau vide pour stocker les valeurs lissées, boucler et calculer, et renvoyer les valeurs lissées. Voilà à quoi ça ressemble :

@jit(nopython=True) def fast_exponential_smoothing(values, alpha=0.33333333): smoothed_values = np.zeros_like(values) # Array of zeros the same length as values smoothed_values[0] = values[0] # Initialize the first value for i in range(1, len(values)): smoothed_values[i] = alpha * values[i] + (1 - alpha) * smoothed_values[i - 1] return smoothed_values
Copier après la connexion

Simple, non ? Voyons si JIT fait quelque chose maintenant. Tout d’abord, nous devons créer un large tableau d’entiers. Ensuite, nous appelons la fonction, chronométrons le temps qu'il a fallu pour calculer et imprimons les résultats.

# Generate a large random array of a million integers large_array = np.random.randint(1, 100, size=1_000_000) # Test the speed of fast_exponential_smoothing start_time = time.time() smoothed_result = fast_exponential_smoothing(large_array) end_time = time.time() print(f"Exponential Smoothing with JIT took {end_time - start_time:.6f} seconds with 1,000,000 sample array.")
Copier après la connexion

This can be repeated and altered just a bit to test the function without the JIT decorator. Here are the results that I got:

Using JIT-compilers to make my Python loops slower?

Wait, what the f***?

I thought JIT was supposed to speed it up. It looks like the standard Python function beat the JIT version and a version that attempts to use no recursion. That's strange. I guess you can't just slap the JIT decorator on something and make it go faster? Perhaps simple array loops and NumPy operations are already pretty efficient? Perhaps I don't understand the use case for JIT as well as I should? Maybe we should try this on a more complex loop?

Here is the entire code python file I created for testing:

import numpy as np from numba import jit import time @jit(nopython=True) def fast_exponential_smoothing(values, alpha=0.33333333): smoothed_values = np.zeros_like(values) # Array of zeros the same length as values smoothed_values[0] = values[0] # Initialize the first value for i in range(1, len(values)): smoothed_values[i] = alpha * values[i] + (1 - alpha) * smoothed_values[i - 1] return smoothed_values def fast_exponential_smoothing_nojit(values, alpha=0.33333333): smoothed_values = np.zeros_like(values) # Array of zeros the same length as values smoothed_values[0] = values[0] # Initialize the first value for i in range(1, len(values)): smoothed_values[i] = alpha * values[i] + (1 - alpha) * smoothed_values[i - 1] return smoothed_values def non_recursive_exponential_smoothing(values, alpha=0.33333333): n = len(values) smoothed_values = np.zeros(n) # Initialize the first value smoothed_values[0] = values[0] # Calculate the rest of the smoothed values decay_factors = (1 - alpha) ** np.arange(1, n) cumulative_weights = alpha * decay_factors smoothed_values[1:] = np.cumsum(values[1:] * np.flip(cumulative_weights)) + (1 - alpha) ** np.arange(1, n) * values[0] return smoothed_values # Generate a large random array of a million integers large_array = np.random.randint(1, 1000, size=10_000_000) # Test the speed of fast_exponential_smoothing start_time = time.time() smoothed_result = fast_exponential_smoothing_nojit(large_array) end_time = time.time() print(f"Exponential Smoothing without JIT took {end_time - start_time:.6f} seconds with 1,000,000 sample array.") # Test the speed of fast_exponential_smoothing start_time = time.time() smoothed_result = fast_exponential_smoothing(large_array) end_time = time.time() print(f"Exponential Smoothing with JIT took {end_time - start_time:.6f} seconds with 1,000,000 sample array.") # Test the speed of fast_exponential_smoothing start_time = time.time() smoothed_result = non_recursive_exponential_smoothing(large_array) end_time = time.time() print(f"Exponential Smoothing with no recursion or JIT took {end_time - start_time:.6f} seconds with 1,000,000 sample array.")
Copier après la connexion

I attempted to create the non-recursive version to see if vectorized operations across arrays would make it go faster, but it seems to be pretty damn fast as it is. These results remained the same all the way up until I didn't have enough memory to make the array of random integers.

Let me know what you think about this in the comments. I am by no means a professional developer, so I am accepting all comments, criticisms, or educational opportunities.

Until next time.

Happy coding!

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!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!