Mundarija (23)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Birinchi savol: vaqt qayerga ketyapti
- 2.2. To'rt model — taqqoslash
- 2.3. Qaror daraxti
- 2.4. Amdahl qonuni
- 2.5. Gibrid arxitekturalar
- 2.6. Free-threading va kelajak
- 2.7. Tanlov qoidalari
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — I/O yukida to'rt model
- Misol 2 — CPU yukida to'rt model
- Misol 3 — Yaratish narxi, miqyos va Amdahl
- Misol 4 — Amaliy: yukni tasniflovchi va gibrid quvur
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
- 14-qism yakuni
- 3-bosqich yakuni: "Python ichidan"
14.8-dars: Qaysi birini qachon tanlash
14-QISM — KONKURENTLIK VA PARALLELLIK · 8-dars
1. Kirish va motivatsiya
14-qismda to'rtta model o'rgandik: iplar, jarayonlar, subinterpretatorlar va asyncio. Har biri o'z darsida yaxshi ko'rindi. Endi eng muhim savol: aniq vazifa uchun qaysi birini tanlash kerak?
Noto'g'ri tanlov qimmatga tushadi. Ikki real vaziyat:
- Jamoa rasmlarni qayta ishlash xizmatini
asyncioga ko'chirdi, chunki "asinxron — tez". Natija: tezlik o'zgarmadi, lekin kod ikki barobar murakkablashdi. Sabab — ish CPU yuki edi,asyncioesa faqat kutishni tejaydi. - Boshqa jamoa 2 000 ta API so'rovi uchun
ProcessPoolExecutorishlatdi. Har so'rov jarayonlar orasida serializatsiya qilindi, 16 jarayon xotirani to'ldirdi. OddiyThreadPoolExecutor(50)bir xil ishni 5 barobar kam xotira bilan bajardi.
Tanlov "qaysi texnologiya zamonaviyroq" degan savolga emas, "vaqt qayerga ketyapti?" degan savolga bog'liq. Bu darsda shu savolga o'lchov bilan javob beramiz va 14-qismni yakunlaymiz.
Bu darsda:
- Yuk turi: I/O, CPU yoki aralash — qanday aniqlanadi
- To'rt model bir xil yukda: haqiqiy o'lchovlar
- Yaratish narxi va miqyos: 1 000 ip va 10 000 vazifa
- Amdahl qonuni: parallellikning chegarasi
- Qaror daraxti va gibrid arxitekturalar
- Free-threading va kelajak
- Amaliy: yukni avtomatik tasniflovchi va gibrid quvur
- 14-qism va 3-bosqich yakuni
2. Nazariya — chuqur tushuntirish
2.1. Birinchi savol: vaqt qayerga ketyapti
| Yuk turi | Belgisi | Misollar |
|---|---|---|
| I/O yuki | Dastur ko'pincha kutadi | HTTP so'rov, baza, fayl, navbat |
| CPU yuki | Protsessor to'la band | Hisob, parsing, siqish, rasm |
| Aralash | Kutish + hisob | Yuklab olish + tahlil |
Oddiy o'lchov: CPU vaqti / devor vaqti nisbati.
bosh_devor, bosh_cpu = time.perf_counter(), time.process_time()
ish()
nisbat = (time.process_time() - bosh_cpu) / (time.perf_counter() - bosh_devor)| Nisbat | Xulosa |
|---|---|
| ≈ 0 | I/O yuki — kutish ustun |
| ≈ 1 | CPU yuki — bitta yadro to'la |
| Oraliqda | Aralash — ikkala tomonni alohida ko'ring |
2.2. To'rt model — taqqoslash
| Iplar | Jarayonlar | Subinterpretatorlar | asyncio |
|
|---|---|---|---|---|
| Parallel CPU (GIL bilan) | (3.14) | |||
| I/O kutishni tejaydi | ||||
| Yaratish narxi | O'rtacha | Yuqori | O'rtacha | Juda past |
| Xotira / birlik | ~MB stek | O'nlab MB | Bir necha MB | KB |
| Bir vaqtda sig'imi | Yuzlab | O'nlab | O'nlab | O'n minglab |
| Umumiy xotira | (xavfli) | (bitta ip) | ||
| Ma'lumot uzatish | Bepul | Pickle | Pickle/cheklangan | Bepul |
| Poyga xavfi | Har joyda | Kam | Kam | Faqat await da |
| Kutubxonalar mosligi | Hammasi | Pickle bo'ladiganlar | C kengaytmalar cheklangan | Asinxron kutubxonalar kerak |
2.3. Qaror daraxti
Vaqt qayerga ketyapti?
│
├─ I/O kutish
│ ├─ Minglab ulanish / asinxron kutubxonalar bor → asyncio
│ ├─ O'nlab-yuzlab vazifa / sinxron kutubxonalar → ThreadPoolExecutor
│ └─ asyncio + bloklovchi kutubxona → asyncio + to_thread
│
├─ CPU hisob (sof Python)
│ ├─ Vazifalar mustaqil, katta → ProcessPoolExecutor
│ ├─ Yengilroq izolyatsiya kerak (3.14) → InterpreterPoolExecutor
│ ├─ NumPy / C kengaytma (GIL ni qo'yib yuboradi) → iplar ham yetadi
│ └─ Juda mayda vazifalar → avval vektorlash / paketlash
│
└─ Aralash
└─ asyncio (I/O) + run_in_executor(ProcessPool) (CPU)2.4. Amdahl qonuni
Dasturning parallel bo'lmaydigan qismi tezlashish chegarasini belgilaydi:
tezlashish(N) = 1 / ((1 - P) + P / N)Parallel qism P |
4 yadro | 16 yadro | Cheksiz yadro |
|---|---|---|---|
| 50% | 1.60× | 1.88× | 2× |
| 90% | 3.08× | 6.40× | 10× |
| 99% | 3.88× | 13.91× | 100× |
Xulosa: yadrolar qo'shishdan oldin ketma-ket qismni qisqartiring. 10% ketma-ket kod bilan hech qachon 10 barobardan tezlasha olmaysiz.
2.5. Gibrid arxitekturalar
| Naqsh | Qachon |
|---|---|
asyncio + to_thread |
Asinxron xizmat, bitta-ikkita bloklovchi kutubxona |
asyncio + ProcessPoolExecutor |
Veb-xizmat og'ir hisob bilan |
| Jarayonlar × iplar | Har yadroda bitta jarayon, ichida I/O iplari (Gunicorn + threads) |
Jarayonlar × asyncio |
Har yadroda bitta loop (Uvicorn workers) |
| Navbat + ishchilar | Uzoq vazifalar — alohida xizmatga (Celery, RQ — 29-qism) |
Ishlab chiqarishdagi veb-serverlar deyarli har doim gibrid: jarayonlar — yadrolarni ishlatish uchun, iplar yoki asyncio — har jarayon ichida ulanishlar uchun.
2.6. Free-threading va kelajak
| Yo'nalish | Holati (3.14) | Nimani o'zgartiradi |
|---|---|---|
Free-threaded build (3.14t) |
Rasmiy qo'llab-quvvatlanadi | Iplar CPU yukida ham parallel |
| Subinterpretatorlar | InterpreterPoolExecutor |
Jarayondan yengil izolyatsiya |
| JIT | Tajribaviy | Bitta ip tezligi |
Free-threaded rejimda iplar haqiqiy parallel bo'ladi — demak 14.3-darsdagi poyga holatlari yanada ko'p uchraydi. Qulflarsiz "tasodifan ishlagan" kod buzilishi mumkin.
2.7. Tanlov qoidalari
- Avval o'lchang — taxmin emas (
process_time/perf_counter) - Eng sodda modeldan boshlang — ko'p hollarda ketma-ket kod yetarli
- I/O → iplar yoki
asyncio, CPU → jarayonlar - Mayda vazifalarni paketlang (12.6, 14.5-darslar)
- Bir vaqtdagi ishlarni cheklang —
Semaphore,max_workers,maxsize - Algoritm va kutubxonani konkurentlikdan oldin yaxshilang
3. Tez ma'lumotnoma
# Yuk turini aniqlash
nisbat = cpu_vaqti / devor_vaqti # ≈0 → I/O, ≈1 → CPU
# I/O, sinxron kutubxonalar
with ThreadPoolExecutor(max_workers=32) as ex: ...
# I/O, minglab ulanish
async with asyncio.TaskGroup() as g: ...
# CPU
with ProcessPoolExecutor() as ex: ex.map(f, data, chunksize=...)
# Aralash
await loop.run_in_executor(jarayonlar_puli, ogir_hisob, x)Qoidalar
avval o'lchang: process_time / perf_counter
I/O → iplar yoki asyncio
CPU → jarayonlar (yoki subinterpretatorlar)
aralash → asyncio + ProcessPool
Amdahl: ketma-ket qism chegarani belgilaydi4. Batafsil misollar
Misol 1 — I/O yukida to'rt model
"""Bir xil I/O yuki (40 ta 50 ms kutish): ketma-ket, iplar, jarayonlar, subinterpretatorlar, asyncio."""
import asyncio
import concurrent.futures as cf
import time
VAZIFALAR = 40
HA, YOQ = "✅ ha", "❌ yo'q"
def kutish(_: int) -> int:
time.sleep(0.05) # tarmoq so'rovini taqlid qiladi
return 1
async def kutish_async(_: int) -> int:
await asyncio.sleep(0.05)
return 1
def olchov(funksiya) -> float:
bosh = time.perf_counter()
funksiya()
return time.perf_counter() - bosh
def pul_bilan(Ex, ishchilar: int) -> None:
with Ex(max_workers=ishchilar) as ex:
assert sum(ex.map(kutish, range(VAZIFALAR))) == VAZIFALAR
async def asinxron() -> None:
assert sum(await asyncio.gather(*(kutish_async(i) for i in range(VAZIFALAR)))) == VAZIFALAR
def main() -> None:
vaqtlar = {
"ketma-ket": olchov(lambda: [kutish(i) for i in range(VAZIFALAR)]),
"iplar (20)": olchov(lambda: pul_bilan(cf.ThreadPoolExecutor, 20)),
"jarayonlar (8)": olchov(lambda: pul_bilan(cf.ProcessPoolExecutor, 8)),
"subinterpretatorlar (8)": olchov(lambda: pul_bilan(cf.InterpreterPoolExecutor, 8)),
"asyncio": olchov(lambda: asyncio.run(asinxron())),
}
asos = vaqtlar["ketma-ket"]
print("=== 1. Kamida 1.5 barobar tezlashdimi ===")
for nom, vaqt in vaqtlar.items():
if nom != "ketma-ket":
print(f" {nom:24} {HA if asos / vaqt >= 1.5 else YOQ}")
print("\n=== 2. Modellar orasida ===")
print(f" asyncio iplardan tez: {vaqtlar['asyncio'] < vaqtlar['iplar (20)']}")
print(f" iplar jarayonlardan tez: {vaqtlar['iplar (20)'] < vaqtlar['jarayonlar (8)']}")
print(f" asyncio ketma-ketdan kamida 10 barobar tez: {asos / vaqtlar['asyncio'] >= 10}")
print("\n=== 3. Nega ===")
print(" ⭐ hamma model kutishni tejaydi — GIL kutish paytida qo'yib yuboriladi")
print(" jarayonlar: ishga tushirish narxi + faqat 8 ishchi")
print(" iplar: 20 ishchi, lekin har biri OS ipi")
print(" asyncio: 40 ta arzon vazifa, bitta ip")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Kamida 1.5 barobar tezlashdimi ===
iplar (20) ✅ ha
jarayonlar (8) ✅ ha
subinterpretatorlar (8) ✅ ha
asyncio ✅ ha
=== 2. Modellar orasida ===
asyncio iplardan tez: True
iplar jarayonlardan tez: True
asyncio ketma-ketdan kamida 10 barobar tez: True
=== 3. Nega ===
⭐ hamma model kutishni tejaydi — GIL kutish paytida qo'yib yuboriladi
jarayonlar: ishga tushirish narxi + faqat 8 ishchi
iplar: 20 ishchi, lekin har biri OS ipi
asyncio: 40 ta arzon vazifa, bitta ipNima ko'rsatdi: 2.2, 2.3-bo'limlar.
Misol 2 — CPU yukida to'rt model
"""Bir xil CPU yuki (8 ta sof Python hisob): ketma-ket, iplar, jarayonlar, subinterpretatorlar, asyncio."""
import asyncio
import concurrent.futures as cf
import sys
import time
VAZIFALAR = 8
HA, YOQ = "✅ ha", "❌ yo'q"
def hisob(_: int = 0) -> int:
s = 0
for i in range(6_000_000):
s += i * i
return s
KUTILGAN = 0 # main() da hisoblanadi — ishchilar qayta hisoblamasin
def olchov(funksiya) -> float:
bosh = time.perf_counter()
funksiya()
return time.perf_counter() - bosh
def pul_bilan(Ex) -> None:
with Ex(max_workers=VAZIFALAR) as ex:
assert all(n == KUTILGAN for n in ex.map(hisob, range(VAZIFALAR)))
async def asinxron() -> None:
async def vazifa() -> int:
return hisob() # await yo'q — loop band
natijalar = await asyncio.gather(*(vazifa() for _ in range(VAZIFALAR)))
assert all(n == KUTILGAN for n in natijalar)
def main() -> None:
global KUTILGAN
KUTILGAN = hisob()
print(f"GIL yoqilgan: {sys._is_gil_enabled()}")
vaqtlar = {
"ketma-ket": olchov(lambda: [hisob() for _ in range(VAZIFALAR)]),
"iplar": olchov(lambda: pul_bilan(cf.ThreadPoolExecutor)),
"jarayonlar": olchov(lambda: pul_bilan(cf.ProcessPoolExecutor)),
"subinterpretatorlar": olchov(lambda: pul_bilan(cf.InterpreterPoolExecutor)),
"asyncio": olchov(lambda: asyncio.run(asinxron())),
}
asos = vaqtlar["ketma-ket"]
print("\n=== 1. Kamida 1.5 barobar tezlashdimi ===")
for nom, vaqt in vaqtlar.items():
if nom != "ketma-ket":
print(f" {nom:20} {HA if asos / vaqt >= 1.5 else YOQ}")
print("\n=== 2. Tahlil ===")
print(f" iplar ketma-ketdan tez emas (GIL): {vaqtlar['iplar'] > asos * 0.7}")
print(f" asyncio ketma-ketdan tez emas: {vaqtlar['asyncio'] > asos * 0.7}")
print(f" jarayonlar va subinterpretatorlar parallel: "
f"{asos / vaqtlar['jarayonlar'] >= 1.5 and asos / vaqtlar['subinterpretatorlar'] >= 1.5}")
print("\n=== 3. Xulosa ===")
print(" ⭐ I/O yukida g'olib bo'lgan asyncio bu yerda foydasiz")
print(" ⭐ har interpretatorning o'z GIL i bor — shuning uchun subinterpretatorlar parallel")
print(" ⚠️ free-threaded (3.14t) buildda iplar ham tezlashardi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
GIL yoqilgan: True
=== 1. Kamida 1.5 barobar tezlashdimi ===
iplar ❌ yo'q
jarayonlar ✅ ha
subinterpretatorlar ✅ ha
asyncio ❌ yo'q
=== 2. Tahlil ===
iplar ketma-ketdan tez emas (GIL): True
asyncio ketma-ketdan tez emas: True
jarayonlar va subinterpretatorlar parallel: True
=== 3. Xulosa ===
⭐ I/O yukida g'olib bo'lgan asyncio bu yerda foydasiz
⭐ har interpretatorning o'z GIL i bor — shuning uchun subinterpretatorlar parallel
⚠️ free-threaded (3.14t) buildda iplar ham tezlashardiNima ko'rsatdi: 2.2, 2.6-bo'limlar.
Misol 3 — Yaratish narxi, miqyos va Amdahl
"""1 000 ip va 1 000 vazifa yaratish narxi; 10 000 bir vaqtdagi kutish; Amdahl qonuni jadvali."""
import asyncio
import threading
import time
def olchov(funksiya) -> float:
bosh = time.perf_counter()
funksiya()
return time.perf_counter() - bosh
def ming_ip() -> None:
iplar = [threading.Thread(target=time.sleep, args=(0,)) for _ in range(1_000)]
for ip in iplar:
ip.start()
for ip in iplar:
ip.join()
async def ming_vazifa() -> None:
await asyncio.gather(*(asyncio.sleep(0) for _ in range(1_000)))
async def on_ming_kutish() -> None:
await asyncio.gather(*(asyncio.sleep(0.2) for _ in range(10_000)))
def amdahl(parallel_qism: float, yadrolar: float) -> float:
return 1 / ((1 - parallel_qism) + parallel_qism / yadrolar)
def main() -> None:
print("=== 1. Yaratish narxi ===")
ip_vaqti = olchov(ming_ip)
vazifa_vaqti = olchov(lambda: asyncio.run(ming_vazifa()))
print(f" 1 000 vazifa 1 000 ipdan kamida 5 barobar arzon: {ip_vaqti / vazifa_vaqti >= 5}")
print("\n=== 2. 10 000 ta bir vaqtdagi kutish ===")
vaqt = olchov(lambda: asyncio.run(on_ming_kutish()))
print(f" 10 000 ta 0.2 s lik kutish 1 s dan kam vaqtda tugadi: {vaqt < 1.0}")
print(" ⭐ 10 000 ip bilan bu OS chegaralariga urilardi")
print("\n=== 3. Amdahl qonuni ===")
print(f" {'parallel qism':>14} {'4 yadro':>9} {'16 yadro':>9} {'1000 yadro':>11}")
for p in (0.5, 0.9, 0.95, 0.99):
qator = "".join(f"{amdahl(p, n):>9.2f}×" for n in (4, 16))
print(f" {p:>13.0%} {qator} {amdahl(p, 1000):>10.2f}×")
print("\n=== 4. Amdahl nima deydi ===")
print(f" 90% parallel kod, 16 yadro: {amdahl(0.9, 16):.2f}× — 16× emas")
print(f" ketma-ket qismni 10% dan 5% ga tushirish: {amdahl(0.9, 16):.2f}× → {amdahl(0.95, 16):.2f}×")
print(f" yadrolarni 16 dan 32 ga oshirish: {amdahl(0.9, 16):.2f}× → {amdahl(0.9, 32):.2f}×")
print(" ⭐ ketma-ket qismni qisqartirish yadro qo'shishdan foydaliroq")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Yaratish narxi ===
1 000 vazifa 1 000 ipdan kamida 5 barobar arzon: True
=== 2. 10 000 ta bir vaqtdagi kutish ===
10 000 ta 0.2 s lik kutish 1 s dan kam vaqtda tugadi: True
⭐ 10 000 ip bilan bu OS chegaralariga urilardi
=== 3. Amdahl qonuni ===
parallel qism 4 yadro 16 yadro 1000 yadro
50% 1.60× 1.88× 2.00×
90% 3.08× 6.40× 9.91×
95% 3.48× 9.14× 19.63×
99% 3.88× 13.91× 90.99×
=== 4. Amdahl nima deydi ===
90% parallel kod, 16 yadro: 6.40× — 16× emas
ketma-ket qismni 10% dan 5% ga tushirish: 6.40× → 9.14×
yadrolarni 16 dan 32 ga oshirish: 6.40× → 7.80×
⭐ ketma-ket qismni qisqartirish yadro qo'shishdan foydaliroqNima ko'rsatdi: 2.2, 2.4-bo'limlar.
Misol 4 — Amaliy: yukni tasniflovchi va gibrid quvur
Kirishdagi vaziyatlarning yechimi: avval yukni o'lchab tasniflaymiz, keyin aralash vazifani — "hujjatni yuklab olish (I/O) + tahlil qilish (CPU)" — uch xil arxitekturada bajaramiz: faqat iplar, faqat asyncio va gibrid (asyncio + jarayonlar puli).
"""CPU/devor nisbati bilan yuk turini aniqlash; aralash quvur: iplar, asyncio, asyncio + ProcessPool."""
import asyncio
import concurrent.futures as cf
import time
HUJJATLAR = 16
def yuk_turi(funksiya) -> str:
bosh_devor, bosh_cpu = time.perf_counter(), time.process_time()
funksiya()
nisbat = (time.process_time() - bosh_cpu) / (time.perf_counter() - bosh_devor)
if nisbat < 0.2:
return "I/O"
if nisbat > 0.7:
return "CPU"
return "aralash"
def tahlil(matn: str) -> int:
"""CPU qismi: sof Python hisob."""
s = 0
for i in range(1_200_000):
s += (i * len(matn)) % 7
return s
def yuklab_ol_sinxron(i: int) -> str:
time.sleep(0.1) # I/O qismi
return f"hujjat-{i}"
async def yuklab_ol(i: int) -> str:
await asyncio.sleep(0.1)
return f"hujjat-{i}"
def faqat_iplar() -> list[int]:
def toliq(i: int) -> int:
return tahlil(yuklab_ol_sinxron(i))
with cf.ThreadPoolExecutor(max_workers=HUJJATLAR) as ex:
return list(ex.map(toliq, range(HUJJATLAR)))
async def faqat_asyncio() -> list[int]:
async def toliq(i: int) -> int:
return tahlil(await yuklab_ol(i)) # ⚠️ hisob loopni bloklaydi
return list(await asyncio.gather(*(toliq(i) for i in range(HUJJATLAR))))
async def gibrid() -> list[int]:
loop = asyncio.get_running_loop()
with cf.ProcessPoolExecutor(max_workers=8) as pul:
async def toliq(i: int) -> int:
matn = await yuklab_ol(i) # I/O — loopda
return await loop.run_in_executor(pul, tahlil, matn) # CPU — jarayonda
return list(await asyncio.gather(*(toliq(i) for i in range(HUJJATLAR))))
def aralash_ish() -> None:
for i in range(3):
yuklab_ol_sinxron(i)
tahlil("hujjat-0")
def olchov(funksiya) -> tuple[list[int], float]:
bosh = time.perf_counter()
natija = funksiya()
return natija, time.perf_counter() - bosh
def main() -> None:
print("=== 1. Yuk turini o'lchab aniqlash ===")
print(f" yuklab olish: {yuk_turi(lambda: [yuklab_ol_sinxron(i) for i in range(3)])}")
print(f" tahlil: {yuk_turi(lambda: tahlil('hujjat-0'))}")
print(f" ikkalasi: {yuk_turi(aralash_ish)}")
print("\n=== 2. Aralash quvur, uch arxitektura ===")
kutilgan = [tahlil(f"hujjat-{i}") for i in range(HUJJATLAR)]
iplar, iplar_vaqti = olchov(faqat_iplar)
asinx, asinx_vaqti = olchov(lambda: asyncio.run(faqat_asyncio()))
gib, gib_vaqti = olchov(lambda: asyncio.run(gibrid()))
print(f" uchala natija to'g'ri: {iplar == kutilgan and asinx == kutilgan and gib == kutilgan}")
print(f" gibrid faqat iplardan tez: {gib_vaqti < iplar_vaqti}")
print(f" gibrid faqat asyncio dan tez: {gib_vaqti < asinx_vaqti}")
print(f" gibrid eng sekinidan kamida 2 barobar tez: {max(iplar_vaqti, asinx_vaqti) / gib_vaqti >= 2}")
print("\n=== 3. Nega ===")
print(" faqat iplar: yuklash parallel, tahlil GIL da navbat kutadi")
print(" faqat asyncio: tahlil loopni bloklaydi — yuklash ham to'xtaydi")
print(" gibrid: yuklash loopda, tahlil 8 ta jarayonda parallel")
print("\n=== 4. Qaror ===")
for vazifa, tanlov in [
("10 000 WebSocket ulanishi", "asyncio"),
("50 ta API ga sinxron kutubxona bilan", "ThreadPoolExecutor"),
("1 mln qatorni sof Python da hisoblash", "ProcessPoolExecutor"),
("veb-xizmat + og'ir hisobot", "asyncio + ProcessPoolExecutor"),
("NumPy matritsa amallari", "iplar yoki vektorlash (GIL qo'yib yuboriladi)"),
]:
print(f" {vazifa:40} → {tanlov}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Yuk turini o'lchab aniqlash ===
yuklab olish: I/O
tahlil: CPU
ikkalasi: aralash
=== 2. Aralash quvur, uch arxitektura ===
uchala natija to'g'ri: True
gibrid faqat iplardan tez: True
gibrid faqat asyncio dan tez: True
gibrid eng sekinidan kamida 2 barobar tez: True
=== 3. Nega ===
faqat iplar: yuklash parallel, tahlil GIL da navbat kutadi
faqat asyncio: tahlil loopni bloklaydi — yuklash ham to'xtaydi
gibrid: yuklash loopda, tahlil 8 ta jarayonda parallel
=== 4. Qaror ===
10 000 WebSocket ulanishi → asyncio
50 ta API ga sinxron kutubxona bilan → ThreadPoolExecutor
1 mln qatorni sof Python da hisoblash → ProcessPoolExecutor
veb-xizmat + og'ir hisobot → asyncio + ProcessPoolExecutor
NumPy matritsa amallari → iplar yoki vektorlash (GIL qo'yib yuboriladi)Nima ko'rsatdi: 2.1, 2.3, 2.5-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"asyncio har doim eng tez" |
Faqat I/O yukida; CPU yukida foydasiz |
| "Jarayonlar har doim tezlashtiradi" | Mayda va I/O vazifalarda narxi foydadan oshadi |
| "Iplar Python'da befoyda" | I/O yukida juda samarali |
| "Ko'proq ishchi — ko'proq tezlik" | Amdahl va resurs chegaralari bor |
| "16 yadro — 16 barobar tezlik" | Ketma-ket qism chegarani belgilaydi |
| "Bir model tanlash kerak" | Real tizimlar gibrid |
| "Free-threading hamma muammoni hal qiladi" | Poyga holatlarini ko'paytiradi |
| "Avval konkurentlik, keyin optimallashtirish" | Avval o'lchov va algoritm |
6. Keng tarqalgan xatolar va yechimlari
1. CPU yukini asyncio bilan tezlashtirishga urinish
async def ishla(x): return ogir_hisob(x) # ❌ loop bloklanadi
await loop.run_in_executor(pul, ogir_hisob, x) # ✅2. I/O uchun jarayonlar
ProcessPoolExecutor().map(requests.get, urllar) # ❌ ortiqcha narx
ThreadPoolExecutor(32).map(requests.get, urllar) # ✅3. O'lchamasdan tanlash
# ❌ "bu sekin, parallel qilamiz"
# ✅ process_time / perf_counter bilan yuk turini aniqlang4. Chegarasiz ishchilar
ThreadPoolExecutor(max_workers=10_000) # ❌5. Mayda CPU vazifalarini alohida jarayonlarga yuborish
ex.map(kichik, katta_royxat) # ❌ chunksize=1
ex.map(kichik, katta_royxat, chunksize=10_000) # ✅6. Ketma-ket qismni e'tiborsiz qoldirish
# ❌ natijalarni yig'ish bitta ipda 50% vaqt oladi — yadro qo'shish yordam bermaydi7. Modellarni aralashtirib, holatni bo'lishish
async def f(): await asyncio.to_thread(umumiy_royxat.append, x) # ⚠️ iplar poygasi8. NumPy uchun jarayonlar
ProcessPoolExecutor().map(np.dot, ...) # ⚠️ katta massivlarni pickle qilish
# ✅ vektorlash yoki iplar — NumPy GIL ni qo'yib yuboradi7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 12.5–12.6-darslar (o'tilgan): GIL va uning ta'siri
- 14.1–14.7-darslar (o'tilgan): to'rt modelning har biri
- 18-qism: profiling — vaqt qayerga ketayotganini aniqlash
- 22.5-dars: WSGI va ASGI — Uvicorn/Gunicorn ishchilari
- 24-qism: NumPy va vektorlash
- 29-qism: navbatlar, Celery, gorizontal miqyoslash
- 30-qism: Rust/C kengaytmalari — GIL ni qo'yib yuboradigan kod
8. Eng yaxshi amaliyotlar
Avval o'lchang: CPU/devor nisbati.
Eng sodda yechimdan boshlang — ketma-ket kod ko'pincha yetadi.
I/O → iplar yoki
asyncio; CPU → jarayonlar.Aralash yukda — gibrid:
asyncio+ jarayonlar puli.Amdahl'ni yodda tuting — ketma-ket qismni qisqartiring.
Ishchilar sonini cheklang va sozlanadigan qiling.
Konkurentlikdan oldin algoritm va kutubxonani yaxshilang.
Qaroringizni o'lchov bilan hujjatlashtiring — keyingi dasturchi nega shunday tanlanganini bilsin.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
import asyncio, concurrent.futures as cf, sys, time
1. def amdahl(p, n): return 1 / ((1 - p) + p / n)
print(round(amdahl(0.5, 1_000_000), 2))
2. def amdahl(p, n): return 1 / ((1 - p) + p / n)
print(round(amdahl(1.0, 8), 2))
3. def amdahl(p, n): return 1 / ((1 - p) + p / n)
print(round(amdahl(0.0, 8), 2))
4. print(hasattr(cf, "InterpreterPoolExecutor"))
5. print(callable(getattr(sys, "_is_gil_enabled", None)))
6. b = time.process_time()
time.sleep(0.1)
print(time.process_time() - b < 0.05)
7. print(issubclass(cf.ThreadPoolExecutor, cf.Executor),
issubclass(cf.ProcessPoolExecutor, cf.Executor))
8. async def m(): return await asyncio.to_thread(sum, [1, 2, 3])
print(asyncio.run(m()))
9. async def m():
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, pow, 2, 10)
print(asyncio.run(m()))
10. with cf.ThreadPoolExecutor(4) as ex:
print(list(ex.map(len, ["a", "bb", "ccc"])))
11. def amdahl(p, n): return 1 / ((1 - p) + p / n)
print(amdahl(0.9, 16) < amdahl(0.95, 16))
12. print(issubclass(cf.InterpreterPoolExecutor, cf.ThreadPoolExecutor))Javoblar
2.0— 50% ketma-ket qism chegarani 2× qiladi8.01.0True— 3.14TrueTrue—sleepCPU vaqtini sarflamaydiTrue True61024—None— sukut iplar puli[1, 2, 3]TrueTrue— u iplar puli ustiga qurilgan, har ip o'z interpretatorida
Vazifa 2: Model tanlang va asoslang
Har vaziyat uchun model tanlang va bir jumla bilan asoslang:
1. Telegram bot: 20 000 foydalanuvchi, har xabarga bazadan o'qish
2. 5 GB CSV faylni qatorma-qator sof Python bilan tozalash
3. 300 ta saytni har 5 daqiqada tekshiradigan monitoring (requests kutubxonasi)
4. Video fayllarni ffmpeg bilan qayta kodlash (tashqi dastur)
5. FastAPI xizmati: har so'rovda 2 soniyalik PDF hisobot yaratishJavoblar
1. asyncio — minglab bir vaqtdagi kutish, asinxron baza drayveri (asyncpg)
2. ProcessPoolExecutor — sof Python CPU yuki; faylni bo'laklarga bo'lib, chunksize bilan
3. ThreadPoolExecutor(32–64) — I/O yuki, sinxron kutubxona; yoki httpx + asyncio
4. asyncio.create_subprocess_exec (yoki iplar) — og'ir ishni ffmpeg jarayoni bajaradi,
Python faqat kutadi; parallel sonini Semaphore bilan yadrolar soniga cheklang
5. asyncio + run_in_executor(ProcessPoolExecutor) — loop bloklanmasin;
yuk katta bo'lsa — navbat + alohida ishchilar (Celery, 29-qism)Vazifa 3: Avtomatik maslahatchi
maslahat(funksiya, namuna_argumentlar) funksiyasini yozing:
- Namunada CPU/devor nisbatini o'lchasin
- Bitta chaqiruv vaqtini o'lchasin (mayda/o'rtacha/og'ir)
- Argumentlar pickle bo'ladimi — tekshirsin
- Model,
max_workersvachunksizetavsiya qilsin - Tavsiyani haqiqiy o'lchov bilan tekshirib, jadval chiqarsin
Vazifa 4: Amdahl'ni amalda tekshirish
- 80% parallellashadigan, 20% ketma-ket vazifa yozing
- 1, 2, 4, 8 jarayonda o'lchang
- Haqiqiy tezlashishni Amdahl bashorati bilan bir grafikda solishtiring
- Farq sabablarini (ishga tushirish, pickle, xotira) tushuntiring
Vazifa 5: Gibrid veb-xizmat
asyncio stream lari bilan (14.7-dars) server yozing:
/salom— darhol javob/hisobot— 1 soniyalik CPU hisobProcessPoolExecutorda- 200 ta bir vaqtdagi
/salomso'rovi/hisobotbajarilayotganda ham tez javob olishini isbotlang - Xuddi shuni hisobni loopda bajarib takrorlang va farqni o'lchang
Vazifa 6: Free-threading tajribasi
- Agar mavjud bo'lsa,
python3.14to'rnating - Misol 2 ni ikkala buildda ishga tushiring
- Iplar natijasini solishtiring
- 14.3-darsdagi qulfsiz hisoblagichni ikkala buildda sinang — nima o'zgardi?
- Bitta ip tezligi qanchaga pasaydi?
Vazifa 7: O'ylash
Go tilida bitta model bor — goroutine'lar, va ular I/O ham, CPU ham uchun ishlaydi. Python'da esa to'rtta model va ularni tanlash qoidalari bor. Bu Python'ning kamchiligimi yoki afzalligimi? Free-threading va subinterpretatorlar kelajakda bu manzarani qanday o'zgartirishi mumkin?
Javob
Qisqa javob: Bu asosan tarixiy narx — GIL va C ekotizimi bilan moslik uchun to'langan. Ko'p model tanlash murakkabligini beradi, lekin har biri o'z joyida juda yaxshi ishlaydi. Free-threading va subinterpretatorlar Python'ni "bitta universal model"ga yaqinlashtirmoqda, lekin asyncio I/O uchun alohida qolishi ehtimoli katta.
1. Go qanday qiladi
| Xususiyat | Go |
|---|---|
| Birlik | Goroutine (~2 KB stek, o'sadi) |
| Rejalashtirish | Runtime M:N — goroutine'lar OS iplariga taqsimlanadi |
| I/O | Runtime avtomatik "to'xtatib turadi" — bloklovchi ko'rinadigan kod asinxron ishlaydi |
| CPU | Barcha yadrolarda parallel |
| Narx | Umumiy xotira poygalari; runtime murakkabligi |
2. Nega Python'da bunday emas
- GIL (12.5-dars): CPython xotira boshqaruvini sodda va tez qilish uchun tanlangan — natijada iplar CPU uchun parallel emas
- C kengaytmalari: NumPy, lxml va boshqalar GIL ga tayanadi; runtime ularni "to'xtatib" qo'ya olmaydi
- Orqaga moslik: mavjud kodni buzmasdan yangi model faqat qo'shilishi mumkin edi
- Shuning uchun har muammoga alohida vosita paydo bo'ldi: jarayonlar (CPU),
asyncio(ko'p I/O), iplar (sinxron I/O)
3. Kamchilik tomoni
- O'rganish egri chizig'i tik: to'rt model, to'rt xil xato turi
- "Funksiya rangi" (14.6-dars) — ekotizim bo'linishi
- Noto'g'ri tanlov oson (kirishdagi vaziyatlar)
4. Afzallik tomoni
- Har model aniq kelishuvlarga ega:
asyncioda poyga faqatawaitda (14.7-dars), jarayonlarda umumiy xotira yo'q - Jarayon izolyatsiyasi — bitta ishchi yiqilsa, boshqalar ishlaydi
- Mavjud C ekotizimi to'liq saqlanadi
asynciobitta ipda o'n minglab ulanishni kamroq xotira bilan ushlaydi
5. Kelajak
| Yo'nalish | Ta'siri |
|---|---|
| Free-threading (PEP 703) | Iplar CPU uchun ham parallel — "iplar va jarayonlar" tanlovi yo'qolishi mumkin |
| Subinterpretatorlar (PEP 734) | Bir jarayonda izolyatsiyali parallellik — multiprocessing o'rnini qisman egallaydi |
asyncio |
I/O ko'p ulanishlar uchun qoladi — Go'da ham epoll ichkarida ishlaydi |
Ehtimoliy manzara: iplar (free-threaded) — umumiy parallellik, asyncio — ko'p ulanishli I/O, jarayonlar — izolyatsiya va ishonchlilik uchun.
6. Xulosa
- To'rt model — GIL va C ekotizimi bilan moslikning tarixiy narxi
- Tanlash qiyin, lekin har model o'z joyida kuchli va aniq kafolatlarga ega
- Free-threading va subinterpretatorlar tanlovni soddalashtiradi
- Qoida o'zgarmaydi: vaqt qayerga ketayotganini o'lchang, keyin tanlang
Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.
Xulosa
Bu darsda konkurentlik modellarini tanlash qoidalarini o'lchov bilan asosladik.
Eng muhim uch fikr:
Tanlov yuk turiga bog'liq — va uni o'lchash mumkin. CPU vaqti / devor vaqti nisbati ≈ 0 bo'lsa — I/O yuki, ≈ 1 bo'lsa — CPU yuki. I/O yukida
asynciova iplar ketma-ket koddan o'nlab barobar tezlashdi, CPU yukida esa ular hech narsa bermadi — faqat jarayonlar va subinterpretatorlar (har birining o'z GIL i bor) parallel ishladi.Real tizimlar gibrid. Aralash vazifada faqat iplar (hisob GIL da navbat kutadi) ham, faqat
asyncio(hisob loopni bloklaydi) ham sekin —asyncio+run_in_executor(ProcessPoolExecutor)ikkalasidan tez bo'ldi. Ishlab chiqarishdagi serverlar ham shunday: yadrolar uchun jarayonlar, har jarayon ichida ulanishlar uchun iplar yokiasyncio.Amdahl qonuni — parallellikning shipi. 90% parallel kod 16 yadroda atigi 6.4× tezlashadi. Ketma-ket qismni qisqartirish yadro qo'shishdan foydaliroq. Shuning uchun tartib doim bir xil: o'lchash → algoritm va kutubxona → keyin konkurentlik.
14-qism yakuni
| Dars | Mavzu | Asosiy g'oya |
|---|---|---|
| 14.1 | Jarayon, oqim, korutina | Uch birlikning narxi va kafolatlari |
| 14.2 | threading asoslari |
Thread, join, daemon; ThreadPoolExecutor |
| 14.3 | Race condition, Lock, Queue |
O'qish-o'zgartirish-yozish poygasi; deadlock; navbat bilan ishchilar |
| 14.4 | multiprocessing |
spawn, pickle, __main__ himoyasi; Queue, Pipe, shared_memory |
| 14.5 | Pool va concurrent.futures |
Future, as_completed; chunksize, initializer; muddat va bekor qilish |
| 14.6 | asyncio — hodisa sikli |
Korutina va Task; TaskGroup; bekor qilish; loopni bloklash |
| 14.7 | async / await amaliyoti |
Asinxron iterator va kontekst menejer; Queue; await dagi poyga; stream lar |
| 14.8 | Qaysi birini tanlash | Yuk turini o'lchash; qaror daraxti; gibrid; Amdahl |
3-bosqich yakuni: "Python ichidan"
14-qism bilan kursning uchinchi bosqichi tugadi. 35 darsda Python'ni tashqaridan emas, ichidan ko'rdik:
| Qism | Nimani tushunib oldik |
|---|---|
| 11 — Obyekt modeli va xotira | Havolalar, reference counting, GC, interning, __slots__, weakref |
| 12 — CPython ichki tuzilishi | Bytecode, frame lar, PyObject, GIL, 3.11+ tezlashuvlari |
| 13 — Turlar tizimi | Annotatsiyalar, generiklar, Protocol, mypy, pydantic, turlar bilan dizayn |
| 14 — Konkurentlik va parallellik | Iplar, jarayonlar, subinterpretatorlar, asyncio va ularni tanlash |
Endi siz Python kodini yozibgina qolmay, nega shunday ishlashini tushuntira olasiz: nega is ba'zan True, nega iplar hisobni tezlashtirmaydi, nega await dan keyin holat o'zgarishi mumkin.
Keyingi bosqich — "Kutubxona va professional vositalar". 15-qismda standart kutubxonaning eng ko'p ishlatiladigan modullaridan boshlaymiz: muntazam ifodalar, datetime, collections, itertools, pathlib, subprocess, logging — professional dasturchining kundalik asboblari.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!