Repository navigation
perf: parallelize embedAll with sliding worker pool (30x speedup) - #35
Closed
stephenhungg wants to merge 1 commit into
Closed
stephenhungg wants to merge 1 commit into
stephenhungg wants to merge 1 commit into
Conversation
Before: embedAll processed pages sequentially, one embedBatch call at a time. On a 20k-chunk corpus this ran at ~2 chunks/sec, taking ~2.5 hours. After: a sliding worker pool of N concurrent workers share a queue and each pulls the next page as soon as it finishes. Throughput on the same workload jumped to ~60 chunks/sec — a 30x speedup, finishing in ~8 min. Concurrency defaults to 20 (safely under OpenAI tier-1 embedding RPM limits and gentle on postgres connection pools) and is tunable via GBRAIN_EMBED_CONCURRENCY. Why a sliding pool over fixed-window Promise.all: per-page workloads are uneven (some pages have 1 chunk, others have 50), so fixed windows leave fast workers idling while slow ones finish. The sliding pool keeps every worker saturated. Adds test/embed.test.ts covering: - concurrent execution (max concurrent > 1, <= configured limit) - GBRAIN_EMBED_CONCURRENCY=1 serial fallback - --stale skipping pages whose chunks are already embedded
Owner
|
Included in fix wave PR #65 (v0.9.1). 30x embedAll speedup landed with the sliding worker pool. Thanks stephenhungg! 🙏 |
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
gbrain embed --allcurrently processes pages sequentially, making oneembedBatchcall at a time. On real-world corpora this is painfully slow — on a 20k-chunk personal knowledge base I measured ~2 chunks/sec, which meant embedding from scratch took ~2.5 hours.This PR parallelizes the loop with a sliding worker pool. Same workload now runs at ~60 chunks/sec, finishing in ~8 minutes. That's a ~30x speedup, entirely bottlenecked on OpenAI's embedding API rather than on the client.
Design
nextIdxqueue; each worker pulls the next page as soon as it finishes its current one.GBRAIN_EMBED_CONCURRENCYenv var for users on higher tiers or tighter infra.processed/total pages, embedded chunks) still works — writes are just interleaved across workers.Why a sliding pool over fixed-window
Promise.all?Per-page chunk counts are uneven (some pages have 1 chunk, others 50). A fixed window of
Promise.allchunks of size N leaves fast workers idling while the slowest in the window finishes. A sliding pool keeps every worker saturated, which matters more the more skewed the distribution is.Benchmarks
Tested on a real 19,507-page / 20,693-chunk gbrain import (life-wiki):
No observed rate-limit errors from OpenAI at concurrency=20 on a tier-1 key.
Tests
Adds
test/embed.test.tswith mockedembedBatch+ Proxy-based mock engine (matching the pattern intest/import-file.test.ts):embedBatchcalls is> 1and<= CONCURRENCYlimitGBRAIN_EMBED_CONCURRENCY=1keeps max concurrent at exactly 1--staleskip — pages whose chunks are already embedded don't trigger anembedBatchcallFull suite still green:
259 pass, 0 fail.Test plan
bun test— all existing tests passbun test test/embed.test.ts— new concurrency tests pass