forked from ray-project/ray
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Doc] Streaming generator alpha doc (ray-project#39914)
This is a PR to write a streaming generator documentation. Unlike existing generator doc, streaming generator doc is under "advanced" usage, and I will add proper links from each task/actor/pattern pages to this doc. The doc is written with the assumption is is an "alpha" feature. It is important to merge this doc PR before proceeding with ray-project#38784 as it will need to refer docs here and there. Some of the contents (such as "API is subject to change") will be removed after we merge these PRs.
- Loading branch information
Showing
7 changed files
with
442 additions
and
7 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
# flake8: noqa | ||
|
||
# fmt: off | ||
|
||
# __streaming_generator_define_start__ | ||
import ray | ||
import time | ||
|
||
@ray.remote(num_returns="streaming") | ||
def task(): | ||
for i in range(5): | ||
time.sleep(5) | ||
yield i | ||
|
||
# __streaming_generator_define_end__ | ||
|
||
# __streaming_generator_execute_start__ | ||
gen = task.remote() | ||
# Blocks for 5 seconds. | ||
ref = next(gen) | ||
# return 0 | ||
ray.get(ref) | ||
# Blocks for 5 seconds. | ||
ref = next(gen) | ||
# Return 1 | ||
ray.get(ref) | ||
|
||
# Returns 2~4 every 5 seconds. | ||
for ref in gen: | ||
print(ray.get(ref)) | ||
|
||
# __streaming_generator_execute_end__ | ||
|
||
# __streaming_generator_exception_start__ | ||
@ray.remote(num_returns="streaming") | ||
def task(): | ||
for i in range(5): | ||
time.sleep(1) | ||
if i == 1: | ||
raise ValueError | ||
yield i | ||
|
||
gen = task.remote() | ||
# it's okay. | ||
ray.get(next(gen)) | ||
|
||
# Raises an exception | ||
try: | ||
ray.get(next(gen)) | ||
except ValueError as e: | ||
print(f"Exception is raised when i == 1 as expected {e}") | ||
|
||
# __streaming_generator_exception_end__ | ||
|
||
# __streaming_generator_actor_model_start__ | ||
@ray.remote | ||
class Actor: | ||
def f(self): | ||
for i in range(5): | ||
yield i | ||
|
||
@ray.remote | ||
class AsyncActor: | ||
async def f(self): | ||
for i in range(5): | ||
yield i | ||
|
||
@ray.remote(num_concurrency=5) | ||
class ThreadedActor: | ||
def f(self): | ||
for i in range(5): | ||
yield i | ||
|
||
actor = Actor.remote() | ||
for ref in actor.f.options(num_returns="streaming").remote(): | ||
print(ray.get(ref)) | ||
|
||
actor = AsyncActor.remote() | ||
for ref in actor.f.options(num_returns="streaming").remote(): | ||
print(ray.get(ref)) | ||
|
||
actor = ThreadedActor.remote() | ||
for ref in actor.f.options(num_returns="streaming").remote(): | ||
print(ray.get(ref)) | ||
|
||
# __streaming_generator_actor_model_end__ | ||
|
||
# __streaming_generator_asyncio_start__ | ||
import asyncio | ||
|
||
@ray.remote(num_returns="streaming") | ||
def task(): | ||
for i in range(5): | ||
time.sleep(1) | ||
yield i | ||
|
||
|
||
async def main(): | ||
async for ref in task.remote(): | ||
print(await ref) | ||
|
||
asyncio.run(main()) | ||
|
||
# __streaming_generator_asyncio_end__ | ||
|
||
# __streaming_generator_gc_start__ | ||
@ray.remote(num_returns="streaming") | ||
def task(): | ||
for i in range(5): | ||
time.sleep(1) | ||
yield i | ||
|
||
gen = task.remote() | ||
ref1 = next(gen) | ||
del gen | ||
|
||
# __streaming_generator_gc_end__ | ||
|
||
# __streaming_generator_concurrency_asyncio_start__ | ||
import asyncio | ||
|
||
@ray.remote(num_returns="streaming") | ||
def task(): | ||
for i in range(5): | ||
time.sleep(1) | ||
yield i | ||
|
||
|
||
async def async_task(): | ||
async for ref in task.remote(): | ||
print(await ref) | ||
|
||
async def main(): | ||
t1 = async_task() | ||
t2 = async_task() | ||
await asyncio.gather(t1, t2) | ||
|
||
asyncio.run(main()) | ||
# __streaming_generator_concurrency_asyncio_end__ | ||
|
||
# __streaming_generator_wait_simple_start__ | ||
@ray.remote(num_returns="streaming") | ||
def task(): | ||
for i in range(5): | ||
time.sleep(5) | ||
yield i | ||
|
||
gen = task.remote() | ||
|
||
# Because it takes 5 seconds to make the first yield, | ||
# with 0 timeout, the generator is unready. | ||
ready, unready = ray.wait([gen], timeout=0) | ||
print("timeout 0, nothing is ready.") | ||
print(ready) | ||
assert len(ready) == 0 | ||
assert len(unready) == 1 | ||
|
||
# Without a timeout argument, ray.wait waits until the given argument | ||
# is ready. When a next item is ready, it returns. | ||
ready, unready = ray.wait([gen]) | ||
print("Wait for 5 seconds. The next item is ready.") | ||
assert len(ready) == 1 | ||
assert len(unready) == 0 | ||
next(gen) | ||
|
||
# Because the second yield hasn't happened yet, | ||
ready, unready = ray.wait([gen], timeout=0) | ||
print("Wait for 0 seconds. The next item is not ready.") | ||
print(ready, unready) | ||
assert len(ready) == 0 | ||
assert len(unready) == 1 | ||
|
||
# __streaming_generator_wait_simple_end__ | ||
|
||
# __streaming_generator_wait_complex_start__ | ||
from ray._raylet import StreamingObjectRefGenerator | ||
|
||
@ray.remote(num_returns="streaming") | ||
def generator_task(): | ||
for i in range(5): | ||
time.sleep(5) | ||
yield i | ||
|
||
@ray.remote | ||
def regular_task(): | ||
for i in range(5): | ||
time.sleep(5) | ||
return | ||
|
||
gen = [generator_task.remote()] | ||
ref = [regular_task.remote()] | ||
ready, unready = [], [*gen, *ref] | ||
result = [] | ||
|
||
while unready: | ||
ready, unready = ray.wait(unready) | ||
for r in ready: | ||
if isinstance(r, StreamingObjectRefGenerator): | ||
try: | ||
ref = next(r) | ||
result.append(ray.get(ref)) | ||
except StopIteration: | ||
pass | ||
else: | ||
unready.append(r) | ||
else: | ||
result.append(ray.get(r)) | ||
|
||
# __streaming_generator_wait_complex_end__ |
Oops, something went wrong.