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, Python omotač za povratni poziv i kompajliranu Numba petlju.
Kontrolisani numerički test samo za CPU prati identične vrednosti kroz CPython petlje, NumPy aritmetiku, Python omotač za povratni poziv i kompajliranu Numba petlju. NumPy sa spremnim ulazom imao je veliku prednost, dok je konverzija iz i nazad u Python liste smanjila tu prednost na skoro izjednačenje. Epizoda objašnjava granicu izvršenja i kako izmeriti rezultat koji aplikacija treba.
Pročitajte pisano izdanje (engleski) ↗
Šta ovaj video pokriva
- CPython kompajlira izvorni kod u bajtkod. Ponavljajući rad tumača/objekata i tipizirana izvorna numerička petlja su različiti načini izvršenja.
- Numerička NumPy aritmetika može predati tipizirane skupove kompajliranim operacijama; aritmetička petlja i dalje postoji, a pogledi mogu imati neprekidan raspored.
- Izmerite konverziju i potrebnu izlaznu reprezentaciju. U ovom pokretanju, konverzija Python listi u nizove i nazad izbrisala je veliku prednost spremnog niza.
- Mala radna opterećenja mogu favorizovati običan Python jer su fiksni troškovi poziva niza važni. Razumevanje od osam vrednosti pobedilo je u ovom merenom poređenju.
- np.vectorize je zgodan omotač koji poziva Python za uzastopne elemente; samo grupno sučelje ne uspostavlja izvorno numeričko izvršavanje.
- Kompajlirane petlje Python sintakse takođe mogu biti brze. Uključite troškove hladne kompilacije/postavke kada su oni važni za pozivaoca.
- Izvorno izvršavanje, CPU SIMD, višenitnost i GPU pozadine su odvojene ideje. Dostupne CPU funkcije ne prate instrukcije korišćene u određenom pokretanju.
Prevedeni transkript
Prevedeno iz originalne engleske naracije. Dostupan zvuk i titlovi kontroliše YouTube.
Zašto isti CPU može završiti ranije?
0:00 Mislite da Pythonu treba novi jezik da bi bio brz. Isti CPU je brže pokrenuo našu grupu nakon što se petlja pomerila. Gde je otišao posao? Kada ubrzanje nestaje? 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 Kompajlira vaš izvorni kod u bajtkod, a zatim izvršava te instrukcije u interpreteru. NumPy je numerička biblioteka čiji nizovi sadrže vrednosti sa deklarisanim tipom podataka. Njegova ugrađena aritmetika može predati celu grupu kompajliranim operacijama. Uzmite milion floating point vrednosti, 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 ostanu izvan trke.
0:40 Na ovoj mašini, razumevanje je trajalo oko trideset tri milisekunde. NumPy je trajao manje od jedne. Oba su se pokretala na CPU-u. To je koristan dokaz za ovo 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 ponovi. 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 bajtkod instrukcije. Python verzije i interpreteri donose različite izbore.
1:14 Ovi tajminzi pripadaju verziji na ekranu, koja je Python tri tačka deset. Python lista čuva reference na objekte, tako da različite vrste stvari mogu deliti listu. Naš aritmetički zadatak koristi brojeve svuda. Plaća opšti aranžman dok traži ponavljajuću uslugu. Zamislite da naručite istu kafu milion puta i popunjavate novi obrazac za svaku šoljicu. Aparat za kafu je u redu.
1:36 Papirologija je razvila sopstvenu karijeru.
Gde NumPy pomera petlju?
1:38 NumPy menja ugovor. Ovaj niz sadrži float vrednosti 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 koju vrstu podataka prima. Poziv predaje grupu izvornoj operaciji. Petlja i dalje posećuje vrednosti unutar kompajliranog koda. Može ponovo koristiti znanje o tipu kroz taj posao.
2:00 Python ostaje osoba koja predaje posao. Itamar Turner Trauring objašnjava razliku u Python Speed. Grupni 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 drugi 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 mehanizam za odabir podržanih jezgara. Naše vreme izvršavanja prijavljuje dostupne funkcije, ali ne prati instrukcije ovog izraza. Davanje celokupnog izmerenog dobitka vektorskim instrukcijama bi preneprodalo dokaz.
2:40 GPU korpa za kupovinu može ostati zatvorena. Ima nešto duboko zadovoljavajuće u popravci 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 sićušnog posla. Veći spremni nizovi su se izdvojili. Koristili smo ponovljene pokušaje i zagrejani proces. Novije Python verzije mogu promeniti presek. Zatim promenite ono što aplikacija treba. Počnite sa Python listom i završite sa svežom Python listom. NumPy mora pretvoriti ulaz u niz i pretvoriti odgovor nazad u Python
3:11 objekte. Ti koraci spadaju u račun ako ih vaša aplikacija treba. Na milion vrednosti, taj kompletan put je trajao oko dvadeset osam milisekundi. Razumevanje je trajalo takođe 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 više puta konvertujete između lista i nizova, izmerite ta ukrštanja. Zaustavljanje pre skupog prenosa 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 treba memoriju i pomera 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 kompajlirana Python petlja takođe 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 njenim dekoratorom kompajlera. I dalje piše 'for'. Skup put interpretera se promenio. Nakon kompilacije, ova petlja je trajala manje od pola milisekunde sa spremnim nizovima. Sama kompilacija je trajala oko sekund u ovom pokretanju. Taj trošak podešavanja je isključen iz ponovljenih merenja vremena,
4:22 tako da mali jednokratni posao zaslužuje drugačije poređenje. Numba-in vlastiti vodič rado prikazuje eksplicitne petlje. Pokretanje migracije jezika pre pronalaženja vruće putanje je složen način da se izbegne otvaranje profajlera. Prvo pronađite šta troši vreme. Mrežno čekanje, parsiranje objekata i masovna aritmetika zahtevaju različite popravke. Nativne operacije sa nizovima pomažu kada se izračunavanje i podaci uklapaju.
4:43 Izmerite rezultat koji vaš pozivaoc zapravo treba. Sada za tu prečicu. Imate Python funkciju i želite da prihvati niz. Omotač pod nazivom 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 omotač prvenstveno namenjen pogodnosti. Njegova implementacija je u suštini for petlja. Omotač prihvata grupe, ali nastavlja pozivati vašu Python funkciju za uzastopne elemente. Interfejs je postao kraći. Granica izvršenja je ostala zauzeta. Pogledajte 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 Koristan omotač za emitovanje može sačuvati tačan rad po elementu koji ste pokušavali da uklonite. Proverite implementaciju iza grupnog interfejsa, čuvajte kompatibilne podatke u njihovom korisnom obliku i uključite konverziju u poređenje. Nativne, povratne i kompajlirane petlje mogu izgledati slično sa pozivačeve stolice. Ime vectorize je dalo obećanje koje vaša štoperica nikada nije potpisala. Zadržite ga kada vam je pogodnost ono što vam treba. Za performanse, proverite petlju koja se pokreće.
Šta treba da promenite u svom kodu?
5:41 Moja presuda je SHIP IT za grupisanje izmerene numeričke vruće putanje. Zadržite obične petlje kada već dobro funkcionišu. Proverite gde se posao dešava 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



