IlmHamroh
Python kursi/Kompyuter va dasturlash4/8-dars14 daqiqa
Mundarija (24)

1.4-dars: Kompilyator, interpretator va JIT — kod qanday bajariladi

1-QISM — KOMPYUTER VA DASTURLASH · 4-dars · Oldingi dars: dasturlash tillari va Python o'rni


1. Kirish va motivatsiya

2-darsda kompilyator va interpretator o'rtasidagi asosiy farqni ko'rdik: biri oldindan tarjima qiladi, ikkinchisi ishlash paytida.

Bu tushuntirish boshlash uchun yetarli edi. Lekin u jiddiy soddalashtirish. Haqiqatda:

  • Java kompilyatsiya qilinadi — lekin mashina kodiga emas
  • JavaScript interpretatsiya qilinadi — lekin brauzerda u mashina kodiga aylantiriladi
  • Python interpretatsiya qilinadi — lekin avval kompilyatsiya qilinadi
  • Ba'zi tillar umuman boshqa tilga o'giriladi

Bu chalkashlikka o'xshaydi. Aslida esa bu spektr — qora va oq emas, oralig'ida ko'p tus bor.

Bu darsni tugatgach quyidagilarni bilasiz:

  • JIT nima va nega PyPy oddiy Pythondan 5–10 barobar tez
  • Nega Java "bir marta yoz, hamma joyda ishlat" deydi va Python ham xuddi shunday
  • Transpiler nima (TypeScript → JavaScript bu qanday ishlaydi)
  • Nega Python jamoasi bytecode'ni tanlagan va bu qanday narx bilan keldi
  • Nega numpy tez, sof Python esa sekin — aniq sabab, "chunki C" emas

Bu bilim keyinchalik unumdorlik muammosini yechishda hal qiluvchi bo'ladi. "Python sekin" — bu javob emas. Nega sekin va qayerda tez qilish mumkin — bu javob.


2. Nazariya — chuqur tushuntirish

2.1. Uch savol, uch o'lchov

Tillarni "kompilyatsiya qilinadi / interpretatsiya qilinadi" deb ikkiga bo'lish noto'g'ri. To'g'ri savol — uchta:

  1. Nimaga tarjima qilinadi? Mashina kodigami, oraliq ko'rinishgami, boshqa tilgami?
  2. Qachon tarjima qilinadi? Oldindanmi, ishlash paytidami, aralashmi?
  3. Kim bajaradi? Protsessormi, virtual mashinami?

Har bir tilni shu uch o'lcham bo'yicha joylashtirish mumkin.

2.2. AOT — oldindan kompilyatsiya

AOT (Ahead-Of-Time) — dastur ishga tushishidan oldin to'liq mashina kodiga o'giriladi.

text
main.c  →  [kompilyator]  →  main.exe  →  protsessor bajaradi
        (bir marta, oldin)              (har safar, to'g'ridan-to'g'ri)

Tillar: C, C++, Rust, Go, Fortran.

Ustunliklari:

  • Eng tez bajarilish — oradagi qatlam yo'q
  • Kompilyator butun dasturni ko'radi va chuqur optimizatsiya qila oladi
  • Tayyor faylni tarqatasiz, foydalanuvchida hech narsa o'rnatilishi shart emas

Kamchiliklari:

  • Har bir platforma uchun alohida kompilyatsiya (Windows, macOS, Linux — uchta fayl)
  • Kod o'zgarsa qayta kompilyatsiya kutish kerak (katta loyihada bu daqiqalar)
  • Ishlash paytidagi haqiqiy ma'lumotni bilmaydi, faqat taxmin qiladi

Oxirgi nuqta muhim — u JIT ning mavjud bo'lish sababi.

2.3. Sof interpretatsiya

Interpretator manba kodni qatorma-qator o'qib, darhol bajaradi. Oraliq fayl yaratmaydi.

text
main.sh  →  [interpretator o'qiydi va bajaradi]

Bu eng sodda yondashuv. Bash skriptlari, eski BASIC shunday ishlagan.

Muammosi: sikl ichidagi qator har safar qayta tahlil qilinadi. 1000 marta aylanadigan siklda bir xil qator 1000 marta o'qib, tushunilib, bajariladi. Bu isrofgarchilik.

Aynan shu muammoni yechish uchun oraliq ko'rinish o'ylab topilgan.

2.4. Bytecode va virtual mashina — Python yo'li

Python (va Java, C#) o'rta yo'lni tanlagan:

text
main.py  →  [kompilyator]  →  bytecode  →  [virtual mashina]  →  bajariladi
            (bir marta)                     (har safar)

Bytecode — mashina kodi emas. U virtual mashina deb ataladigan dastur uchun mo'ljallangan buyruqlar to'plami.

Nega bu yaxshi?

  1. Tahlil bir marta. Manba kod bir marta tahlil qilinadi, natija saqlanadi (__pycache__).
  2. Platformadan mustaqil. Bir xil bytecode Windows'da ham, Linux'da ham ishlaydi.
  3. Kompilyatsiya tez. Bytecode'ga o'girish mashina kodiga o'girishdan ancha oson.

Nima uchun baribir sekin?

Chunki virtual mashina har bir bytecode buyrug'ini talqin qiladi. Bu qo'shimcha qatlam. Protsessor to'g'ridan-to'g'ri bajarish o'rniga, avval "bu buyruq nima?" deb qaraydi.

Taxminiy taqqoslash:

text
C:      son += 1   →  1 ta protsessor buyrug'i
Python: son += 1   →  ~4 ta bytecode buyrug'i,
                      har biri VM tomonidan talqin qilinadi,
                      har birida obyekt turi tekshiriladi

Farq shu yerdan kelib chiqadi.

2.5. Dinamik turlash — sekinlikning asosiy sababi

Ko'p odam "Python sekin, chunki interpretatsiya qilinadi" deb o'ylaydi. Bu yarim haqiqat. Asosiy sabab boshqa — dinamik turlash.

C'da:

c
int a = 5;
int b = 3;
int c = a + b;   // Kompilyator BILADI: bular int.
                 // Bitta protsessor buyrug'i yetarli.

Pythonda:

python
a = 5
b = 3
c = a + b        # Python ISHLASH PAYTIDA bilib oladi:
                 # 1. a nima turda?
                 # 2. b nima turda?
                 # 3. bu turlar uchun + qanday ishlaydi?
                 # 4. natija uchun yangi obyekt yaratish

Har bir amalda shu tekshiruvlar takrorlanadi. Sikl ichida million marta aylansa — million marta tekshiriladi, garchi javob har safar bir xil bo'lsa ham.

Asosiy fikr: Python sekinligining sababi faqat "oraliq qatlam" emas, balki har bir amalda turni aniqlash zarurati. Buni bilish keyinroq muhim bo'ladi: numpy aynan shu takrorlanuvchi tekshiruvni yo'q qilgani uchun tez.

2.6. JIT — ishlash paytida kompilyatsiya

JIT (Just-In-Time) — ikki dunyoning yaxshi tomonini birlashtirishga urinish.

G'oya oddiy: dastur ishlab turganda uni kuzat. Qaysi qism ko'p marta takrorlanayotganini aniqla va aynan o'sha qismni mashina kodiga o'gir.

text
1. Bytecode odatdagidek talqin qilinadi
2. VM sanaydi: bu funksiya necha marta chaqirildi?
3. Ma'lum chegaradan oshsa ("qaynoq joy") →
   o'sha funksiya mashina kodiga kompilyatsiya qilinadi
4. Keyingi chaqiruvlar tayyor mashina kodini ishlatadi

JIT ning kutilmagan ustunligi bor: u ishlash paytidagi haqiqiy ma'lumotni ko'radi.

Agar funksiya 10 000 marta chaqirilgan bo'lsa va har safar int kelgan bo'lsa, JIT shunday deyishi mumkin: "Bu doim int ekan. Men int uchun maxsus tez versiya yasayman va turni tekshirishni tashlab yuboraman. Agar boshqa tur kelsa — sekin yo'lga qaytaman."

AOT kompilyator buni qila olmaydi, chunki u ishlash paytini ko'rmaydi.

JIT ishlatadigan tillar: Java (JVM), C# (.NET), JavaScript (V8), PyPy.

Kamchiligi: dastur boshida sekin ("isishi" kerak) va ko'proq xotira talab qiladi. Qisqa skript uchun JIT foyda bermaydi.

2.7. CPython va PyPy

Bu yerda muhim ajratish bor. Python — bu til. Uni bajaradigan dasturlar esa bir nechta:

Nomi Nima Xususiyati
CPython Rasmiy, C tilida yozilgan Standart. Siz o'rnatadigan Python — shu
PyPy JIT bilan 5–10 barobar tez, lekin ba'zi kutubxona ishlamaydi
MicroPython Mikrokontroller uchun Juda kichik, chegaralangan
Jython JVM ustida Java bilan integratsiya

python --version deb yozganingizda odatda CPython ishlaydi.

Nega CPython JIT ishlatmaydi? Sabablari bor:

  • C bilan integratsiya (numpy, pandas — hammasi CPython ga tayanadi)
  • Soddalik va barqarorlik
  • JIT xotira sarfini oshiradi

Python 3.13 dan boshlab CPython'ga ham eksperimental JIT qo'shildi, lekin u hali boshlang'ich bosqichda.

2.8. Transpiler — tildan tilga

To'rtinchi yondashuv: kodni boshqa dasturlash tiliga o'girish.

text
TypeScript  →  [transpiler]  →  JavaScript  →  brauzer bajaradi

Bu kompilyatsiya emas, chunki natija ham yuqori darajali til.

Misollar:

  • TypeScript → JavaScript — turlar qo'shish uchun
  • Sass → CSS
  • Cython → C — Pythonga o'xshash kodni C ga o'girib, keyin kompilyatsiya qiladi

Cython bizga qiziq: u Python kodini olib, unga tur belgilarini qo'shish imkonini beradi va natijani C ga o'giradi. Natijada 10–100 barobar tezlik. Buni 18-qismda o'rganamiz.

2.9. Umumiy manzara

Yondashuv Nimaga Qachon Kim bajaradi Misol
AOT Mashina kodi Oldindan Protsessor C, Rust, Go
Bytecode + VM Bytecode Oldindan Virtual mashina Python, Java
JIT Bytecode → mashina kodi Ishlash paytida Ikkalasi PyPy, JVM, V8
Sof interpretatsiya Hech nimaga — Interpretator Bash
Transpiler Boshqa til Oldindan O'sha til muhiti TypeScript, Cython

Pythonning o'rni: bytecode + VM. Kerak bo'lganda JIT (PyPy) yoki transpiler (Cython) bilan tezlashtiriladi.

2.10. Amaliy xulosa — bu bilim nima beradi

Endi "Python sekin" degan gapga aniq javob bera olasiz:

Nega sekin:

  1. Bytecode VM tomonidan talqin qilinadi (qo'shimcha qatlam)
  2. Har bir amalda tur tekshiriladi (dinamik turlash narxi)
  3. Har bir son ham to'liq obyekt (xotira va vaqt sarfi)

Qanday tezlashtirish mumkin:

Usul Qachon mos Tezlik
Yaxshiroq algoritm Deyarli har doim birinchi qadam Cheksiz
numpy / pandas Massiv va jadval bilan ishlash 10–100×
PyPy Sof Python, uzoq ishlaydigan dastur 5–10×
Cython Aniq bir og'ir funksiya 10–100×
C / Rust kengaytmasi Eng og'ir qism 50–1000×
Konkurentlik Kutish ko'p bo'lsa (tarmoq, disk) Vaziyatga qarab

Diqqat: birinchi qatorga qarang. Algoritmni yaxshilash deyarli har doim tilni almashtirishdan foydaliroq. Sekin algoritmni C'da yozsangiz — u baribir sekin bo'ladi, faqat 50 barobar tezroq sekin.


3. Tez ma'lumotnoma

To'rt yondashuv

text
AOT          : manba → mashina kodi (oldindan)
Bytecode+VM  : manba → bytecode → VM talqin qiladi
JIT          : bytecode → qaynoq joylar mashina kodiga (ishlash paytida)
Transpiler   : manba → boshqa til

Python bajaruvchilari

text
CPython      — standart, C da yozilgan
PyPy         — JIT bilan, 5-10x tez
MicroPython  — mikrokontroller uchun

Python nega sekin (uch sabab)

text
1. VM qatlami
2. Har amalda tur tekshiruvi
3. Har bir qiymat — to'liq obyekt

4. Batafsil misollar

Bu misollarni 2-qismdan keyin sinab ko'rasiz.

Misol 1 — Bytecode'ni o'z ko'zingiz bilan ko'rish

python
import dis

def qoshish(a, b):
    return a + b

dis.dis(qoshish)

Natija (Python 3.12 da, soddalashtirilgan):

text
  2           RESUME                   0
  3           LOAD_FAST                a
              LOAD_FAST                b
              BINARY_OP                0 (+)
              RETURN_VALUE

Nima ko'rsatdi: 2.4-bo'limdagi bytecode. Bitta a + b amali to'rtta bytecode buyrug'iga aylandi. C'da bu bitta protsessor buyrug'i bo'lardi.

BINARY_OP buyrug'i bajarilganda VM a va b ning turini tekshiradi — 2.5-bo'limdagi sekinlik sababi aynan shu.

Misol 2 — Dinamik turlash narxini o'lchash

python
import time

# 1) Sof Python — har amalda tur tekshiriladi
boshlanish = time.time()
jami = 0
for i in range(5_000_000):
    jami += i
sof_python = time.time() - boshlanish

# 2) NumPy — turlar oldindan ma'lum, tsikl C ichida
import numpy as np
boshlanish = time.time()
jami2 = int(np.arange(5_000_000).sum())
numpy_vaqt = time.time() - boshlanish

print(f"Sof Python : {sof_python:.3f} s")
print(f"NumPy      : {numpy_vaqt:.3f} s")
print(f"Farq       : {sof_python / numpy_vaqt:.0f} barobar")

# Odatiy natija:
#   Sof Python : 0.310 s
#   NumPy      : 0.012 s
#   Farq       : 26 barobar

Nima ko'rsatdi: 2.5 va 2.10-bo'limlar. NumPy tez, chunki u massivdagi hamma element bir xil turda ekanini biladi va tsiklni C ichida bajaradi — har elementda tur tekshirmaydi.

Bu "C sehri" emas. Bu takrorlanuvchi tekshiruvni yo'q qilish.

Misol 3 — Algoritm tildan muhimroq

python
import time

sonlar = list(range(100_000))
qidirilayotgan = 99_999

# YOMON algoritm: ro'yxatda ketma-ket qidirish — O(n)
boshlanish = time.time()
topildi = qidirilayotgan in sonlar
yomon = time.time() - boshlanish

# YAXSHI algoritm: to'plamda qidirish — O(1)
tuplam = set(sonlar)
boshlanish = time.time()
topildi = qidirilayotgan in tuplam
yaxshi = time.time() - boshlanish

print(f"Ro'yxatda : {yomon * 1_000_000:.1f} mikrosekund")
print(f"To'plamda : {yaxshi * 1_000_000:.1f} mikrosekund")

# Odatiy natija:
#   Ro'yxatda : 950.0 mikrosekund
#   To'plamda : 0.3 mikrosekund     ← 3000 barobar tez

Nima ko'rsatdi: 2.10-bo'limdagi eng muhim maslahat. Bu yerda til o'zgarmadi, kutubxona qo'shilmadi — faqat ma'lumot tuzilmasi almashtirildi va 3000 barobar tezlik olindi.

Hech qanday JIT yoki C kengaytmasi bunday natija bermaydi. Shuning uchun optimizatsiya har doim algoritmdan boshlanadi.

Misol 4 — __pycache__ haqiqatan ishlayotganini ko'rish

python
# hisob.py nomli fayl yarating:
def kvadrat(x):
    return x * x
python
# main.py:
import hisob
print(hisob.kvadrat(5))

Birinchi marta python main.py deb ishga tushiring — yonida __pycache__ papkasi paydo bo'ladi va uning ichida hisob.cpython-312.pyc fayli bo'ladi.

text
loyiha/
├── main.py
├── hisob.py
└── __pycache__/
    └── hisob.cpython-312.pyc      ← bytecode saqlandi

Ikkinchi marta ishga tushirganda Python hisob.py ni qayta kompilyatsiya qilmaydi — tayyor bytecode'ni oladi.

Nima ko'rsatdi: 2.4-bo'limdagi "tahlil bir marta" tamoyili. Diqqat: main.py uchun .pyc yaratilmaydi — Python faqat import qilingan modullar uchun saqlaydi.


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

Noto'g'ri fikr To'g'risi
"Python kompilyatsiya qilinmaydi" Qilinadi — bytecode'ga. Mashina kodiga emas
"Python sekin, chunki interpretatsiya qilinadi" Asosiy sabab — dinamik turlash. VM qatlami ikkinchi darajali
"JIT har doim tezroq" Qisqa dasturda JIT "isishga" ulgurmaydi va foyda bermaydi
"Java kompilyatsiya qilinadi, Python yo'q" Ikkalasi ham bytecode'ga kompilyatsiya qilinadi. Farq — Java'da JIT bor
"numpy tez, chunki C da yozilgan" Yarim to'g'ri. Asosiy sabab — takrorlanuvchi tur tekshiruvining yo'qligi
"Tez kerak bo'lsa tilni almashtirish kerak" Avval algoritmni tekshiring. U ko'pincha 1000 barobar farq beradi
"PyPy — Pythonning yangi versiyasi" Yo'q, bu boshqa bajaruvchi. Til bir xil
"__pycache__ — vaqtinchalik axlat" Bu optimizatsiya. O'chirsa bo'ladi, lekin keyingi ishga tushirish sekinroq

6. Keng tarqalgan xatolar va yechimlari

1. O'lchamasdan optimizatsiya qilish

Muammo: "bu qism sekin bo'lsa kerak" deb taxmin qilib, kodni murakkablashtirish.

Yechim: avval o'lchang (time, cProfile). Dasturchilar sekinlik joyini taxmin qilishda deyarli har doim adashadi. 18-qismda o'rganamiz.

2. Algoritmni tashlab, tilni ayblash

Muammo: O(n²) algoritm yozib, "Python sekin ekan" degan xulosaga kelish.

Yechim: 3-misolga qarang. Ro'yxat o'rniga to'plam — 3000 barobar. Til bir xil qoldi.

3. Har bir joyda PyPy yoki Cython ishlatishga urinish

Muammo: 0.1 soniya ishlaydigan skriptni optimizatsiya qilishga bir kun sarflash.

Yechim: tezlik faqat muammo bo'lganda muammo. Foydalanuvchi kutmayotgan bo'lsa — optimizatsiya kerak emas.

4. PyPy ni tekshirmasdan ishlatish

Muammo: PyPy ba'zi C kengaytmalari bilan ishlamaydi yoki sekinroq ishlaydi (jumladan numpy ning ba'zi qismlari).

Yechim: PyPy ga o'tishdan oldin loyihangizni sinab ko'ring va o'lchang.

5. __pycache__ ni versiya nazoratiga qo'shish

Muammo: .pyc fayllari git'ga tushadi, keraksiz konfliktlar chiqadi.

Yechim: .gitignore ga __pycache__/ va *.pyc qo'shing.


7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 12-qism (CPython): dis bilan bytecode'ni ochish, VM ishlashi — bu darsning to'liq davomi
  • 13-qism (Turlar): tur belgilari va nega ular tezlik bermaydi
  • 18-qism (Vositalar): profiling — sekinlikni o'lchash
  • 24-qism (NumPy/pandas): 2.5-bo'limdagi tamoyilning amaliy qo'llanishi
  • 29-qism (Miqyos): qachon va nimani optimizatsiya qilish
  • 30-qism (Boshqa tillar): Cython, C kengaytmasi, Rust moduli — 2.8-bo'limning davomi

8. Eng yaxshi amaliyotlar

  1. Avval o'lchang, keyin optimizatsiya qiling. Taxmin qilish — vaqt isrofi.

  2. Optimizatsiyani algoritmdan boshlang. Ma'lumot tuzilmasini to'g'ri tanlash tilni almashtirishdan foydaliroq.

  3. Tayyor kutubxonani afzal ko'ring. numpy, pandas — ular allaqachon optimizatsiya qilingan.

  4. Tezlik muammo bo'lgandagina shug'ullaning. "Erta optimizatsiya — barcha yomonliklarning ildizi" (Donald Knuth).

  5. __pycache__ ni .gitignore ga qo'ying.

  6. "Python sekin" degan gapga aniq javob bering. Nima sekin? Qancha? O'lchandimi? Bu savollar suhbatni foydali yo'nalishga buradi.


9. Amaliy topshiriq

Vazifa 1: Yondashuvlarni ajratish

Quyidagi tillar/vositalar qaysi yondashuvga tegishli? (AOT / Bytecode+VM / JIT / Transpiler)

  1. Rust
  2. Java
  3. TypeScript
  4. PyPy
  5. CPython
  6. Sass
  7. Go
Javoblar
  1. Rust — AOT
  2. Java — Bytecode+VM, ustiga JIT (JVM)
  3. TypeScript — Transpiler (JavaScript'ga)
  4. PyPy — Bytecode+VM, ustiga JIT
  5. CPython — Bytecode+VM (JIT'siz)
  6. Sass — Transpiler (CSS'ga)
  7. Go — AOT

Vazifa 2: Tezlikni o'lchash (2-qismdan keyin)

3-misoldagi tajribani takrorlang, lekin o'zgartiring:

  1. Ro'yxat hajmini 1 000, 10 000, 100 000, 1 000 000 qilib sinab ko'ring
  2. Har biri uchun ro'yxat va to'plamdagi qidirish vaqtini yozib boring
  3. Jadval tuzing va savolga javob bering: ro'yxat hajmi 10 barobar oshsa, qidirish vaqti necha barobar oshadi? To'plamda-chi?

Kutilayotgan xulosa: ro'yxatda vaqt hajmga proporsional oshadi, to'plamda esa deyarli o'zgarmaydi. Bu — algoritmik murakkablik farqi (O(n) va O(1)). Buni 6-qismda batafsil o'rganamiz.

Vazifa 3: Fikrlash

Sizga aytishdi: "Bizning Python dasturimiz sekin ishlayapti, uni Go'ga ko'chiraylik."

Ko'chirishdan oldin qanday beshta savol berardingiz? Har biriga sabab yozing.

Mumkin bo'lgan savollar
  1. Aniq nima sekin? Butun dasturmi yoki bitta funksiyami? (Profiling qilinganmi?)
  2. Qancha sekin va qancha kerak? "Sekin" — bu 2 soniyami yoki 2 daqiqami? Maqsad nima?
  3. Vaqt qayerda ketyapti? Hisoblashdami yoki kutishdami (baza, tarmoq)? Agar kutishda bo'lsa, tilni almashtirish yordam bermaydi.
  4. Algoritm tekshirilganmi? Balki muammo O(n²) da, tilda emas.
  5. Faqat og'ir qismni ko'chirsa bo'lmaydimi? Cython yoki Rust moduli butun loyihani qayta yozishdan ancha arzon.

Nimani mustahkamlaydi: 2.9 (yondashuvlar) va 2.10 (amaliy xulosa) — bu darsning eng qimmatli qismlari.


Xulosa

Bu darsda "kompilyatsiya va interpretatsiya" degan sodda ikkilikni chuqurroq manzaraga almashtirdik.

Eng muhim uch fikr:

  1. Bu spektr, ikkilik emas. AOT, bytecode+VM, JIT va transpiler — to'rt xil yondashuv, va ko'p til ularni birlashtiradi.

  2. Python sekinligining asosiy sababi — dinamik turlash, VM qatlami emas. Har bir amalda tur tekshiriladi. Aynan shuning uchun numpy tez: u bu tekshiruvni yo'q qiladi.

  3. Optimizatsiya algoritmdan boshlanadi. Ro'yxatni to'plamga almashtirish 3000 barobar tezlik berdi — hech qanday JIT bunday natija bermaydi.

Keyingi darsda Python kim tomonidan, qachon va nega yaratilganini ko'ramiz — va bu tarix tilning bugungi ko'rinishini qanday belgilaganini tushunamiz.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
1.4-dars: Kompilyator, interpretator va JIT — kod qanday bajariladi — IlmHamroh