Initial setup
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
### Day 1 — GIL + CPU-bound vs I/O-bound threading
|
||||
|
||||
#### Goals (what "done" means)
|
||||
- [ ] I can explain the GIL in 2–3 sentences and why it impacts **CPU-bound** threading more than **I/O-bound** threading.
|
||||
- [ ] I can choose between threads, processes, and asyncio for a given workload and justify it.
|
||||
- [ ] I have benchmark results (numbers) that demonstrate the above.
|
||||
|
||||
#### Reading (mostly)
|
||||
- [ ] Read: [What Is the Python Global Interpreter Lock (GIL)? (Real Python)](https://realpython.com/python-gil/)
|
||||
- [ ] Write 5 bullets: what the GIL is, what it is *not*, and why it exists.
|
||||
- [ ] Read: [Understanding the Python GIL (PDF)](http://www.dabeaz.com/python/UnderstandingGIL.pdf)
|
||||
- [ ] Write 5 bullets: where threads *do* help, where they don’t, and what "switch interval" means (high level).
|
||||
- [ ] Skim (reference): [concurrent.futures — Python docs](https://docs.python.org/3/library/concurrent.futures.html)
|
||||
- [ ] Note the difference between `ThreadPoolExecutor` vs `ProcessPoolExecutor`.
|
||||
|
||||
#### Hands-on (benchmark + notes)
|
||||
- [ ] Create a benchmark script that compares these variants for a **CPU-bound** function:
|
||||
- [ ] Serial (single process, no threads)
|
||||
- [ ] `ThreadPoolExecutor`
|
||||
- [ ] `ProcessPoolExecutor`
|
||||
- [ ] Use a **repeatable** timing method (pick one):
|
||||
- [ ] `timeit` (preferred for small benchmarks): [timeit — Python docs](https://docs.python.org/3/library/timeit.html)
|
||||
- [ ] or wall-clock timing with `time.perf_counter()` (fine for longer tasks)
|
||||
- [ ] Record results in a small table (example format):
|
||||
- [ ] workload size parameters (so you can reproduce)
|
||||
- [ ] runtime for each approach
|
||||
- [ ] notes on CPU utilization / behavior
|
||||
- [ ] Add an **I/O-bound** comparison:
|
||||
- [ ] Serial loop calling `time.sleep(x)`
|
||||
- [ ] Thread pool version (expect improvement)
|
||||
- [ ] Write "Conclusions" (minimum 6 sentences):
|
||||
- [ ] What happened for CPU-bound?
|
||||
- [ ] What happened for I/O-bound?
|
||||
- [ ] What I would choose in real systems and why
|
||||
|
||||
#### Stretch (optional)
|
||||
- [ ] Skim: [PEP 703 – Making the Global Interpreter Lock Optional in CPython](https://peps.python.org/pep-0703/)
|
||||
- [ ] Write 3 bullets: what changes, tradeoffs, and why it matters.
|
||||
|
||||
#### Deliverables (to commit)
|
||||
- [ ] `notes.md` updated with:
|
||||
- [ ] explanation bullets
|
||||
- [ ] benchmark table
|
||||
- [ ] conclusions
|
||||
- [ ] `benchmark.py` (or similar) added with instructions to run
|
||||
|
||||
#### Suggested commits
|
||||
- [ ] `day01: add GIL notes + benchmark scaffolding`
|
||||
- [ ] `day01: record CPU vs IO benchmark results and conclusions`
|
||||
@@ -0,0 +1,60 @@
|
||||
### Day 2 — asyncio fundamentals (event loop, tasks, gather)
|
||||
|
||||
#### Goals (what "done" means)
|
||||
- [ ] I can explain the event loop + coroutines + tasks in plain language.
|
||||
- [ ] I can write a small asyncio program using `create_task` + `gather`.
|
||||
- [ ] I can add timeouts, cancellation handling, and concurrency limits.
|
||||
|
||||
#### Reading (official first)
|
||||
- [ ] Read (core usage): [Coroutines and Tasks — Python docs](https://docs.python.org/3/library/asyncio-task.html)
|
||||
- [ ] Note what each does: `asyncio.run`, `asyncio.create_task`, `asyncio.gather`, cancellation.
|
||||
- [ ] Read (practical walkthrough): [Python’s asyncio: A Hands-On Walkthrough (Real Python)](https://realpython.com/async-io-python/)
|
||||
- [ ] Write 5 bullets: when asyncio is a good fit, and when it’s not.
|
||||
- [ ] Read (debugging): [Developing with asyncio — Python docs](https://docs.python.org/3/library/asyncio-dev.html#debug-mode)
|
||||
- [ ] Note 2 debugging tips you can reuse later.
|
||||
|
||||
#### Hands-on (build incrementally)
|
||||
1. **Minimal concurrency demo (no external libs)**
|
||||
- [ ] Write `async def worker(i):` that does `await asyncio.sleep(...)` and returns a value
|
||||
- [ ] Run N workers concurrently with `asyncio.gather`
|
||||
- [ ] Print start/end timestamps to show concurrency
|
||||
|
||||
2. **Add concurrency limit**
|
||||
- [ ] Add `asyncio.Semaphore(k)` and wrap the worker body so only `k` run at once
|
||||
- [ ] Demonstrate by running N=50 with k=5 and showing batching behavior
|
||||
|
||||
3. **Add timeouts + error isolation**
|
||||
- [ ] Wrap each worker with `asyncio.wait_for(..., timeout=...)`
|
||||
- [ ] Make some workers intentionally exceed timeout
|
||||
- [ ] Use `gather(..., return_exceptions=True)` and summarize:
|
||||
- [ ] successes
|
||||
- [ ] timeouts
|
||||
- [ ] other exceptions
|
||||
|
||||
4. **Cancellation behavior**
|
||||
- [ ] Trigger cancellation of a running task and confirm you handle `asyncio.CancelledError` cleanly
|
||||
- [ ] Write 3 bullets: what cancellation means and how to design for it
|
||||
|
||||
#### Write-up (in notes)
|
||||
- [ ] "Mental model" section (8–10 bullets):
|
||||
- [ ] coroutine vs task
|
||||
- [ ] scheduling
|
||||
- [ ] awaiting vs creating tasks
|
||||
- [ ] why blocking calls break concurrency
|
||||
- [ ] "Pitfalls" section (at least 5 bullets):
|
||||
- [ ] blocking I/O
|
||||
- [ ] CPU-bound work in asyncio
|
||||
- [ ] forgetting to await
|
||||
- [ ] unhandled exceptions in tasks
|
||||
- [ ] cancellation edge cases
|
||||
|
||||
#### Deliverables (to commit)
|
||||
- [ ] `notes.md` updated with:
|
||||
- [ ] mental model bullets
|
||||
- [ ] code snippets or links to your local scripts
|
||||
- [ ] brief results/observations
|
||||
- [ ] `asyncio_demo.py` (or similar) added with instructions to run
|
||||
|
||||
#### Suggested commits
|
||||
- [ ] `day02: add asyncio notes + minimal gather demo`
|
||||
- [ ] `day02: add semaphore, timeout, and cancellation examples`
|
||||
Reference in New Issue
Block a user