-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[Draft] Async-based parallelism #1183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…c guidance functions
…ts in bg event loop
|
So the synchronous versions just do a |
We're maintaining a single long-lived event loop in a daemon thread (which has its own implications I suppose), so we just submit the coroutine and block the main thread until it's ready. The nice thing is that this is only happening at the very top-level entry point, so we don't need multiple threads or anything like that to support recursive calls. Getting that working without deadlocks was an an interesting exercise -- more than happy to look at that code together! |
|
Codecov ReportAttention: Patch coverage is
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## main #1183 +/- ##
===========================================
+ Coverage 40.63% 55.73% +15.10%
===========================================
Files 62 63 +1
Lines 4782 4972 +190
===========================================
+ Hits 1943 2771 +828
+ Misses 2839 2201 -638 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Thanks for the push here @hudson-ai -- fantastic work, really. I've always been a huge fan of Jax's async dispatch model, and want to better understand why benefit 2 will no longer apply in an async dispatch world. Can't we keep e.g. a debounce buffer that batches objects as much as we can, thereby getting most of the benefit anyway?
|
Thanks @Harsha-Nori! And I appreciate the input / question. I don't honestly know the answer -- maybe some kind of buffering would work. Just going to think out loud a bit... Let's say we have a chain of lm objects: lm_1 = lm + foo(name="foo")
lm_2 = lm_1 + bar(name="bar")
lm_3 = lm_2 + baz(name="baz")With lazy execution as it's implemented in this PR, nothing gets executed until we do something like I'm imagining that if we did async dispatch + eager execution (no buffering), each of With debounce-style buffering, we could track parent-child relationships, and noting that This doesn't seem too bad, but I think the story gets far more complicated once we start having branching calls / arbitrary DAGs. E.g. for _ in range(100):
lm += qux()
lm_1 = lm
for _ in range(100):
lm_1 += foo()
lm2 = lm
for _ in range(100):
lm_2 += bar()
If we can figure out the right way to do this "back-filling", I kind of like the idea. But it's also a bit spooky... Thoughts? |
|
Some kind of @nopdive I know you're a fan of async dispatch. Any thoughs on your end? |
|
Notes / status update for anyone watching this --
|
Behavior changes:
lm += foo()always returns immediately)str(lm),lm[key], etc.Introduces:
await lm.get_async(key)(API is still a WIP)@guidancedecorator onasync deffunctionsNote: async accessors are fully compatible with non-async guidance functions (even stateful ones). I.e. you don't have to rewrite your existing guidance functions as async to get the concurrency benefits of async accessors farther up the stack.
Here's an example usage:
@guidancefunctionextract_image_data-- it does not need to be aware that its callers may be async!@guidancefunctionget_and_describe_imagethat uses external async functions, namely thegetmethod of anhttpx.AsyncClient.Modelobject (lm) are disallowed inside of async@guidancefunctions and will raise an exception. We could probably "fix" this, but it's honestly kind of a nice safeguard against shooting ourselves in the foot.mainfunction that gathers some number of coroutines returned by an async accessor on each of our unevaluated guidance programs.@guidancefunctions can also be naively parallelized (regardless of whether or not they are async) via thebatchedentrypoints:Note that these entrypoints actually run the functions and are not lazy like
+=is.TODOs:
token_counton state