Vsebina
Na tej točki osnovno mehaniko že obvladate: moduli se prevedejo, Python jih uvozi, funkcije delujejo. V realnih projektih se stvari pogosteje lomijo zaradi bolj vsakdanjih razlogov: spremeni se orodna veriga, knjižnic ni mogoče najti ali pa vmesnik nehote postane počasen.
Ta modul je namenjen navadam, ki delujejo tudi zunaj delavnice.
Ponovljivost: pripnite trikotnik (Python, prevajalnik, odvisnosti)
Prevedena razširitev je odvisna od:
- Python ABI-ja (verzija in zastavice gradnje)
- prevajalnika in standardne knjižnice (GCC/Clang, libstdc++)
- povezanih zunanjih knjižnic (Eigen/BLAS/GSL/...)
Če se to med gradnjo in pogonom razlikuje, lahko dobite zmedene ImportError napake.
Praktična navada: najprej določite okolje, nato znotraj njega gradite.
Tri najpogostejše napake (in kako o njih razmišljati)
1) »Module not found«
Če Python ne najde razširitvene datoteke, dobite ModuleNotFoundError.
Običajno je težava v poti iskanja: prevedeni .so ni tam, kjer Python išče module.
2) »Undefined symbol« ali manjkajoče knjižnice
Če Python modul najde, a dinamični nalagalnik ne more razrešiti simbolov, dobite ImportError
z opozorilom o nedefiniranih simbolih ali manjkajočih deljenih knjižnicah.
Najhitrejša diagnostika je pregled odvisnosti:
- Linux:
ldd yourmodule*.so - macOS:
otool -L yourmodule*.so
Tako takoj vidite, kaj modul potrebuje ob zagonu in česa manjka.
3) Predloge (templates) eksplodirajo med prevajanjem
Če dobite ogromna predložna sporočila o napaki, je najpogostejši vzrok manjkajoča glava pybind11,
ki omogoči pretvorbo tipa (npr. pybind11/stl.h za STL vsebnike, pybind11/eigen.h za Eigen).
Razhroščevanje, ki se skaluje
Zanesljiv potek razhroščevanja je:
- V Pythonu izpišite, od kod je modul prišel:
import yourmodule print(yourmodule.__file__) - Preglejte odvisnosti (
ldd/otool -L). - Po potrebi znova prevedite iz čiste gradbene mape v predvidenem okolju.
S tem se izognete naključnemu ugibanju in praviloma hitreje pridete do pravega vzroka.
Intuicija za zmogljivost (kaj optimizirati najprej)
Največja pohitritev pogosto ni mikro-optimizacija C++ kode. Pogosto je ključ v tem, da se izognete zasnovi, kjer mejo Python↔C++ prečkate milijonkrat.
Dober vzorec:
- Python pripravi polja
- en klic v C++ izvede celotno jedro
- Python analizira/riše rezultate
Tvegan vzorec:
- Python ima zanko in na element kliče majhno C++ funkcijo
Pazite tudi na nenamerne kopije:
- Python
list↔std::vectorpomeni pretvorbo/kopiranje - nezvezni NumPy pogledi pogosto prisilijo kopijo, preden jih Eigen lahko porabi
Prava drža je skeptična: merite na realnih vhodih, nato optimizirajte tisto, kar res prevladuje.