Heim > Artikel > Backend-Entwicklung > Verwenden Sie JIT-Compiler, um meine Python-Schleifen langsamer zu machen?
Falls Sie es noch nicht gehört haben: Python-Schleifen können langsam sein – insbesondere bei der Arbeit mit großen Datensätzen. Wenn Sie Berechnungen über Millionen von Datenpunkten durchführen möchten, kann die Ausführungszeit schnell zu einem Engpass werden. Zum Glück verfügt Numba über einen Just-in-Time-Compiler (JIT), mit dem wir unsere numerischen Berechnungen und Schleifen in Python beschleunigen können.
Neulich brauchte ich eine einfache exponentielle Glättungsfunktion in Python. Diese Funktion musste ein Array aufnehmen und ein Array derselben Länge mit den geglätteten Werten zurückgeben. Normalerweise versuche ich, Schleifen in Python nach Möglichkeit zu vermeiden (insbesondere beim Umgang mit Pandas DataFrames). Auf meinem derzeitigen Leistungsstand habe ich nicht gesehen, wie ich die Verwendung einer Schleife zum exponentiellen Glätten eines Wertearrays vermeiden kann.
Ich werde den Prozess der Erstellung dieser exponentiellen Glättungsfunktion durchgehen und sie mit und ohne JIT-Kompilierung testen. Ich werde kurz auf JIT eingehen und wie ich dafür gesorgt habe, dass die Schleife so codiert wurde, dass sie mit dem Nopython-Modus funktioniert.
JIT-Compiler sind besonders nützlich bei höheren Sprachen wie Python, JavaScript und Java. Diese Sprachen sind für ihre Flexibilität und Benutzerfreundlichkeit bekannt, können jedoch im Vergleich zu Sprachen niedrigerer Ebenen wie C oder C++ unter langsameren Ausführungsgeschwindigkeiten leiden. Die JIT-Kompilierung hilft, diese Lücke zu schließen, indem sie die Ausführung des Codes zur Laufzeit optimiert und ihn schneller macht, ohne auf die Vorteile dieser höheren Sprachen zu verzichten.
Bei Verwendung des nopython=True-Modus im Numba-JIT-Compiler wird der Python-Interpreter vollständig umgangen, sodass Numba gezwungen ist, alles bis hin zum Maschinencode zu kompilieren. Dies führt zu einer noch schnelleren Ausführung, da der mit der dynamischen Typisierung von Python und anderen interpreterbezogenen Vorgängen verbundene Overhead entfällt.
Exponentielle Glättung ist eine Technik zur Glättung von Daten durch Anwendung eines gewichteten Durchschnitts über vergangene Beobachtungen. Die Formel für die exponentielle Glättung lautet:
wo:
Die Formel wendet eine exponentielle Glättung an, wobei:
Um dies in Python zu implementieren und die Funktionalität beizubehalten, die mit dem Modus „nopython=True“ funktioniert, übergeben wir ein Array von Datenwerten und den Alpha-Float. Ich verwende standardmäßig den Alphawert 0,33333333, da dies zu meinem aktuellen Anwendungsfall passt. Wir werden ein leeres Array initialisieren, um die geglätteten Werte darin zu speichern, eine Schleife zu erstellen, zu berechnen und geglättete Werte zurückzugeben. So sieht es aus:
@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
Einfach, oder? Mal sehen, ob JIT jetzt etwas tut. Zuerst müssen wir ein großes Array von Ganzzahlen erstellen. Dann rufen wir die Funktion auf, messen die Berechnungsdauer und geben die Ergebnisse aus.
# 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.")
This can be repeated and altered just a bit to test the function without the JIT decorator. Here are the results that I got:
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.")
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!
Das obige ist der detaillierte Inhalt vonVerwenden Sie JIT-Compiler, um meine Python-Schleifen langsamer zu machen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!