Performanse Pythona: izvorne NumPy petlje, povratni pozivi i troškovi konverzije
Kontrolisani numerički test samo za CPU prati identične vrednosti kroz CPython petlje, NumPy aritmetiku, omot za Python povratni poziv i kompilovanu Numba petlju.
Kontrolisani numerički test samo za CPU prati identične vrednosti kroz CPython petlje, NumPy aritmetiku, omot za Python povratni poziv i kompilovanu Numba petlju. NumPy sa spremnim ulazom je imao veliku prednost, dok je konverzija iz i nazad u Python liste smanjila tu prednost na skoro izjednačenje. Epizoda objašnjava granicu izvršavanja i kako izmeriti rezultat koji aplikacija treba.
Pročitajte pisano izdanje (engleski) ↗
Šta ovaj video pokriva
- CPython prevodi izvorni kod u bajtkod. Ponavljajući rad tumača/objekta i tipizovana izvorna numerička petlja su različiti načini izvršavanja.
- Numerička NumPy aritmetika može predati tipizirane serije kompilovanim operacijama; aritmetička petlja i dalje postoji, a pogledi mogu imati neprekidne rasporede.
- Izmerite konverziju i potrebnu izlaznu reprezentaciju. U ovom pokretanju, konverzija Python lista u nizove i nazad izbrisala je veliku prednost spremnog niza.
- Mala radna opterećenja mogu favorizovati običan Python jer su fiksni troškovi pozivanja niza važni. Razumevanje sa osam vrednosti pobedilo je u ovom izmerenom poređenju.
- np.vectorize je zgodan omot koji poziva Python za uzastopne elemente; samo interfejs serije ne uspostavlja izvorno numeričko izvršavanje.
- Kompajlirane petlje sa Python sintaksom takođe mogu biti brze. Uključite troškove hladne kompilacije/podešavanja kada su važni za pozivaoca.
- Izvorno izvršavanje, CPU SIMD, višestruko izvršavanje i GPU pozadine su zasebne ideje. Dostupne CPU funkcije ne prate instrukcije korišćene u određenom pokretanju.
Preveden transkript
Prevedeno sa originalne engleske naracije. Dostupan audio i titlovi kontrolisani su od strane YouTube-a.
Zašto isti CPU može završiti ranije?
0:00 Mislite da je Pythonu potreban novi jezik da bi bio brz. Isti CPU je pokrenuo našu seriju brže nakon što se petlja pomerila. Kuda je otišao posao? Kada ubrzanje nestaje? Koji prečica vraća Python nazad? Ovo je The Daily Diff, ispod haube. Pazite na tu prečicu. CPython je standardna implementacija Pythona.
0:17 Kompiluje vaš izvorni kod u bajtkod, a zatim pokreće te instrukcije u tumaču. NumPy je numerička biblioteka čiji nizovi sadrže vrednosti sa deklarisanim tipom podataka. Njegova ugrađena aritmetika može predati celu seriju kompilovanim operacijama. Uzmite milion vrednosti sa pokretnom tačkom, kvadrirajte svaku, zatim dodajte originalnu vrednost. Svaka verzija dobija identične vrednosti i proizvodi podudarne odgovore. Vrednosti su dovoljno male da razlike u zaokruživanju ne utiču na rezultat.
0:40 Na ovoj mašini, razumevanje je trajalo oko trideset tri milisekunde. NumPy je trajao manje od jedne. Oba su radila na CPU-u. To je koristan dokaz za ovo radno opterećenje i užasna čitulja za programski jezik. Počnite sa običnom petljom.
Koji posao ponavlja Python petlja?
0:53 Python dobija sledeću vrednost, izvodi aritmetiku i dodaje odgovor. Zatim to ponovo radi. Množenje je jeftino. Priprema sledećeg množenja i rukovanje Python objektima oko njega, dodaje posao svaki put. Ovo je stvarni rastav testirane funkcije. Iteracija, množenje i sabiranje pojavljuju se kao instrukcije bajtkoda. Python verzije i tumači donose različite odluke.
1:14 Ovi tajminzi pripadaju verziji na ekranu, što je Python tri tačka deset. Python lista skladišti reference na objekte, tako da različite vrste stvari mogu deliti listu. Naš aritmetički zadatak koristi brojeve tokom celog rada. Plaća za opšti aranžman dok traži uslugu koja se ponavlja. Zamislite da naručujete istu kafu milion puta i popunjavate novi obrazac za svaku šoljicu. Aparat za kafu je u redu.
1:36 Administracija je razvila svoju karijeru.
Gde NumPy pomera petlju?
1:38 NumPy menja ugovor. Ovaj niz sadrži vrednosti sa pokretnom tačkom fiksne veličine sa jednim deklarisanim tipom. Naš niz je neprekidan, tako da njegove vrednosti sede zajedno u memoriji. Pogledi mogu imati druge rasporede. Aritmetička operacija zna kakve podatke prima. Poziv predaje seriju izvornoj operaciji. Petlja i dalje posećuje vrednosti unutar kompilovanog koda. Može ponovo koristiti znanje o tipu tokom tog rada.
2:00 Python ostaje osoba koja predaje posao. Itamar Turner Trauring objašnjava razliku u Python Speed. Serijski interfejsi, izvorne implementacije i vektorske instrukcije procesora su odvojene stvari. Naziv funkcije pruža vrlo malo dokaza.
Da li brzo znači GPU ili mnogo CPU jezgara?
2:13 Ovaj rezultat je koristio NumPy na CPU-u. GPU pozadina kao što je CuPy bira drugačiji dom za nizove i operacije. Kupovina GPU-a ostavlja ovu običnu Python petlju da radi tamo gde je već radila. CPU vektorske instrukcije mogu obrađivati nekoliko vrednosti zajedno. NumPy ima mehanizme za odabir podržanih kernela. Naše vreme izvršavanja prijavljuje dostupne funkcije, ali ne prati instrukcije ovog izraza. Pripisivanje celog izmerenog dobitka vektorskim instrukcijama bi preuveličalo dokaze.
2:40 Kolica za kupovinu GPU-a mogu ostati zatvorena. Nešto je duboko zadovoljavajuće kod ispravke performansi koja takođe otkazuje kupovinu. Nažalost, benchmark se nastavlja.
Kada impresivno ubrzanje nestaje?
2:49 Sa osam vrednosti, razumevanje je pobedilo. Pozivanje mehanizma niza koštalo je više od malog posla. Veći spremni nizovi su preuzeli vođstvo. Koristili smo ponovljene probe i zagrejani proces. Novije Python verzije mogu promeniti presek. Zatim promenite šta aplikacija treba. Počnite sa Python listom i završite sa novom Python listom. NumPy mora pretvoriti ulaz u niz i pretvoriti odgovor nazad u Python
3:11 objekte. Ti koraci pripadaju računu ako su vašoj aplikaciji potrebni. Na milion vrednosti, ta kompletna putanja je trajala oko dvadeset osam milisekundi. Razumevanje je takođe trajalo oko dvadeset osam. Naše dramatično vođstvo postalo je skoro izjednačenje. Brzi deo je bio stvaran. Prednost je zavisila od toga da podaci ostanu u svom numeričkom obliku.
Koje granice pripadaju benchmarku?
3:28 Pratite podatke. Ako sledeća operacija prihvata niz, zadržite niz. Ako se više puta konvertujete između lista i nizova, izmerite te prelaze. Zaustavljanje pre skupe predaje daje tačne brojeve za pogrešno pitanje. Množenje ovog niza stvara privremeni rezultat. Dodavanje originalnih vrednosti stvara izlaz. Izvorno izvršavanje i dalje zahteva memoriju i premešta podatke.
3:47 Neke operacije prihvataju izlazni bafer, smanjujući alokacije kada vaš kod može bezbedno da ga ponovo koristi. Jedna lepa linija može naručiti nekoliko velikih bafera. Python Speed takođe ukazuje na ovaj problem. Alokator je izuzetno ravnodušan prema vašoj estetici.
Može li i kompilovana Python petlja biti brza?
4:00 Takođe možete zadržati sintaksu petlje i promeniti njeno izvršavanje. Numba može kompajlirati podržane numeričke Python funkcije u izvorni kod. Ovde je petlja iz našeg testa sa svojim kompajler dekoratorom. I dalje piše za. Skupa putanja tumača se promenila. Nakon kompilacije, ova petlja je trajala manje od pola milisekunde sa spremnim nizovima. Sama kompilacija je trajala oko sekundu u ovom pokretanju. Taj trošak podešavanja je isključen iz ponovljenih tajminga,
4:22 tako da mali jednokratni posao zaslužuje drugačije poređenje. Numbin sopstveni vodič rado prikazuje eksplicitne petlje. Pokretanje migracije jezika pre nego što se pronađe vruća putanja je složen način da se izbegne otvaranje profilera. Prvo pronađite šta troši vreme. Mrežna čekanja, parsiranje objekata i grupna aritmetika zahtevaju različite ispravke. Izvorne operacije sa nizovima pomažu kada se izračunavanje i podaci podudaraju.
4:43 Izmerite rezultat koji vaš pozivaoc zapravo treba. Sada za tu prečicu. Imate Python funkciju i želite da ona prihvata niz. Omot nazvan vectorize zvuči kao nedostajuće dugme za performanse. Njegova dokumentacija ima važnu rečenicu koja čeka.
Šta np.vectorize zapravo izvršava?
4:56 NumPy kaže da je njegov vectorize omot prvenstveno namenjen pogodnosti. Njegova implementacija je u suštini for petlja. Omot prihvata serije, ali nastavlja da poziva vašu Python funkciju za uzastopne elemente. Interfejs je postao kraći. Granica izvršavanja je ostala zauzeta. Gledajte kako se iste vrednosti vraćaju kroz Python povratni poziv. Sa spremnim nizovima, ova verzija je trajala oko osamdeset sedam milisekundi. Bila je sporija od našeg razumevanja.
5:18 Korisni broadcasting omot može sačuvati tačan rad po elementu koji ste pokušavali da uklonite. Proverite implementaciju iza serijskog interfejsa, držite kompatibilne podatke u njihovom korisnom obliku i uključite konverziju u poređenje. Izvorne, povratne i kompilovane petlje mogu izgledati slično sa mesta pozivaoca. Naziv vectorize je dao obećanje koje vaša štoperica nikada nije potpisala. Zadržite ga kada vam je potrebna pogodnost. Za performanse, proverite petlju koja se izvršava.
Šta treba da promenite u svom kodu?
5:41 Moja presuda je SHIP IT za serijsku obradu izmerene numeričke vruće putanje. Zadržite obične petlje kada već dobro rade. Proverite gde se posao obavlja i šta sledeći pozivaoc treba. Pretplatite se, pritisnite zvonce i recite mi u komentarima da li biste ga drugačije žigosali. I to je razlika za danas. Ja sam Niko iz Axrisija. Spajajte odgovorno.
Izvori
- Universal functions (ufunc) basics — NumPy 2.2NumPy
- The N-dimensional array — NumPy 2.2NumPy
- numpy.vectorize — convenience and performance notesNumPy
- CPU/SIMD optimizations — NumPy 2.2NumPy
- dis — Disassembler for Python bytecodePython Software Foundation
- CPython 3.10.12 list representationCPython contributors
- The limits of Python vectorization as a performance techniqueItamar Turner-Trauring / Python⇒Speed
- Performance TipsNumba
- Basics of CuPyCuPy



