perf: parallelize page-memory title_detection and node_assembly VLM calls#207
Closed
suguanYang wants to merge 1 commit into
Closed
perf: parallelize page-memory title_detection and node_assembly VLM calls#207suguanYang wants to merge 1 commit into
suguanYang wants to merge 1 commit into
Conversation
…alls Both stages ran their per-page VLM calls in a plain serial loop with zero concurrency, unlike sibling stages (tag_pages, hierarchy scopes, asset summaries) that already use a bounded GeventPool. Staging traces showed these two stages dominating v2 job time (avg 236s and 143s per job). - Add title_detection_concurrency/node_assembly_concurrency to PageMemoryConfig, threaded into the two stage call sites. - Parallelize tag_page_titles via the same GeventPool idiom used by tag_pages in the same file. - Split build_node_rows into two pooled phases (page-text resolution, then node summaries) with an index-preserving merge so row order stays stable regardless of greenlet completion order. - Retry once on empty VLM results in _vlm_node_summary/resolve_page_text before falling back to the existing degrade-gracefully behavior.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
title_detection(avg 236s/job) andnode_assembly(avg 143s/job).forloop with zero concurrency, unlike sibling stages in the same codebase (tag_pages, hierarchy-scope processing, asset summaries) that already use a boundedGeventPoolidiom and get real parallelism out of it. This worker runs Celery with--pool=gevent, so the same idiom genuinely overlaps VLM round-trips instead of just interleaving.PageMemoryConfiggainstitle_detection_concurrencyandnode_assembly_concurrency(default4, mirroringscope_concurrency/tag_concurrency), threaded into the two stage call sites inmemory_service.py.page_tagger.tag_page_titlesnow runs its VLM calls through aGeventPool, mirroringtag_pagesin the same file. Results are merged by page index aftergevent.joinall, so there's no concurrent mutation of shared tag objects.node_assembler.build_node_rows's two loops (page-text resolution, then node summaries) each get their own bounded pool. The second pool still runs strictly after the first joins, since node summaries read resolved page text. Node-summary results are written into an index-addressed list so row order is preserved regardless of greenlet completion order (existing tests assert row order)._vlm_node_summaryandresolve_page_text(node_assembler's two VLM call sites) now retry once on an empty result (5s backoff) before falling back to the existing silent-degradation behavior — previously a single transient failure would silently produce a degraded summary/empty OCR text with no retry at all.GeventPool(size=min(n, len(items))) → pool.spawn → gevent.joinallpattern was already duplicated at 8 call sites with no shared helper; this adds 2 more inline copies rather than extracting a generic utility.max_keepalive_connections=20ceiling and DashScope's per-key RPM=300 limit.Verification
cd apps/worker && python -m pytest tests/contract/ -k "page_memory" -v→ 68 passed (58 pre-existing + 10 new)cd apps/worker && python -m pytest tests/ -q→ 134 passed, no regressionsPageMemoryConfigdefaults,from_mappingoverrides, andto_dict()round-trip for the two new fieldstest_page_memory_node_assembler_contract.py: row-order preservation under concurrent node summaries, retry-then-succeed and retry-exhausted cases for both_vlm_node_summaryandresolve_page_texttest_page_memory_page_tagger_contract.py(new file): order preservation under concurrent title detection, image-missing skip behavior, no-op when no fat-leaf pagesStage completed: page_memory.title_detection/page_memory.node_assembly) to confirm the expected ~4x reduction, and watching for any new rate-limit warning volume from the added retries.Deployment Notes
PageMemoryConfigfields resolved from job metadata (default4when absent), consistent with existing sibling knobs.title_detection_concurrency/node_assembly_concurrencyin their metadata fall back to the default of4, preserving current behavior modulo the concurrency and retry changes.Checklist