Skoči na vsebino

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:

  1. V Pythonu izpišite, od kod je modul prišel:
    import yourmodule
    print(yourmodule.__file__)
    
  2. Preglejte odvisnosti (ldd / otool -L).
  3. 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 liststd::vector pomeni 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.