|
1 |
| -# Object Streaming Examples |
| 1 | +# Object Streaming |
2 | 2 |
|
3 | 3 | ## Overview
|
4 |
| -The examples here demonstrate how to use object streamers to send large file/objects memory efficiently. |
| 4 | +The examples here demonstrate how to use object streamers to send large objects in a memory-efficient manner. |
5 | 5 |
|
6 |
| -The object streamer uses less memory because it sends files by chunks (default chunk size is 1MB) and |
7 |
| -it sends containers entry by entry. |
| 6 | +Current default setting is to send and receive large objects in full, so extra memory will be needed and allocated to hold the received message. |
| 7 | +This works fine when the message is small, but can become a limit when model size is large, e.g. for large language models. |
8 | 8 |
|
9 |
| -For example, if you have a dict with 10 1GB entries, it will take 10GB extra space to send the dict without |
10 |
| -streaming. It only requires extra 1GB to serialize the entry using streaming. |
| 9 | +To save on memory usage, we can stream the message send / receive: when sending large objects (e.g. a dict), |
| 10 | +streamer sends containers entry by entry (e.g. one dict item each time); further, if we save the object to a file, |
| 11 | +streamer can send the file by chunks (default chunk size is 1MB). |
11 | 12 |
|
| 13 | +Thus, the memory demand can be reduced to the size of the largest entry for container streaming; while nearly no extra memory is needed for file |
| 14 | +streaming. For example, if sending a dict with 10 1GB entries, without streaming, it will take 10GB extra space to send the dict. |
| 15 | +With container streaming, it only requires extra 1GB; and if saved to a file before sending, it only requires 1MB extra space to send the file. |
| 16 | + |
| 17 | +All examples are run with NVFlare Simulator via [JobAPI](https://nvflare.readthedocs.io/en/main/programming_guide/fed_job_api.html). |
12 | 18 | ## Concepts
|
13 | 19 |
|
14 | 20 | ### Object Streamer
|
15 |
| - |
16 |
| -ObjectStreamer is a base class to stream an object piece by piece. The `StreamableEngine` built in the NVFlare can |
| 21 | +ObjectStreamer is the base class to stream an object piece by piece. The `StreamableEngine` built in the NVFlare can |
17 | 22 | stream any implementations of ObjectSteamer
|
18 | 23 |
|
19 |
| -Following implementations are included in NVFlare, |
| 24 | +The following implementations are included in NVFlare, |
20 | 25 |
|
21 |
| -* `FileStreamer`: It can be used to stream a file |
22 |
| -* `ContainerStreamer`: This class can stream a container entry by entry. Currently, dict, list and set are supported |
| 26 | +* `ContainerStreamer`: This class is used to stream a container entry by entry. Currently, dict, list and set are supported |
| 27 | +* `FileStreamer`: This class is used to stream a file |
23 | 28 |
|
24 |
| -The container streamer can only stream the top level entries. All the sub entries of a top entry are sent at once with |
25 |
| -the top entry. |
| 29 | +Note that the container streamer split the stream by the top level entries. All the sub entries of a top entry are expected to be |
| 30 | +sent as a whole, therefore the memory is determined by the largest entry at top level. |
26 | 31 |
|
27 | 32 | ### Object Retriever
|
28 |
| - |
29 |
| -`ObjectRetriever` is designed to request an object to be streamed from a remote site. It automatically sets up the streaming |
| 33 | +Building upon the streamers, `ObjectRetriever` is designed for easier integration with existing code: to request an object to be streamed from a remote site. It automatically sets up the streaming |
30 | 34 | on both ends and handles the coordination.
|
31 | 35 |
|
32 |
| -Currently, following implementations are available, |
33 |
| - |
34 |
| -* `FileRetriever`: It's used to retrieve a file from remote site using FileStreamer. |
35 |
| -* `ContainerRetriever`: This class can be used to retrieve a container from remote site using ContainerStreamer. |
| 36 | +Similarly, the following implementations are available, |
36 | 37 |
|
37 |
| -To use ContainerRetriever, the container must be given a name and added on the sending site, |
| 38 | +* `ContainerRetriever`: This class is used to retrieve a container from remote site using `ContainerStreamer`. |
| 39 | +* `FileRetriever`: This class is used to retrieve a file from remote site using `FileStreamer`. |
38 | 40 |
|
| 41 | +Note that to use ContainerRetriever, the container must be given a name and added on the sending site, |
39 | 42 | ```
|
40 | 43 | ContainerRetriever.add_container("model", model_dict)
|
41 | 44 | ```
|
42 | 45 |
|
43 |
| -## Example Jobs |
| 46 | +## Simple Examples |
| 47 | +First, we demonstrate how to use the Streamer directly without Retriever: |
| 48 | +```commandline |
| 49 | +python simple_file_streaming_job.py |
| 50 | +``` |
| 51 | +Note that in this example, the file streaming is relatively "standalone", as the `FileReceiver` and `FileSender` |
| 52 | +are used directly as components, and no training workflow is used - as executor is required by NVFlare, here we used |
| 53 | +a dummy executor. |
| 54 | + |
| 55 | +Although the file streaming is simple, it is not very practical for real-world applications, because |
| 56 | +in most cases, rather than standalone, we need to send an object when it is generated at certain point in the workflow. In such cases, |
| 57 | +Retriever is more convenient to use: |
| 58 | +```commandline |
| 59 | +python simple_dict_streaming_job.py |
| 60 | +``` |
| 61 | +In this second example, the `ContainerRetriever` is setup in both server and client, and will automatically handle the streaming. |
| 62 | +It couples closely with the workflow, and is easier to define what to send and where to retrieve. |
| 63 | + |
| 64 | +## Full-scale Examples and Comparisons |
| 65 | +The above two simple examples illustrated the basic usage of streaming with random small messages. In the following, |
| 66 | +we will demonstrate how to use the streamer with Retriever in a workflow with real large language model object, |
| 67 | +and compare the memory usage with and without streaming. To track the memory usage, we use a simple script `utils/log_memory.sh`. |
| 68 | +Note that the tracked usage is not fully accurate, but it is sufficient to give us a rough idea. |
| 69 | + |
| 70 | +All three settings: regular, container streaming, and file streaming, are integrated in the same script to avoid extra variabilities. |
| 71 | +To run the examples: |
| 72 | +```commandline |
| 73 | +bash regular_transmission.sh |
| 74 | +``` |
| 75 | +```commandline |
| 76 | +bash container_stream.sh |
| 77 | +``` |
| 78 | +```commandline |
| 79 | +bash file_stream.sh |
| 80 | +``` |
44 | 81 |
|
45 |
| -### file_streaming job |
| 82 | +We then examine the memory usage by comparing the peak memory usage of the three settings. The results are shown below, |
| 83 | +note that the numbers here are the results of one experiment on one machine, and can be highly variable depending on the system and the environment. |
| 84 | + |
| 85 | +| Setting | Peak Memory Usage (MB) | Job Finishing Time (s) | |
| 86 | +| --- | --- | --- | |
| 87 | +| Regular Transmission | 42,427 | 47 |
| 88 | +| Container Streaming | 23,265 | 50 |
| 89 | +| File Streaming | 19,176 | 170 |
| 90 | + |
| 91 | +As shown, the memory usage is significantly reduced by using streaming, especially for file streaming, |
| 92 | +while file streaming takes much longer time to finish the job. |
46 | 93 |
|
47 |
| -This job uses the FileStreamer object to send a large file from server to client. |
48 | 94 |
|
49 |
| -It demonstrates following mechanisms: |
50 |
| -1. It uses components to handle the file transferring. No training workflow is used. |
51 |
| - Since executor is required by NVFlare, a dummy executor is created. |
52 |
| -2. It shows how to use the streamer directly without an object retriever. |
53 | 95 |
|
54 |
| -The job creates a temporary file to test. You can run the job in POC or using simulator as follows, |
55 | 96 |
|
56 |
| -``` |
57 |
| -nvflare simulator -n 1 -t 1 jobs/file_streaming |
58 |
| -``` |
59 |
| -### dict_streaming job |
60 | 97 |
|
61 |
| -This job demonstrate how to send a dict from server to client using object retriever. |
62 | 98 |
|
63 |
| -It creates a task called "retrieve_dict" to tell client to get ready for the streaming. |
64 | 99 |
|
65 |
| -The example can be run in simulator like this, |
66 |
| -``` |
67 |
| -nvflare simulator -n 1 -t 1 jobs/dict_streaming |
68 |
| -``` |
|
0 commit comments