Debuginimas
Yra daug IDE, skirtų Pythonui (IDLE -- ateina su Pythono instaliacija, Eric3, WingIDE (komercinė), ir t.t.). Dalis iš jų turi integruotą debugerį. Bet apie tai aš nekalbėsiu, nes naudoju VIMą.
Pythonas turi standartinį komandų eilutės debugerį pdb standartinėje bibliotekoje. Debugeris rašytas Pythonu, tad galite pasižiūrėti jo source kodą ar prikabinti prie jo kokį nors front-endą (ką visokie IDE ir daro).
Paprasčiausias būdas debuginti programą -- norimoje vietoje įterpti breakpointą:
def my_function_with_a_problem(args, ...): ... ... for ...: if ...: import pdb; pdb.set_trace() # <- paleidžiam debugerį ... ...
Toliau paleidžiate programą konsolėje ir laukiate, kol ji sustos. PDB duoda valdomas komandomis, iš kurių naudingiausios yra
- where (arba bt)
- Parodo programos veikimo steką (call stack)
- list (arba l)
- Parodo programos source kodą (nurodytas eilutes)
- step (arba s)
- Žingsnis į priekį (lenda į kviečiamų funkcijų vidų)
- next (arba n)
- Žingsnis į priekį (nelenda į kviečiamų funkcijų vidų)
- continue (arba c)
- Baigia debuginimą ir leidžia programai veikti toliau
- up (arba u)
- Pakyla programos veikimo steku aukštyn. Tai įtakoja, pvz.,
list
komandą (ji rodo pasirinktos funkcijos source kodą) bei kintamuosius, matomusprint
bei!
komandoms. - down (arba d)
- Nusileidžia programos veikimo steku žemyn. Žr.
up
. - print (arba p)
- Spausdina Python reiškinį
- !
- Vykdo Python sakinį (kuris turi tilpti vienoje eilutėje)
- break (arba b)
- Prideda breakpointą (galima nurodyti failą ir eilutę arba tiesiog funkciją).
- help
- Išvardina ar paaiškina pdb komandas
Kartais debuginti patogiau prikaišiojant print
sakinių į
kodą.
Optimizavimas
Premature optimization is the root of all evil.
-- Donald E. Knuth
Pirmiausia įsitikinkite, kad programą tikrai reikia optimizuoti. Teisinga darbo tvarka:
- Padaryk, kad programa veiktų.
- Padaryk, kad programa veiktų teisingai.
- Padaryk, kad programa veiktų greitai.
Jei jau programa veikia, ir veikia teisingai (ir turi rinkinį testų), bet veikia per lėtai, tuomet galima užsiimti ir optimizacija:
- Reikia susigalvoti benchmarką ir matuoti programos veikimo greitį -- kitaip bus neaišku, kurie kodo pakeitimai situaciją pagerina, o kurie pablogina.
- Reikia programą profiliuoti -- 90% (ar daugiau) veikimo laiko praleidžiama 10% (ar mažiau) kodo. Praktika rodo, kad programuotojų intucija nesugeba teisingai atspėti, kas yra tie 10% kodo.
- Reikia pagalvoti, ar nėra efektyvesnio algoritmo -- esant didesniems duomenų rinkiniams labai gerai optimizuotas bubble sortas stabdys, o visai neoptimizuotas quick sortas -- ne.
Laiko matavimas
Geriau dviračio neišradinėti ir pasinaudoti moduliu timeit:
# mymodule.py def square1(x): return x * x def square2(x): return x ** 2 if __name__ == '__main__': import timeit, mymodule n_times = 100000 timer = timeit.Timer("square1(42)", "from mymodule import square1") square1_best_of_3 = min(timer.repeat(3, n_times)) print "square1: %.3f microseconds" % square1_best_of_3 timer = timeit.Timer("square2(42)", "from mymodule import square2") square2_best_of_3 = min(timer.repeat(3, n_times)) print "square2: %.3f microseconds" % square2_best_of_3
Veikimo pavyzdys:
$ python mymodule.py square1: 0.170 seconds square2: 0.204 seconds
Galima timeit paleisti iš komandų eilutės:
$ python /usr/lib/python2.3/timeit.py -s 'from mymodule import square1' 'square1(42)' 1000000 loops, best of 3: 2.08 usec per loop $ python /usr/lib/python2.3/timeit.py -s 'from mymodule import square2' 'square2(42)' 100000 loops, best of 3: 2.12 usec per loop
Algoritmai
Svarbu parinkti tinkamas duomenų struktūras -- pvz., jei tikrinsite, ar elementas yra aibėje, tai aibei reprezentuoti naudokite sets modulį ar bent jau žodyną, bet ne sąrašą.
Taip pat svarbu pasirinkti efektyvius algoritmus. Labai gerai optimizuotas bubble sortas esant nors kiek didesniam duomenų rinkiniui praloš prieš visai neoptimizuotą quick sortą.
Profiliavimas
90% programos laiko yra praleidžiama 10% programos kodo. Sunku atspėti, kas būtent yra tie 10%. Profiliatorius leidžia tai nustatyti.
Profiliatorius programos veikimo metu skaičiuoja, kiek kartų buvo iškviesta kiekviena funkcija bei kiek laiko ji veikė. Akivaizdu, kad labiau apsimoka optimizuota tas funkcijas, kurios kviečiamos šimtus tūkstančių kartų, nei tas, kurios kviečiamos tris kartus. Kita vertus reikia atsižvelgti ir į funkcijoje praleistą laiką -- jei funkcija kviečiama 10 kartų suryja 90% laiko, geriau optimizuoti ją, nei funkciją, kuri per 100000 iškvietimų suryja 1% laiko.
Python standartinėje bibliotekoje yra du profiliatoriai -- hotshot (rašytas C, greitesnis) bei profile (rašytas pliku Pythonu, lėtesnis). Jų naudojimas panašus. Toliau demonstruosiu hotshot.
Tarkime, kad jūs norite išsiaiškinti, kodėl funkcija do_something iš modulio my_module užtrunka labai ilgai. Pasileiskite Python interpretatorių ir...
>>> import hotshot >>> from my_module import do_something >>> profiler = hotshot.Profile('do_something.prof') >>> profiler.runcall(do_something) >>> profiler.close()
Jei do_something turi argumentų, juos galite perduoti runcall metodui. Jei do_something grąžina reikšmę, ją jums grąžins runcall. Profiliavimo duomenys bus įrašyti į failą do_something.prof. Jų analizė:
>>> import hotshot >>> stats = hotshot.stats.load('do_something.prof') >>> stats.strip_dirs() # mus domina failų vardai be katalogų >>> stats.sort_stats('time', 'calls') # akivaizdu, ką tai daro >>> stats.print_stats(20) # parodys 20 lėčiausių funkcijų
Kiekviena išspausdintos lentelės eilutė atitinka funkciją. Stulpeliai yra
- ncalls
- Kiek kartų ši funkcija buvo kviesta.
- tottime
- Kiek iš viso laiko buvo praleista šioje funkcijoje.
- percall
- tottime padalinta iš ncalls
- cumtime
- Kiek iš viso laiko buvo praleista šioje funkcijoje ir iš jos iškviestose funkcijose.
- percall
- cumtime padalinta iš ncalls
Apsimoka pilną rezultatų lentelę importuoti į kokį speadsheetą ir su ja pažaisti -- parūšiuoti vienaip ar kitaip, pasumuoti, pažiūrėti, kas labiausiai stabdo ir ką labiau apsimoka optimizuoti.
Jei niekas nepadeda
Jei jūsų algoritmo esmė -- gilūs skaičiavimai, kuriuos Pythonas vykdo per lėtai, apsimoka perrašyti skaičiavimus C ar kita žemesnio lygio kalba arba paieškoti bibliotekų, kuriose reikiami skaičiavimai būtų efektyviai realizuoti.
Žmonės su pliku Pythonu ir standartinės bibliotekos moduliais padaro Voice over IP klientus, kuriuose tinklo paketus reikia spėti apdoroti per 20 milisekundžių. Pasidairykite standartinėje bibliotekoje.
Numerical Python yra biblioteka realizuojanti greitus skaičiavimus su dideliais masyvais ar matricomis. Pats tas darbui su grafika.
Psyco yra Just In Time kompiliatorius Pythono kodui.
Pyrex yra į Pythoną panaši kalba skirta rašyti Pythono išplėtimo moduliams. Pyrex kodas su (nebūtinomis) tipų anotacijomis yra verčiamas į C kodą, kurį galima kompiliuoti.