Skip to content

perf(algo,llm): run in-memory compute and LLM extraction tasks on the waiting thread - #147

Open
zahariash wants to merge 1 commit into
LadybugDB:mainfrom
zahariash:perf/tasks-on-waiting-thread
Open

zahariash wants to merge 1 commit into
LadybugDB:mainfrom
zahariash:perf/tasks-on-waiting-thread

Conversation

@zahariash

Copy link
Copy Markdown
Contributor

This work was produced with the help of language models.

Follow-up to LadybugDB/ladybug#1582, which made the engine's GDS frontier and vertex tasks run on the waiting thread.

InMemGDSUtils::runParallelCompute (Louvain, spanning forest) and the request batches of AI_EXTRACT and AI_EXTRACT_RECORD still passed launchNewWorkerThread = true, so every call started a thread to take the caller's scheduler slot while the caller slept. CALL LOUVAIN on a 200,000-node, 1.19M-edge graph started 173 threads per query, at 1 thread and at 16.

Fix. All three calls use the default false. A one-thread task then runs inline (since 0.22.0), and since LadybugDB/ladybug#1482 a waiting thread that holds a slot also works on any other task it waits for, so the Louvain query starts no threads. A literal call folded at bind time holds no slot: its one-row task runs on the binding thread when a slot is free, and otherwise (every slot busy, or a query timeout) waits for a worker instead of getting its own thread.

Needs LadybugDB/ladybug#1482, which is in no release yet. On 0.22.0–0.22.3 a waiting thread sleeps on any task it can't run inline (two or more threads, or a query timeout), so a query hangs when no other slot is free, for example Louvain after CALL threads=2 on a one-thread database. The 0.22.0 extensions serve every 0.22.x engine, so this should ship with the 0.23.0 extensions, not a 0.22.x patch.

Tests. The algo and llm tests pass, also with MAX_NUM_THREADS=1. They can't reliably keep every slot busy, so the engine's DenseRecursiveJoinWithOneThread covers the waiting-thread path. The llm tests stop at bind errors and NULL input, so the request path was checked against a local OpenAI-compatible mock. On both builds, Louvain on a one-thread database after CALL threads=2, also under a query timeout, finds the same 528 communities, and AI_EXTRACT and AI_EXTRACT_RECORD return all 40 rows with the same peak concurrency (4 requests at threads=4, 1 on the one-thread database at threads=2). Folded literal calls return.

Cost. None measured. Louvain at 1 thread runs 1.9% fewer instructions per query (36.65G instead of 37.35G) with the same 87.1M mallocs. Wall time is within noise: 3.75 → 3.69 s at 1 thread and 1.00 → 0.98 s at 16 (mean of three rounds' medians of 9; one round at 1 thread went the other way).

… waiting thread

InMemGDSUtils::runParallelCompute and the request batches of AI_EXTRACT and
AI_EXTRACT_RECORD passed launchNewWorkerThread = true, so every call started a
thread to take the caller's scheduler slot. A one-thread task runs inline
without it, and since ladybug PR 1482 a waiting thread that holds a slot also
works on any other task it waits for, so all three calls now use the default.
CALL LOUVAIN on a 200,000-node graph started 173 threads per query; it now
starts none.

Needs an engine with ladybug PR 1482: on 0.22.0-0.22.3 the waiting thread
sleeps on a task of two or more threads, and on any task under a query timeout,
so such a query hangs when no other slot is free.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant