Izvedba Pythona: izvorne NumPy petlje, povratni pozivi i troškovi konverzije
Kontrolirani numerički test samo za CPU prati identične vrijednosti kroz CPython petlje, NumPy aritmetiku, Python omotač povratnog poziva i kompiliranu Numba petlju.
Kontrolirani numerički test samo za CPU prati identične vrijednosti kroz CPython petlje, NumPy aritmetiku, Python omotač povratnog poziva i kompiliranu Numba petlju. NumPy sa spremnim ulazom imao je veliku prednost, dok je konverzija iz i natrag u Python liste smanjila tu prednost na gotovo izjednačenje. Epizoda objašnjava granicu izvršavanja i kako izmjeriti rezultat koji aplikacija treba.
Pročitajte pisano izdanje (engleski) ↗
Što ovaj video pokriva
- CPython prevodi izvorni kod u bajtkod. Ponovljeni rad prevoditelja/objekata i tipizirana izvorna numerička petlja različiti su načini izvršavanja.
- Numerička NumPy aritmetika može predati tipizirane skupove kompiliranima operacijama; aritmetička petlja i dalje postoji, a pogledi mogu imati neprekinute rasporede.
- Izmjerite konverziju i potrebnu izlaznu reprezentaciju. U ovom pokretanju, pretvaranje Python lista u nizove i natrag izbrisalo je veliku prednost spremnog niza.
- Mala radna opterećenja mogu pogodovati običnom Pythonu jer su fiksni troškovi pozivanja niza važni. Razumijevanje s osam vrijednosti pobijedilo je u ovoj mjerenoj usporedbi.
- np.vectorize je prikladan omotač koji poziva Python za uzastopne elemente; samo sučelje skupne obrade ne uspostavlja izvorno numeričko izvršavanje.
- Kompilirane petlje sintakse Pythona također mogu biti brze. Uključite troškove hladne kompilacije/postavljanja kada su važni za pozivatelja.
- Izvorno izvršavanje, CPU SIMD, višestruko obrađivanje i GPU pozadina su zasebne ideje. Dostupne CPU značajke ne prate upute korištene u određenom pokretanju.
Prevedeni transkript
Prevedeno iz izvornog engleskog pripovijedanja. Dostupni zvuk i titlovi kontroliraju se putem YouTubea.
Zašto isti CPU može završiti ranije?
0:00 Mislite da Pythonu treba novi jezik da bi bio brz. Isti CPU je pokrenuo našu seriju brže nakon što se petlja pomaknula. Kamo je nestao posao? Kada ubrzanje nestane? Koja prečica vraća Python? Ovo je The Daily Diff, ispod haube. Pazite na tu prečicu. CPython je standardna implementacija Pythona.
0:17 On prevodi vaš izvorni kod u bajtkod, a zatim izvršava te instrukcije u interpretatoru. NumPy je numerička biblioteka čiji nizovi sadrže vrijednosti s deklariranim tipom podataka. Njegova ugrađena aritmetika može predati cijelu seriju kompiliranima operacijama. Uzmite milijun float vrijednosti, kvadrirajte svaku, zatim dodajte izvornu vrijednost. Svaka verzija dobiva identične vrijednosti i proizvodi podudarne odgovore. Vrijednosti su dovoljno male da razlike u zaokruživanju ne utječu na utrku.
0:40 Na ovom stroju, izračun je trajao oko trideset tri milisekunde. NumPy je trajao manje od jedne. Oba su se pokrenula na CPU-u. To je koristan dokaz za ovo radno opterećenje, i strašna osmrtnica za programski jezik. Počnimo s običnom petljom.
Koji posao ponavlja Python petlja?
0:53 Python dobiva sljedeću vrijednost, izvodi aritmetiku i dodaje odgovor. Zatim to radi ponovno. Množenje je jeftino. Organiziranje sljedećeg množenja i rukovanje Python objektima oko njega dodaje posao svaki put. Ovo je stvarni rastav testirane funkcije. Iteracija, množenje i zbrajanje pojavljuju se kao instrukcije bajtkoda. Verzije i interpretatori Pythona donose različite izbore.
1:14 Ova vremena pripadaju verziji na ekranu, što je Python tri točka deset. Python lista pohranjuje reference na objekte, tako da različite vrste stvari mogu dijeliti listu. Naš aritmetički zadatak koristi brojeve posvuda. Plaća opći aranžman dok traži ponavljajuću uslugu. Zamislite da naručite istu kavu milijun puta i ispunite novi obrazac za svaku šalicu. Aparat za kavu je u redu.
1:36 Papirologija je razvila vlastitu karijeru.
Gdje NumPy pomiče petlju?
1:38 NumPy mijenja ugovor. Ovaj niz sadrži float vrijednosti fiksne veličine s jednim deklariranim tipom. Naš niz je neprekidan, tako da njegove vrijednosti sjede zajedno u memoriji. Pogledi mogu imati druge rasporede. Aritmetička operacija zna koju vrstu podataka prima. Poziv predaje seriju izvornoj operaciji. Petlja i dalje posjećuje vrijednosti unutar kompiliranog koda. Može ponovno koristiti znanje o tipu kroz taj posao.
2:00 Python ostaje osoba koja predaje posao. Itamar Turner Trauring objašnjava razliku u Python Speedu. Serijska sučelja, izvorne implementacije i vektorske instrukcije procesora su različite stvari. Naziv funkcije pruža vrlo malo dokaza.
Znači li brzo GPU ili mnogo CPU jezgri?
2:13 Ovaj rezultat koristio je NumPy na CPU-u. GPU pozadina kao što je CuPy odabire drugi dom za nizove i operacije. Kupnja GPU-a ostavlja ovu običnu Python petlju da radi tamo gdje je već radila. CPU vektorske instrukcije mogu zajedno obrađivati nekoliko vrijednosti. NumPy ima mehanizme za odabir podržanih jezgri. Naše vrijeme izvođenja izvješćuje o dostupnim značajkama, ali ne prati instrukcije ovog izraza. Davanje cijelog izmjerenog dobitka vektorskim instrukcijama bi preuveličalo dokaze.
2:40 Kolica za kupovinu GPU-a mogu ostati zatvorena. Postoji nešto duboko zadovoljavajuće u popravku performansi koji ujedno poništava kupnju. Nažalost, benchmark se nastavlja.
Kada nestaje impresivno ubrzanje?
2:49 S osam vrijednosti, shvaćanje je pobijedilo. Pozivanje mehanizma za nizove koštalo je više od malog posla. Veći gotovi nizovi su poveli. Koristili smo ponovljene pokušaje i zagrijani proces. Novije Python verzije mogu promijeniti prijelaznu točku. Zatim promijenite što aplikacija treba. Počnite s Python listom i završite s novom Python listom. NumPy mora pretvoriti ulaz u niz i pretvoriti odgovor natrag u Python
3:11 objekte. Ti koraci spadaju na račun ako ih vaša aplikacija treba. S milijun vrijednosti, taj je potpuni put trajao oko dvadeset osam milisekundi. Shvaćanje je trajalo oko dvadeset osam također. Naša dramatična prednost postala je gotovo izjednačenje. Brzi dio je bio stvaran. Prednost je ovisila o tome da podaci ostanu u svom numeričkom obliku.
Koje granice pripadaju benchmarku?
3:28 Slijedite podatke. Ako sljedeća operacija prihvaća niz, zadržite niz. Ako se više puta pretvarate između lista i nizova, izbrojite te prijelaze. Zaustavljanje prije skupog prijenosa daje točne brojeve za pogrešno pitanje. Množenje ovog niza stvara privremeni rezultat. Dodavanje izvornih vrijednosti stvara izlaz. Izvorno izvršavanje i dalje treba memoriju i premješta podatke.
3:47 Neke operacije prihvaćaju izlazni buffer, smanjujući alokacije kada vaš kod to može sigurno ponovno koristiti. Jedna lijepa linija može naručiti nekoliko velikih međuspremnika. Python Speed također ističe ovaj problem. Alokator je izvanredno ravnodušan prema vašoj estetici.
Može li i kompilovana Python petlja biti brza?
4:00 Također možete zadržati sintaksu petlje i promijeniti njezino izvršavanje. Numba može prevesti podržane numeričke Python funkcije u izvorni kod. Evo petlje iz našeg testa s njenim dekoratorom kompilatora. I dalje piše 'za'. Skupi interpretatorski put se promijenio. Nakon kompilacije, ova petlja je trajala manje od pola milisekunde sa spremnim nizovima. Sama kompilacija je trajala oko sekunde u ovom pokretanju. Taj trošak postavljanja bio je isključen iz ponovljenih mjerenja vremena,
4:22 tako da mali jednokratni posao zaslužuje drugačiju usporedbu. Numbein vlastiti vodič rado prikazuje eksplicitne petlje. Pokretanje migracije jezika prije pronalaženja vruće putanje je složen način da se izbjegne otvaranje profilera. Prvo pronađite što troši vrijeme. Mrežna čekanja, parsiranje objekata i skupna aritmetika zahtijevaju različita rješenja. Nativne operacije niza pomažu kada se izračun i podaci podudaraju.
4:43 Izmjerite rezultat koji vaš pozivatelj zapravo treba. Sada, za taj prečac. Imate Python funkciju i želite da prihvati niz. Omotač nazvan vectorize zvuči kao nedostajući gumb za performanse. Njegova dokumentacija sadrži važnu rečenicu koja čeka.
Što np.vectorize zapravo izvršava?
4:56 NumPy kaže da je njegov vectorize omotač prvenstveno namijenjen za praktičnost. Njegova implementacija je u biti for petlja. Omotač prihvaća serije, ali nastavlja pozivati vašu Python funkciju za uzastopne elemente. Sučelje je postalo kraće. Granica izvršenja ostala je zauzeta. Gledajte kako se iste vrijednosti vraćaju kroz Python povratni poziv. S gotovim nizovima, ova verzija je trajala oko osamdeset sedam milisekundi. Bila je sporija od našeg shvaćanja.
5:18 Koristan omotač za emitiranje može zadržati točan rad po elementu koji ste pokušavali ukloniti. Provjerite implementaciju iza skupnog sučelja, čuvajte kompatibilne podatke u njihovom korisnom obliku i uključite konverziju u usporedbu. Izvorne, povratne i kompilovane petlje mogu izgledati slično iz perspektive pozivatelja. Naziv 'vectorize' je obećao nešto što vaš štoperica nikada nije potvrdila. Zadržite ga kada je praktičnost ono što vam treba. Za performanse, provjerite petlju koja se izvodi.
Što biste trebali promijeniti u svom kodu?
5:41 Moja presuda je SHIP IT za batching izmjerene numeričke vruće staze. Držite obične petlje kada već dobro rade. Provjerite gdje se posao obavlja i što sljedeći pozivatelj treba. Pretplatite se, pritisnite zvonce i recite mi u komentarima biste li to drugačije označili. 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



