+− THE DAILY DIFFdev & AI news
SHIP IT

Výkon Pythonu: nativní smyčky NumPy, zpětná volání a náklady na konverzi

Řízený numerický test pouze na CPU sleduje identické hodnoty prostřednictvím smyček CPython, aritmetiky NumPy, obálky zpětného volání Pythonu a kompilované smyčky Numba.

Want to see our stories first on Google? Add us to your preferred sources — it helps us grow.

Řízený numerický test pouze na CPU sleduje identické hodnoty prostřednictvím smyček CPython, aritmetiky NumPy, obálky zpětného volání Pythonu a kompilované smyčky Numba. NumPy s připraveným vstupem měl velký náskok, zatímco konverze z Python seznamů a zpět snížila tuto výhodu na téměř vyrovnaný stav. Epizoda vysvětluje hranici provádění a jak změřit výsledek, který aplikace potřebuje.

Přečtěte si psané vydání (anglicky) ↗

Co toto video pokrývá

  • CPython kompiluje zdrojový kód do bytecode. Opakovaná práce interpreta/objektů a typovaná nativní numerická smyčka jsou odlišná uspořádání provádění.
  • Numerická aritmetika NumPy může předávat typované dávky kompilovaným operacím; aritmetická smyčka stále existuje a pohledy mohou mít nesouvislé uspořádání.
  • Měřte konverzi a požadovanou reprezentaci výstupu. V tomto běhu konverze Python seznamů na pole a zpět zrušila velký náskok připravených polí.
  • Malé úlohy mohou upřednostňovat běžný Python, protože záleží na fixních nákladech volání polí. Generátorový výraz pro osm hodnot vyhrál toto měřené srovnání.
  • np.vectorize je pohodlná obálka, která volá Python pro po sobě jdoucí prvky; samotné dávkové rozhraní nezajišťuje nativní numerické provádění.
  • Kompilované smyčky se syntaxí Pythonu mohou být také rychlé. Zahrňte náklady na studenou kompilaci/nastavení, pokud jsou pro volajícího důležité.
  • Nativní provádění, CPU SIMD, multithreading a GPU backendy jsou samostatné myšlenky. Dostupné funkce CPU nesledují instrukce použité v konkrétním běhu.

Přeložený přepis

Přeloženo z původního anglického vyprávění. Dostupné audio a titulky jsou řízeny YouTube.

Proč stejné CPU může dokončit dříve?

0:00 Myslíte si, že Python potřebuje nový jazyk, aby byl rychlý. Stejné CPU zpracovalo naši dávku rychleji poté, co se smyčka přesunula. Kam se práce poděla? Kdy zrychlení zmizí? Která zkratka přivede Python zpět? Toto je The Daily Diff, pod kapotou. Sledujte tu zkratku. CPython je standardní implementace Pythonu.

0:17 Kompiluje váš zdrojový kód do bytecode a poté provádí tyto instrukce v interpretru. NumPy je numerická knihovna, jejíž pole uchovávají hodnoty s deklarovaným datovým typem. Jeho vestavěná aritmetika může předat celou dávku kompilovaným operacím. Vezměte milion desetinných čísel, každé z nich umocněte na druhou, poté přidejte původní hodnotu. Každá verze dostane identické hodnoty a produkuje shodné výsledky. Hodnoty jsou dostatečně malé, aby se rozdíly v zaokrouhlování nedostaly do závodu.

0:40 Na tomto stroji trval generátorový výraz asi třicet tři milisekundy. NumPy trval méně než jednu. Oba běžely na CPU. To je užitečný důkaz pro tuto zátěž a hrozná nekrolog pro programovací jazyk. Začněme s obyčejnou smyčkou.

Jakou práci opakuje smyčka v Pythonu?

0:53 Python získá další hodnotu, provede aritmetiku a připojí odpověď. Pak to udělá znovu. Násobení je levné. Příprava dalšího násobení a manipulace s objekty Pythonu kolem něj pokaždé přidává práci. Toto je skutečný disassembler testované funkce. Iterace, násobení a sčítání se objevují jako instrukce bytecode. Verze a interpretry Pythonu činí různá rozhodnutí.

1:14 Tyto časy patří k verzi na obrazovce, což je Python 3.10. Python seznam ukládá odkazy na objekty, takže různé druhy věcí mohou sdílet seznam. Naše aritmetická úloha používá čísla po celou dobu. Platí za obecné uspořádání, zatímco požaduje opakující se službu. Představte si, že si milionkrát objednáváte stejnou kávu a vyplňujete nový formulář pro každý šálek. Kávovar je v pořádku.

1:36 Administrativa si vyvinula vlastní kariéru.

Kam přesouvá NumPy smyčku?

1:38 NumPy mění smlouvu. Toto pole obsahuje desetinná čísla pevné velikosti s jedním deklarovaným typem. Naše pole je souvislé, takže jeho hodnoty leží pohromadě v paměti. Pohledy mohou mít jiná uspořádání. Aritmetická operace ví, jaký druh dat přijímá. Volání předá dávku nativní operaci. Smyčka stále prochází hodnotami uvnitř kompilovaného kódu. Může znovupoužít znalosti o typu napříč touto prací.

2:00 Python zůstává osobou, která práci předává. Itamar Turner Trauring vysvětluje tento rozdíl v Python Speed. Dávková rozhraní, nativní implementace a vektorové instrukce procesoru jsou samostatné věci. Název funkce poskytuje velmi málo důkazů.

Znamená rychlý GPU nebo mnoho jader CPU?

2:13 Tento výsledek použil NumPy na CPU. GPU backend, jako je CuPy, volí jiný domov pro pole a operace. Koupě GPU zanechá tuto obyčejnou smyčku Pythonu běžet tam, kde už běžela. Vektorové instrukce CPU mohou zpracovávat několik hodnot najednou. NumPy má mechanismus pro výběr podporovaných jader. Naše runtime hlásí dostupné funkce, ale nesleduje instrukce tohoto výrazu. Připsání celého naměřeného zisku vektorovým instrukcím by bylo přehnané tvrzení.

2:40 Nákupní košík pro GPU může zůstat zavřený. Je něco hluboce uspokojivého na opravě výkonu, která zároveň ruší nákup. Bohužel, benchmark pokračuje.

Kdy působivé zrychlení zmizí?

2:49 S osmi hodnotami vyhrál generátorový výraz. Volání mechanismu pole stálo více než malá úloha. Větší připravená pole se dostala dopředu. Použili jsme opakované pokusy a zahřátý proces. Novější verze Pythonu mohou změnit bod překročení. Pak změňte, co aplikace potřebuje. Začněte se seznamem Pythonu a skončete s novým seznamem Pythonu. NumPy musí převést vstup na pole a výsledek zpět na objekty Pythonu.

3:11 Tyto kroky patří na účet, pokud je vaše aplikace potřebuje. U milionu hodnot trvala tato kompletní cesta asi dvacet osm milisekund. Generátorový výraz trval také asi dvacet osm. Náš dramatický náskok se stal téměř nerozhodným. Rychlá část byla skutečná. Výhoda závisela na tom, že data zůstala v numerické formě.

Které hranice patří do benchmarku?

3:28 Sledujte data. Pokud další operace přijímá pole, ponechte pole. Pokud opakovaně převádíte mezi seznamy a poli, měřte tato překročení. Zastavení před drahým předáním dává přesná čísla pro špatnou otázku. Násobení tohoto pole vytvoří dočasný výsledek. Přidání původních hodnot vytvoří výstup. Nativní provádění stále potřebuje paměť a přesouvá data.

3:47 Některé operace přijímají výstupní buffer, čímž snižují alokace, když váš kód může bezpečně jej znovu použít. Jedna hezká linka může objednat několik velkých bufferů. Python Speed také upozorňuje na tento problém. Alokátor je pozoruhodně lhostejný k vaší estetice.

Může být kompilovaná smyčka v Pythonu také rychlá?

4:00 Můžete také zachovat syntaxi smyčky a změnit její provedení. Numba může kompilovat podporované numerické funkce Pythonu do nativního kódu. Zde je smyčka z našeho testu s dekorátorem kompilátoru. Stále říká 'for'. Drahá cesta interpreta se změnila. Po kompilaci trvala tato smyčka méně než půl milisekundy s připravenými poli. Samotná kompilace trvala v tomto běhu asi sekundu. Tyto náklady na nastavení byly vyloučeny z opakovaných měření času,

4:22 takže malá jednorázová úloha si zaslouží jiné srovnání. Numbův vlastní průvodce s radostí ukazuje explicitní smyčky. Zahájení migrace jazyka před nalezením horké cesty je propracovaný způsob, jak vyhnout se otevření profilovače. Nejprve zjistěte, co spotřebovává čas. Čekání na síť, parsování objektů a hromadná aritmetika vyžadují odlišné opravy. Nativní operace s poli pomáhají, když se výpočet a data hodí.

4:43 Měřte výsledek, který váš volající skutečně potřebuje. Nyní k té zkratce. Máte funkci Pythonu a chcete, aby přijímala pole. Wrapper jménem vectorize zní jako chybějící tlačítko výkonu. Jeho dokumentace má důležitou větu, která čeká.

Co np.vectorize skutečně provádí?

4:56 NumPy uvádí, že jeho wrapper vectorize je poskytován primárně pro pohodlí. Jeho implementace je v podstatě smyčka 'for'. Wrapper přijímá dávky, ale stále volá vaši funkci Pythonu pro po sobě jdoucí prvky. Rozhraní se zkrátilo. Hranice provádění zůstala zaneprázdněna. Sledujte, jak se stejné hodnoty vracejí přes zpětné volání Pythonu. S připravenými poli trvala tato verze asi osmdesát sedm milisekund. Byla pomalejší než náš generátorový výraz.

5:18 Užitečný broadcasting wrapper může zachovat přesnou práci pro jednotlivé prvky, kterou jste se snažili odstranit. Zkontrolujte implementaci za dávkovým rozhraním, udržujte kompatibilní data v jejich užitečné formě a zahrňte konverzi do srovnání. Nativní, callback a kompilované smyčky mohou z pohledu volajícího vypadat podobně. Název vectorize sliboval něco, co vaše stopky nikdy nepodepsaly. Ponechte si to, když je pohodlí to, co potřebujete. Pro výkon zkontrolujte, která smyčka běží.

Co byste měli změnit ve svém kódu?

5:41 Můj verdikt je SHIP IT pro dávkové zpracování měřené numerické hot path. Ponechte obyčejné smyčky, když už fungují dobře. Zkontrolujte, kde se práce děje a co potřebuje další volající. Přihlaste se k odběru, stiskněte zvoneček a dejte mi vědět v komentářích, zda byste to označili jinak. A to je pro dnešek rozdíl. Jsem Niko z Axrisi. Sloučit zodpovědně.

Zdroje

  1. Universal functions (ufunc) basics — NumPy 2.2NumPy
  2. The N-dimensional array — NumPy 2.2NumPy
  3. numpy.vectorize — convenience and performance notesNumPy
  4. CPU/SIMD optimizations — NumPy 2.2NumPy
  5. dis — Disassembler for Python bytecodePython Software Foundation
  6. CPython 3.10.12 list representationCPython contributors
  7. The limits of Python vectorization as a performance techniqueItamar Turner-Trauring / Python⇒Speed
  8. Performance TipsNumba
  9. Basics of CuPyCuPy

Související videa