Verwenden Sie JIT-Compiler, um meine Python-Schleifen langsamer zu machen?

PHPz
Freigeben: 2024-08-29 06:35:07
Original
620 Leute haben es durchsucht

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.

Was ist JIT?

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.

Aufbau der schnellen exponentiellen Glättungsfunktion

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:

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

wo:

  • S t S_t St: Stellt den geglätteten Wert zum Zeitpunkt dar t t t.
  • V t V_t Vt: Stellt den ursprünglichen Wert zum Zeitpunkt dar t t taus dem Wertearray.
  • α alpha α: Der Glättungsfaktor, der das Gewicht des aktuellen Werts bestimmt V t V_t Vtim Glättungsprozess.
  • S t 1 S_{t-1} St−1: Stellt den geglätteten Wert zum Zeitpunkt dar t 1 t-1 t−1, d. h. der vorherige geglättete Wert.

Die Formel wendet eine exponentielle Glättung an, wobei:

  • Der neue geglättete Wert S t S_t Stist ein gewichteter Durchschnitt des aktuellen Wertes V t V_t Vtund der vorherige geglättete Wert S t 1 S_{t-1} St−1.
  • Der Faktor α alpha αbestimmt, wie stark der aktuelle Wert beeinflusst wird V t V_t Vthat auf dem geglätteten Wert im Vergleich zum vorherigen geglätteten Wert S t 1 S_{t-1} St−1.

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
Nach dem Login kopieren

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.")
Nach dem Login kopieren

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.")
Nach dem Login kopieren

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!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!