Language
Lt :: En

Lecture 7 (2004-10-18)

This page is only available in Lithuanian

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, matomus print 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:

  1. Padaryk, kad programa veiktų.
  2. Padaryk, kad programa veiktų teisingai.
  3. 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:

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.


Valid XHTML 1.1! Valid CSS! Last updated: 2016-09-10