IlmHamroh
Python kursi/Sifat va vositalar7/8-dars18 daqiqa
Mundarija (22)

18.7-dars: Profiling — cProfile va timeit

18-QISM — SIFAT VA VOSITALAR · 7-dars


1. Kirish va motivatsiya

Kod ishlaydi, lekin sekin. Uni tezlashtirish kerak — lekin qayerdan boshlash kerak? Ko'p dasturchi bu yerda katta xato qiladi: ular kodning qaysi qismi sekinligini taxmin qilib, o'sha yerni optimallashtirishga urinadi. Taxmin ko'pincha noto'g'ri — asosiy sekinlik butunlay boshqa joyda bo'ladi. Profiling (profillash) — kodning qaysi qismi qancha vaqt olishini o'lchaydigan usul. "Taxmin qilma, o'lcha" — optimizatsiyaning birinchi qoidasi.

Python ikki asosiy profiling vositasiga ega: timeit (kichik kod bo'lagini aniq o'lchash) va cProfile (butun dasturda vaqt qayerga ketishini topish).

Real vaziyat. Bir jamoa hisobotlar sekin yaratilayotganidan shikoyat oldi. Dasturchi "bu SQL so'rovlari sekin" deb taxmin qildi va bir hafta so'rovlarni optimallashtirdi — natija deyarli o'zgarmadi. Keyin u cProfile ishlatdi va haqiqiy sekinlikni topdi: kod har hisobot uchun bir xil sozlama faylini minglab marta qayta o'qirdi. Bu bir qatorlik xato edi, lekin vaqtning 90% ini yeb qo'yardi. Uni tuzatib, hisobot 50 barobar tezlashdi — SQL ga umuman tegmasdan. Taxmin bir hafta isrof qildi; o'lchov muammoni bir zumda ko'rsatdi.

Bu darsda kodni profillashni — sekinlikni taxmin emas, o'lchash bilan topishni o'rganamiz.

Bu darsda:

  • "Taxmin qilma, o'lcha" tamoyili
  • timeit — kichik kod bo'lagini o'lchash
  • cProfile — butun dasturni profillash
  • pstats — natijalarni tahlil qilish
  • Natijalarni talqin qilish (ncalls, tottime, cumtime)
  • Erta optimizatsiya — "barcha yovuzlik ildizi"
  • Optimizatsiya ish oqimi
  • Amaliy: sekin kodni tezlashtirish

ℹ timeit va cProfile — standart kutubxonada. Misollarda vaqt o'lchovlari o'zgaruvchan bo'lgani uchun nisbiy taqqoslash va chaqiruvlar soni (deterministik) ko'rsatiladi.


2. Nazariya — chuqur tushuntirish

2.1. "Taxmin qilma, o'lcha"

Optimizatsiyaning birinchi qoidasi — o'lchash:

Taxmin O'lchov
"Bu qism sekin bo'lsa kerak" Aniq raqam
Ko'pincha noto'g'ri Haqiqiy sekinlikni topadi
Vaqt isrofi Optimizatsiyani to'g'ri yo'naltiradi

Dasturchilarning sekinlik haqidagi sezgisi ko'pincha yanglishadi — asosiy sekinlik kutilmagan joyda bo'ladi. Faqat o'lchov haqiqatni ko'rsatadi.

2.2. timeit — mikro-o'lchov

timeit kichik kod bo'lagini ko'p marta ishga tushirib, o'rtacha vaqtini o'lchaydi (bir martalik o'lchov ishonchsiz):

python
import timeit

vaqt = timeit.timeit(lambda: sum(range(1000)), number=10000)
# yoki ikki yondashuvni taqqoslash:
t1 = timeit.timeit("'-'.join(str(i) for i in range(100))", number=10000)
t2 = timeit.timeit("'-'.join([str(i) for i in range(100)])", number=10000)
Parametr Ma'nosi
stmt O'lchanadigan kod (satr yoki funksiya)
number Necha marta takrorlash
setup Bir marta bajarilodigan tayyorlash

timeit ko'p takrorlash bilan tasodifiy shovqinni kamaytiradi — kichik kod bo'laklarini taqqoslash uchun ideal.

2.3. Buyruq qatorida timeit

bash
python -m timeit "'-'.join(str(i) for i in range(100))"
python -m timeit -s "d = {'a': 1}" "d.get('a')"

Tez tekshiruv uchun — kod yozmasdan, terminaldan.

2.4. cProfile — butun dastur

cProfile butun dasturni ishga tushirib, har funksiya qancha vaqt olganini va necha marta chaqirilganini yozadi:

python
import cProfile
cProfile.run("main()")

# yoki buyruq qatorida:
# python -m cProfile -s cumtime skript.py

Namunaviy chiqish:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1000    0.500    0.001    0.800    0.001 hisob.py:10(sekin_funksiya)
    50000    0.300    0.000    0.300    0.000 hisob.py:5(yordamchi)

cProfile — sekinlikni topish vositasi: qaysi funksiya eng ko'p vaqt oladi va necha marta chaqiriladi.

2.5. Natijalarni talqin qilish

Ustun Ma'nosi
ncalls Funksiya necha marta chaqirilgan
tottime Funksiyaning o'zida sarflangan vaqt (ichki chaqiruvlarsiz)
cumtime Jami vaqt (ichki chaqiruvlar bilan)
percall Bir chaqiruvga vaqt

tottime — funksiyaning o'zi sekin (optimallashtiring); cumtime — u chaqirgan narsalar sekin. ncalls juda katta bo'lsa — funksiya ortiqcha chaqirilyapti (masalan sikl ichida).

2.6. pstats — tahlil

python
import cProfile, pstats

pr = cProfile.Profile()
pr.enable()
main()
pr.disable()

stats = pstats.Stats(pr)
stats.sort_stats("cumtime")     # jami vaqt bo'yicha saralash
stats.print_stats(10)           # eng sekin 10 ta
Saralash Foydasi
cumtime Jami vaqt (yuqoridan boshlab)
tottime O'z vaqti (bevosita sekin)
ncalls Chaqiruvlar soni (ortiqcha chaqiruv)

2.7. Erta optimizatsiya

Donald Knuth: "Erta optimizatsiya — barcha yovuzlik ildizi". Bu:

Erta optimizatsiya To'g'ri yondashuv
Yozayotib har qatorni optimallashtirish Avval to'g'ri, o'qiladigan kod
Taxmin bilan tezlashtirish Profillab, hotspot'ni topish
Arzimas qismni optimallashtirish Faqat haqiqiy sekinlikni
O'qib bo'lmas "tez" kod Toza kod, keyin zarur bo'lsa optimallashtirish

Qoida: avval ishlaydigan, toza kod yozing; sekin bo'lsa, profillang; faqat hotspot'ni optimallashtiring. Kodning 90% i tezlikka ta'sir qilmaydi — 10% hotspot'ga e'tibor bering.

2.8. Optimizatsiya ish oqimi

1. To'g'ri, toza kod yoz
2. Sekinmi? → profilla (cProfile)
3. Hotspot'ni top (eng ko'p vaqt/chaqiruv)
4. Faqat o'shani optimallashtir
5. Qayta o'lcha (yaxshilandi mi?)
6. Testlar hali o'tadimi? (17-qism)

Har optimizatsiyadan keyin qayta o'lchang — ba'zi "optimizatsiya" aslida sekinlashtiradi. Va testlar hali o'tishini tekshiring — tezlik uchun to'g'rilikni qurbon qilmang.


3. Tez ma'lumotnoma

python
import timeit, cProfile, pstats

# mikro-o'lchov
timeit.timeit(lambda: kod(), number=10000)

# butun dastur
cProfile.run("main()")

# tahlil
pr = cProfile.Profile(); pr.enable(); main(); pr.disable()
pstats.Stats(pr).sort_stats("cumtime").print_stats(10)
bash
python -m timeit "kod"
python -m cProfile -s cumtime skript.py

Qoidalar

taxmin qilma, o'lcha
timeit — kichik kod (ko'p takrorlash)
cProfile — butun dastur (hotspot topish)
tottime — o'z vaqti; cumtime — jami; ncalls — chaqiruvlar
erta optimizatsiya — yovuzlik ildizi
profilla → hotspot → optimallashtir → qayta o'lcha

4. Batafsil misollar

Vaqt o'lchovlari o'zgaruvchan bo'lgani uchun misollarda nisbiy taqqoslash va chaqiruvlar soni (deterministik) ko'rsatiladi.

Misol 1 — timeit bilan taqqoslash

python
"""timeit kichik kod bo'laklarini o'lchaydi; ikki yondashuvni taqqoslash (nisbiy); ko'p takrorlash shovqinni kamaytiradi; taxmin emas, o'lchov."""

import timeit


def main() -> None:
    print("=== 1. Ikki yondashuvni taqqoslash ===")
    # ro'yxat tushunchasi vs generator (join uchun)
    t_gen = timeit.timeit("'-'.join(str(i) for i in range(100))", number=20000)
    t_list = timeit.timeit("'-'.join([str(i) for i in range(100)])", number=20000)
    print(f"  ro'yxat tushunchasi generatordan tez (join uchun): {t_list < t_gen}")

    print("\n=== 2. Satr birlashtirish usullari ===")
    setup = "sonlar = list(range(1000))"
    t_join = timeit.timeit("''.join(str(s) for s in sonlar)", setup=setup, number=5000)
    t_plus = timeit.timeit("""
natija = ''
for s in sonlar:
    natija += str(s)
""", setup=setup, number=5000)
    print(f"  join += dan tez: {t_join < t_plus}")

    print("\n=== 3. Lug'at kirish usullari ===")
    setup = "d = {i: i for i in range(1000)}"
    t_in = timeit.timeit("500 in d", setup=setup, number=100000)
    t_keys = timeit.timeit("500 in d.keys()", setup=setup, number=100000)
    print(f"  'x in d' 'x in d.keys()' dan tez: {t_in < t_keys}")

    print("\n=== 4. To'plam vs ro'yxatda qidirish ===")
    setup_list = "kolleksiya = list(range(10000))"
    setup_set = "kolleksiya = set(range(10000))"
    t_list = timeit.timeit("9999 in kolleksiya", setup=setup_list, number=10000)
    t_set = timeit.timeit("9999 in kolleksiya", setup=setup_set, number=10000)
    print(f"  set'da qidirish ro'yxatdan tez: {t_set < t_list}")
    print("  ⭐ o'lchov sezgini tasdiqladi: set — O(1), ro'yxat — O(n)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ikki yondashuvni taqqoslash ===
  ro'yxat tushunchasi generatordan tez (join uchun): True

=== 2. Satr birlashtirish usullari ===
  join += dan tez: True

=== 3. Lug'at kirish usullari ===
  'x in d' 'x in d.keys()' dan tez: True

=== 4. To'plam vs ro'yxatda qidirish ===
  set'da qidirish ro'yxatdan tez: True
  ⭐ o'lchov sezgini tasdiqladi: set — O(1), ro'yxat — O(n)

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — cProfile bilan hotspot topish

python
"""cProfile har funksiya chaqiruvlar sonini va vaqtini yozadi; ncalls deterministik; ortiqcha chaqirilgan funksiyani (hotspot) topish."""

import cProfile
import pstats


def yordamchi(x):
    return x * x


def sekin_funksiya(n):
    # yordamchi ni ko'p marta chaqiradi
    return sum(yordamchi(i) for i in range(n))


def ish():
    natija = 0
    for _ in range(50):
        natija += sekin_funksiya(100)
    return natija


def main() -> None:
    pr = cProfile.Profile()
    pr.enable()
    ish()
    pr.disable()

    stats = pstats.Stats(pr)

    print("=== 1. Funksiya chaqiruvlari soni (deterministik) ===")
    chaqiruvlar = {}
    for func, (cc, nc, tt, ct, callers) in stats.stats.items():
        nom = func[2]
        if nom in ("yordamchi", "sekin_funksiya", "ish"):
            chaqiruvlar[nom] = nc
    for nom in ("ish", "sekin_funksiya", "yordamchi"):
        print(f"  {nom:18} {chaqiruvlar[nom]:6} marta chaqirilgan")

    print("\n=== 2. Talqin ===")
    print(f"  ish: 1 marta (kirish nuqtasi)")
    print(f"  sekin_funksiya: 50 marta (sikl ichida)")
    print(f"  yordamchi: 50 × 100 = 5000 marta (ENG KO'P — hotspot)")
    print("  ⭐ yordamchi eng ko'p chaqirilyapti — optimizatsiya shu yerga")

    print("\n=== 3. Jami chaqiruvlar ===")
    jami = sum(nc for func, (cc, nc, tt, ct, callers) in stats.stats.items())
    print(f"  jami funksiya chaqiruvlari: {jami}")
    print("  ⭐ ncalls — deterministik: har run bir xil (vaqt esa o'zgaradi)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Funksiya chaqiruvlari soni (deterministik) ===
  ish                     1 marta chaqirilgan
  sekin_funksiya         50 marta chaqirilgan
  yordamchi            5000 marta chaqirilgan

=== 2. Talqin ===
  ish: 1 marta (kirish nuqtasi)
  sekin_funksiya: 50 marta (sikl ichida)
  yordamchi: 50 × 100 = 5000 marta (ENG KO'P — hotspot)
  ⭐ yordamchi eng ko'p chaqirilyapti — optimizatsiya shu yerga

=== 3. Jami chaqiruvlar ===
  jami funksiya chaqiruvlari: 10152
  ⭐ ncalls — deterministik: har run bir xil (vaqt esa o'zgaradi)

Nima ko'rsatdi: 2.4, 2.5-bo'limlar.

Misol 3 — pstats bilan tahlil va tottime vs cumtime

python
"""pstats natijalarni saralaydi va filtrlaydi; tottime (o'z vaqti) vs cumtime (jami); chaqiruvchi/chaqirilgan; deterministik chaqiruv sonlari."""

import cProfile
import pstats


def past_daraja(x):
    return x + 1


def orta_daraja(n):
    return sum(past_daraja(i) for i in range(n))


def yuqori_daraja():
    jami = 0
    for _ in range(20):
        jami += orta_daraja(50)
    return jami


def main() -> None:
    pr = cProfile.Profile()
    pr.enable()
    yuqori_daraja()
    pr.disable()

    stats = pstats.Stats(pr)

    print("=== 1. Chaqiruvlar iyerarxiyasi ===")
    sonlar = {}
    for func, (cc, nc, tt, ct, callers) in stats.stats.items():
        nom = func[2]
        if nom in ("yuqori_daraja", "orta_daraja", "past_daraja"):
            sonlar[nom] = nc
    for nom, kutilgan in [("yuqori_daraja", 1), ("orta_daraja", 20), ("past_daraja", 1000)]:
        print(f"  {nom:16} {sonlar[nom]:5} marta (kutilgan: {kutilgan})")

    print("\n=== 2. tottime vs cumtime tushunchasi ===")
    print("  yuqori_daraja: tottime kichik (o'zi kam ish), cumtime katta (hammani chaqiradi)")
    print("  past_daraja: tottime = cumtime (ichki chaqiruv yo'q, o'zi ishlaydi)")
    print("  ⭐ tottime katta → funksiya O'ZI sekin; cumtime katta → chaqirganlari sekin")

    print("\n=== 3. Chaqiruvchilar (callers) ===")
    for func, (cc, nc, tt, ct, callers) in stats.stats.items():
        if func[2] == "past_daraja":
            chaqiruvchilar = [f[2] for f in callers]
            print(f"  past_daraja ni chaqirgan: {chaqiruvchilar}")

    print("\n=== 4. Saralash (deterministik tekshiruv) ===")
    # ncalls bo'yicha eng ko'p chaqirilgan (past_daraja)
    eng_kop = max(
        ((func[2], nc) for func, (cc, nc, tt, ct, callers) in stats.stats.items()
         if func[2] in ("yuqori_daraja", "orta_daraja", "past_daraja")),
        key=lambda x: x[1])
    print(f"  eng ko'p chaqirilgan: {eng_kop[0]} ({eng_kop[1]} marta)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Chaqiruvlar iyerarxiyasi ===
  yuqori_daraja        1 marta (kutilgan: 1)
  orta_daraja         20 marta (kutilgan: 20)
  past_daraja       1000 marta (kutilgan: 1000)

=== 2. tottime vs cumtime tushunchasi ===
  yuqori_daraja: tottime kichik (o'zi kam ish), cumtime katta (hammani chaqiradi)
  past_daraja: tottime = cumtime (ichki chaqiruv yo'q, o'zi ishlaydi)
  ⭐ tottime katta → funksiya O'ZI sekin; cumtime katta → chaqirganlari sekin

=== 3. Chaqiruvchilar (callers) ===
  past_daraja ni chaqirgan: ['<genexpr>']

=== 4. Saralash (deterministik tekshiruv) ===
  eng ko'p chaqirilgan: past_daraja (1000 marta)

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.

Misol 4 — Amaliy: sekin kodni tezlashtirish

Real optimizatsiya ish oqimini ko'rsatamiz: sekin funksiyani profillaymiz, hotspot'ni (ortiqcha ish) topamiz, optimallashtirib qayta o'lchaymiz va natijaning to'g'riligini tekshiramiz. Bu — "taxmin qilma, o'lcha, optimallashtir, qayta o'lcha" siklining namunasi.

python
"""optimizatsiya sikli: sekin kodni profillash, hotspot topish (takroriy hisob), keshlash bilan tezlashtirish, qayta o'lchash, to'g'rilikni tekshirish."""

import cProfile
import pstats
import timeit
from functools import lru_cache


# SEKIN: har chaqiruvda qaytadan hisoblaydi
def fib_sekin(n):
    if n < 2:
        return n
    return fib_sekin(n - 1) + fib_sekin(n - 2)


# TEZ: keshlash bilan (10-dars, functools)
@lru_cache(maxsize=None)
def fib_tez(n):
    if n < 2:
        return n
    return fib_tez(n - 1) + fib_tez(n - 2)


def main() -> None:
    print("=== 1. Sekin versiyani profillash ===")
    pr = cProfile.Profile()
    pr.enable()
    fib_sekin(25)
    pr.disable()
    stats = pstats.Stats(pr)
    for func, (cc, nc, tt, ct, callers) in stats.stats.items():
        if func[2] == "fib_sekin":
            print(f"  fib_sekin(25) uchun chaqiruvlar: {nc}")
            print("  ⚠️ eksponensial — bir xil qiymat qayta-qayta hisoblanadi (hotspot)")

    print("\n=== 2. Tez versiya (keshlash bilan) ===")
    fib_tez.cache_clear()
    pr = cProfile.Profile()
    pr.enable()
    fib_tez(25)
    pr.disable()
    stats = pstats.Stats(pr)
    for func, (cc, nc, tt, ct, callers) in stats.stats.items():
        if func[2] == "fib_tez":
            print(f"  fib_tez(25) uchun chaqiruvlar: {nc}")
            print("  ⭐ har qiymat FAQAT BIR MARTA hisoblanadi (kesh)")

    print("\n=== 3. Nisbiy tezlik o'lchovi ===")
    fib_tez.cache_clear()
    t_sekin = timeit.timeit(lambda: fib_sekin(25), number=5)
    t_tez = timeit.timeit(lambda: fib_tez(25), number=5)
    print(f"  keshli versiya sekindan tezroq: {t_tez < t_sekin}")

    print("\n=== 4. To'g'rilikni tekshirish (17-qism) ===")
    fib_tez.cache_clear()
    barcha_teng = all(fib_sekin(n) == fib_tez(n) for n in range(15))
    print(f"  ikki versiya bir xil natija beradi: {barcha_teng}")
    print("  ⭐ optimizatsiya tezlashtirdi, LEKIN to'g'rilikni buzmadi")

    print("\n=== 5. Xulosa ===")
    print("  profilla (hotspot) → optimallashtir (kesh) → qayta o'lcha → testlar o'tadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Sekin versiyani profillash ===
  fib_sekin(25) uchun chaqiruvlar: 242785
  ⚠️ eksponensial — bir xil qiymat qayta-qayta hisoblanadi (hotspot)

=== 2. Tez versiya (keshlash bilan) ===
  fib_tez(25) uchun chaqiruvlar: 26
  ⭐ har qiymat FAQAT BIR MARTA hisoblanadi (kesh)

=== 3. Nisbiy tezlik o'lchovi ===
  keshli versiya sekindan tezroq: True

=== 4. To'g'rilikni tekshirish (17-qism) ===
  ikki versiya bir xil natija beradi: True
  ⭐ optimizatsiya tezlashtirdi, LEKIN to'g'rilikni buzmadi

=== 5. Xulosa ===
  profilla (hotspot) → optimallashtir (kesh) → qayta o'lcha → testlar o'tadi

Nima ko'rsatdi: 2.4, 2.7, 2.8-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Sekinlikni sezaman" Sezgi ko'pincha yanglishadi — o'lchang
"Har qatorni optimallashtirish kerak" Faqat hotspot'ni (10%)
"Bir martalik o'lchov yetarli" timeit — ko'p takrorlash
"tottime va cumtime bir xil" tottime — o'z vaqti, cumtime — jami
"Optimizatsiya doim tezlashtiradi" Ba'zan sekinlashtiradi — qayta o'lchang
"Erta optimizatsiya yaxshi" "Barcha yovuzlik ildizi" — avval toza kod
"Tez kod = yaxshi kod" To'g'rilik va o'qilishlik muhimroq
"cProfile vaqtni aniq beradi" Vaqt o'zgaruvchan; chaqiruvlar deterministik

6. Keng tarqalgan xatolar va yechimlari

1. Taxmin bilan optimallashtirish

python
# "bu sekin bo'lsa kerak" → optimallashtirish   # ❌
cProfile.run("main()")                          # ✅ o'lchang

2. Bir martalik time.time() o'lchovi

python
t = time.time(); kod(); print(time.time() - t)  # ❌ shovqinli
timeit.timeit(lambda: kod(), number=10000)       # ✅ ko'p takrorlash

3. Arzimas qismni optimallashtirish

python
# kamdan-kam ishlaydigan kodni tezlashtirish      # ❌
# ✅ hotspot (eng ko'p vaqt/chaqiruv) ga e'tibor

4. Optimizatsiyadan keyin qayta o'lchamaslik

python
# "optimallashtirdim" (lekin tekshirmadim)        # ❌
# ✅ profilla → optimallashtir → QAYTA profilla

5. To'g'rilikni qurbon qilish

python
# tez, lekin noto'g'ri natija                       # ❌
# ✅ testlar hali o'tishini tekshiring (17-qism)

6. Erta optimizatsiya

python
# yozayotib har qatorni tezlashtirish              # ❌ o'qib bo'lmas kod
# ✅ avval toza kod, keyin (zarur bo'lsa) profilla

7. ncalls ni e'tiborsiz qoldirish

python
# faqat vaqtga qarash                              # ncalls juda katta bo'lsa
# ✅ ortiqcha chaqiruvlar (sikl ichida) — hotspot belgisi

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 10-dars (o'tilgan): functools.lru_cache — keshlash bilan optimizatsiya
  • 5-qism (o'tilgan): ma'lumot tuzilmalari — set vs list tezligi
  • 11-qism (o'tilgan): xotira — vaqt va xotira muvozanati
  • 17-qism (o'tilgan): testlar — optimizatsiyadan keyin to'g'rilikni tekshirish
  • 29-qism: miqyoslash — profiling katta tizimlarda

8. Eng yaxshi amaliyotlar

  1. Taxmin qilma, o'lcha — cProfile bilan hotspot'ni toping.

  2. timeit bilan kichik kod bo'laklarini ko'p takrorlashda o'lchang.

  3. Faqat hotspot'ni (eng ko'p vaqt/chaqiruv) optimallashtiring.

  4. Avval toza, to'g'ri kod; optimizatsiya keyin (zarur bo'lsa).

  5. Har optimizatsiyadan keyin qayta o'lchang.

  6. tottime (o'z vaqti) va cumtime (jami) farqini tushuning.

  7. ncalls juda katta bo'lsa — ortiqcha chaqiruvni qidiring.

  8. Optimizatsiyadan keyin testlar (17-qism) hali o'tishini tekshiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # timeit number parametri nima uchun?
2.  # cProfile ncalls nima?
3.  # tottime va cumtime farqi?
4.  # 'x in list' va 'x in set' qaysi tez?
5.  # profiling birinchi qoidasi?
6.  # ncalls juda katta nimani bildiradi?
7.  # "erta optimizatsiya" haqida Knuth nima dedi?
8.  # optimizatsiyadan keyin nima qilish kerak?
9.  # python -m cProfile nima qiladi?
10. # timeit bir martalik o'lchovmi?
11. # kodning necha % i odatda hotspot?
12. # cumtime katta funksiya nima uchun sekin?
Javoblar
  1. Ko'p takrorlab shovqinni kamaytirish
  2. Funksiya necha marta chaqirilgan
  3. tottime — o'z vaqti, cumtime — jami (ichki chaqiruvlar bilan)
  4. x in set (O(1) vs O(n))
  5. Taxmin qilma, o'lcha
  6. Funksiya ortiqcha chaqirilyapti (sikl ichida)
  7. "Erta optimizatsiya — barcha yovuzlik ildizi"
  8. Qayta o'lchash (yaxshilandimi) va testlar
  9. Skriptni profillaydi (buyruq qatorida)
  10. Yo'q — ko'p takrorlash
  11. ~10%
  12. U chaqirgan narsalar sekin (o'zi emas)

Vazifa 2: Xatolarni tuzating

python
1.  import time
    t = time.time()
    natija = hisobla()
    print(time.time() - t)

2.  # "bu SQL sekin" deb taxmin qilib
    # bir hafta SQL optimallashtirish

3.  def fib(n):
        if n < 2: return n
        return fib(n-1) + fib(n-2)   # sekin, lekin profilsiz

4.  # optimallashtirdim (lekin qayta o'lchamadim)

5.  # tez versiya yozdim (lekin test qilmadim)
Javoblar
python
1.  import timeit
    vaqt = timeit.timeit(lambda: hisobla(), number=1000)   # ko'p takror

2.  cProfile.run("hisobot_yarat()")   # avval profilla, hotspot'ni top

3.  @lru_cache(maxsize=None)          # profillab, keshlash qo'shildi
    def fib(n):
        if n < 2: return n
        return fib(n-1) + fib(n-2)

4.  cProfile.run("main()")            # optimizatsiyadan keyin qayta o'lcha

5.  assert tez_versiya(n) == sekin_versiya(n)   # to'g'rilikni tekshir

Vazifa 3: Benchmark to'plami

Turli yondashuvlarni timeit bilan taqqoslang:

  1. Ro'yxat tushunchasi vs map vs sikl
  2. str.join vs += satr birlashtirish
  3. dict vs list da qidirish
  4. Natijalarni jadval qilib chiqaring (nisbiy)

Vazifa 4: Profillash mashqi

Sekin funksiyani profillang:

  1. cProfile bilan hotspot'ni toping
  2. ncalls, tottime, cumtime ni tahlil qiling
  3. Ortiqcha chaqiruvni aniqlang
  4. Optimallashtirib qayta profillang

Vazifa 5: Optimizatsiya sikli

Ma'lum sekin kod uchun:

  1. Profillang, hotspot'ni yozing
  2. Uch xil optimizatsiya sinang
  3. Har biridan keyin qayta o'lchang
  4. Testlar hali o'tishini tasdiqlang

Vazifa 6: line_profiler

line_profiler (tashqi) bilan:

  1. Funksiyani qator-baqator profillang
  2. Eng sekin qatorni toping (cProfile funksiya darajasida)
  3. cProfile bilan solishtiring
  4. Qachon qaysi biri foydali — yozing

Vazifa 7: O'ylash

Donald Knuth ning "erta optimizatsiya — barcha yovuzlik ildizi" iqtibosi ko'pincha noto'g'ri tushuniladi — u optimizatsiyaga qarshi emas, bevaqt optimizatsiyaga qarshi edi. To'liq iqtibos: "kichik samaradorlikni 97% vaqtda unutishimiz kerak: erta optimizatsiya — barcha yovuzlik ildizi. Lekin qolgan muhim 3% da imkoniyatni qo'ldan bermasligimiz kerak". Nima uchun optimizatsiya vaqti muhim, va "qachon optimallashtirish" savoli "qanday optimallashtirish" dan muhimroqmi?

Javob

Qisqa javob: Optimizatsiya vaqti muhim, chunki bevaqt optimizatsiya ikki narsani buzadi: kod o'qilishligini (tez lekin murakkab) va dasturchi vaqtini (arzimas qismga sarflangan). Knuth'ning asosiy fikri — optimizatsiya maqsadli bo'lishi kerak: avval to'g'ri, toza kod, keyin o'lchov bilan haqiqiy muhim 3% ni topib, faqat o'shani optimallashtirish. "Qachon" savoli "qanday" dan muhimroq, chunki noto'g'ri vaqtda (yoki noto'g'ri joyda) qilingan eng yaxshi optimizatsiya ham foydasiz.

1. Nega bevaqt optimizatsiya zararli

Zarar Tafsilot
O'qilishlik Tez kod ko'pincha murakkab
Vaqt isrofi Arzimas qismga sarflangan
Xatolar Murakkab kod — ko'proq xato
Noto'g'ri joy Sezgi yanglishadi
Erta qaror Talab hali aniq emas

2. Knuth'ning to'liq fikri

Iqtibos optimizatsiyaga qarshi emas — bevaqt ga qarshi:

  • 97% vaqt: kichik samaradorlikni unuting (toza kod)
  • 3% muhim: imkoniyatni qo'ldan bermang (o'lchab optimallashtiring)

3. "Qachon" > "qanday"

Savol Nega muhim
Qachon optimallashtirish To'g'ri vaqt — muammo mavjud va o'lchangan
Qayerda Hotspot — o'lchov ko'rsatadi
Qanday Faqat qachon/qayerda aniq bo'lgach

Eng yaxshi optimizatsiya noto'g'ri joyda foydasiz.

4. To'g'ri tartib

  1. To'g'ri, toza, o'qiladigan kod
  2. Ishlaydimi? (testlar)
  3. Sekinmi? (o'lchov)
  4. Qayerda? (profiling)
  5. Faqat o'shani optimallashtir
  6. Qayta o'lcha

5. Muhandislik saboqlari

  1. Optimizatsiya — o'lchovga asoslangan, maqsadli
  2. O'qilishlik va to'g'rilik — birinchi
  3. "Qachon/qayerda" — "qanday" dan oldin
  4. Ko'p kod tezlikka ta'sir qilmaydi (97%)

6. Xulosa

  1. Bevaqt optimizatsiya o'qilishlik va vaqtni buzadi
  2. Knuth maqsadli optimizatsiyani qo'llab-quvvatlaydi
  3. "Qachon/qayerda" — o'lchov bilan aniqlanadi
  4. Toza kod → o'lchov → maqsadli optimizatsiya

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda kodni profillashni — sekinlikni o'lchash bilan topishni o'rgandik.

Eng muhim uch fikr:

  1. "Taxmin qilma, o'lcha." Optimizatsiyaning birinchi qoidasi — kodning qaysi qismi sekinligini taxmin qilmaslik, balki o'lchash. Dasturchilarning sezgisi ko'pincha yanglishadi — asosiy sekinlik kutilmagan joyda bo'ladi. timeit kichik kod bo'laklarini ko'p takrorlash bilan aniq o'lchaydi (bir martalik o'lchov shovqinli), cProfile esa butun dasturda vaqt qayerga ketishini va har funksiya necha marta chaqirilganini ko'rsatadi.

  2. cProfile va natijalarni talqin. cProfile har funksiyaning ncalls (chaqiruvlar soni — deterministik), tottime (o'z vaqti, ichki chaqiruvlarsiz) va cumtime (jami vaqt) ni beradi. tottime katta — funksiya o'zi sekin; cumtime katta — u chaqirgan narsalar sekin; ncalls juda katta — funksiya ortiqcha (sikl ichida) chaqirilyapti. pstats bilan natijalarni saralab, filtrlab, hotspot'ni topasiz.

  3. Erta optimizatsiya — "barcha yovuzlik ildizi". Avval to'g'ri, toza, o'qiladigan kod yozing; sekin bo'lsa, profillang; faqat hotspot'ni (kodning ~10% i) optimallashtiring. Ish oqimi: profilla → hotspot → optimallashtir → qayta o'lcha (ba'zi "optimizatsiya" aslida sekinlashtiradi) → testlar hali o'tadimi (tezlik uchun to'g'rilikni qurbon qilmang). Knuth'ning fikri — optimizatsiyaga qarshi emas, bevaqt optimizatsiyaga qarshi.

Keyingi darsda — 18-qismning yakuniy darsi: debugging (pdb va IDE) — koddagi xatolarni print bilan emas, tuzatgich (debugger) bilan tizimli topish: to'xtash nuqtalari, qadam-baqadam ijro va o'zgaruvchilarni tekshirish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
18.7-dars: Profiling — cProfile va timeit — IlmHamroh