⚡ Bolt: [performance improvement] Optimize async event loop execution in generate_variations_async#92
Conversation
Wrap `asyncio.as_completed` in a single async coroutine and use `loop.run_until_complete` only once. This prevents the event loop from being repeatedly blocked, stopped, and restarted on each iteration, enabling true concurrency. Co-authored-by: glacy <1131951+glacy@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Pull request overview
Refactors generate_variations_async to avoid repeatedly starting/stopping the asyncio event loop per completed future, enabling true concurrent execution of async variation generation tasks.
Changes:
- Wraps
asyncio.as_completed(...)consumption in a single async coroutine and callsloop.run_until_complete(...)once. - Adds explanatory “Bolt Optimization” comments in
evolutia_engine.py. - Updates
.jules/bolt.mdwith an asyncio event-loop handling note.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| evolutia/evolutia_engine.py | Runs all async tasks within one coroutine to avoid run_until_complete() inside the completion loop. |
| .jules/bolt.md | Replaces prior Bolt notes with an asyncio guidance entry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for coro in tqdm(asyncio.as_completed(async_tasks), total=len(async_tasks), desc="Generando"): | ||
| try: | ||
| result = await coro |
| **Learning:** `re.findall(pattern, string)` recompiles (or retrieves from cache) the pattern on every call. In high-frequency functions called inside loops (like complexity estimation), this overhead adds up. | ||
| **Action:** Always pre-compile regexes (`re.compile`) into module-level or class-level constants if they are used repeatedly, especially in tight loops or recursive functions. | ||
| ## 2024-05-18 - [Optimized Asyncio Event Loop Handling] | ||
| **Learning:** `asyncio.as_completed()` yields coroutines. Calling `loop.run_until_complete(coro)` for each yielded coroutine inside a synchronous for-loop is a major anti-pattern. It repeatedly blocks the main thread, stops the event loop, and restarts it for every single task, completely defeating concurrency. |
| ## 2024-05-18 - [Optimized Asyncio Event Loop Handling] | ||
| **Learning:** `asyncio.as_completed()` yields coroutines. Calling `loop.run_until_complete(coro)` for each yielded coroutine inside a synchronous for-loop is a major anti-pattern. It repeatedly blocks the main thread, stops the event loop, and restarts it for every single task, completely defeating concurrency. | ||
| **Action:** Always wrap concurrent task aggregation in a single overarching async function (e.g., `async def run_all()`) where you `await` the individual tasks, and call `loop.run_until_complete(run_all())` exactly once to keep the event loop running smoothly. |
💡 What: Refactored the event loop execution logic in
generate_variations_async(evolutia/evolutia_engine.py). Instead of repeatedly callingloop.run_until_complete()inside a synchronousforloop for every future yielded byasyncio.as_completed(), all tasks are now awaited within a single overarching async coroutine (run_all), andrun_until_complete()is invoked exactly once.🎯 Why: The previous implementation was a significant asyncio anti-pattern. Calling
run_until_complete()inside a loop blocks the main thread, stops the event loop, and restarts it for every single task. This completely defeats the purpose of running tasks concurrently within an ongoing event loop, severely bottlenecking performance during I/O-bound operations like LLM API calls.📊 Impact: This drastically reduces execution overhead and allows true concurrency for asynchronous variation generation tasks. The event loop is now started once and all tasks are processed within it correctly.
🔬 Measurement: Verified by ensuring the code executes cleanly and passes all test cases correctly.
Also added comments explaining the specific optimization and its reasoning.
PR created automatically by Jules for task 3793013430462545519 started by @glacy