Skip to content

Commit 92959f4

Browse files
Songhao Jiafacebook-github-bot
authored andcommitted
restructure (#462)
Summary: This diff is a preparation for bundled program documentation, including: 1. move bundled program documentation from tutorial/ to sdk/ 2. remove the bento notebook, make its content directly in the .md file 3. elementary update to in line with new api Differential Revision: D49550056
1 parent fa7a464 commit 92959f4

File tree

2 files changed

+361
-2
lines changed

2 files changed

+361
-2
lines changed

docs/source/sdk-bundled-io.md

Lines changed: 359 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,360 @@
1-
# BundledIO
1+
# Bundled Program
22

3-
TBA
3+
## Introduction
4+
Bundled Program is a wrapper around the core ExecuTorch program designed to help users wrapping test cases and other related info with the models they deploy. Bundled Program is not necessarily a core part of the program and not needed for its execution but is more necessary for various other use-cases, especially for model correctness evaluation such as e2e testing during model bring-up etc.
5+
6+
Overall procedure can be broken into two stages, and in each stage we are supporting:
7+
* **Emit stage**: Bundling test I/O cases as well as other useful info in key-value pairs along with the ExecuTorch program.
8+
* **Runtime stage**: Accessing, executing and verifying the bundled test cases during runtime.
9+
10+
## Emit stage
11+
12+
This stage mainly focuses on the creation of a BundledProgram, and dump it out to the disk as a flatbuffer file. The main procedure is as follow:
13+
1. Create a model and emit its executorch program.
14+
2. Construct a BundledConfig to record all info need to be bundled.
15+
3. Generate BundledProgram using the emited model and BundledProgram
16+
4. Serialize the BundledProgram and dump it out to the disk.
17+
18+
### Step 1: Create a model and emit its executorch program.
19+
20+
This is not the part BunledProgram focusing on, so we just give left an example here without detailed APIs usage. Most of the example is borrowed from bundled_program/tests/common.py:
21+
22+
```python
23+
24+
import torch
25+
from executorch import exir
26+
from executorch.exir import ExecutorchBackendConfig
27+
from executorch.exir.passes import MemoryPlanningPass, ToOutVarPass
28+
29+
30+
class SampleModel(torch.nn.Module):
31+
"""An example model with multi-methods. Each method has multiple input and single output"""
32+
33+
def __init__(self) -> None:
34+
super().__init__()
35+
self.a: torch.Tensor = 3 * torch.ones(2, 2, dtype=torch.int32)
36+
self.b: torch.Tensor = 2 * torch.ones(2, 2, dtype=torch.int32)
37+
38+
def encode(
39+
self, x: torch.Tensor, q: torch.Tensor
40+
) -> torch.Tensor:
41+
z = x.clone()
42+
torch.mul(self.a, x, out=z)
43+
y = x.clone()
44+
torch.add(z, self.b, out=y)
45+
torch.add(y, q, out=y)
46+
return y
47+
48+
def decode(
49+
self, x: torch.Tensor, q: torch.Tensor
50+
) -> torch.Tensor:
51+
y = x * q
52+
torch.add(y, self.b, out=y)
53+
return y
54+
55+
56+
method_names = ["encode", "decode"]
57+
model = SampleModel()
58+
59+
capture_inputs = {
60+
m_name: (
61+
(torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
62+
(torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
63+
)
64+
for m_name in method_names
65+
}
66+
67+
# Trace to FX Graph and emit the program
68+
program = (
69+
exir.capture_multiple(model, capture_inputs)
70+
.to_edge()
71+
.to_executorch()
72+
.program
73+
)
74+
75+
```
76+
77+
### Step 2: Construct BundledConfig
78+
79+
BundledConfig is a class under `executorch/bundled_program/config.py` that contains all information needs to be bundled for model verification. Here's the constructor api to create BundledConfig:
80+
81+
```python
82+
class BundledConfig:
83+
def __init__(
84+
self,
85+
method_names: List[str],
86+
inputs: List[List[Any]],
87+
expected_outputs: List[List[Any]],
88+
) -> None:
89+
"""Contruct the config given inputs and expected outputs
90+
91+
Args:
92+
method_names: All method names need to be verified in program.
93+
inputs: All sets of input need to be test on for all methods. Each list
94+
of `inputs` is all sets which will be run on the method in the
95+
program with corresponding method name. Each set of any `inputs` element should
96+
contain all inputs required by eager_model with the same inference function
97+
as corresponding execution plan for one-time execution.
98+
99+
expected_outputs: Expected outputs for inputs sharing same index. The size of
100+
expected_outputs should be the same as the size of inputs and provided method_names.
101+
"""
102+
103+
```
104+
105+
Here's an example of creating a bundled program for SampleModel above:
106+
107+
```python
108+
109+
from executorch.bundled_program.config import BundledConfig
110+
111+
# number of input sets needed to be verified
112+
n_input = 10
113+
114+
# All Input sets need to be verified for all execution plans.
115+
inputs = [
116+
# The below list is all inputs for a single execution plan (inference method).
117+
[
118+
# Each list below is a individual input set.
119+
# The number of inputs, dtype and size of each input follow Program's spec.
120+
[
121+
(torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
122+
(torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
123+
]
124+
for _ in range(n_input)
125+
]
126+
for _ in range(len(program.execution_plan))
127+
]
128+
129+
# Expected outputs align with inputs.
130+
expected_outputs = [
131+
[[getattr(model, m_name)(*x)] for x in inputs[i]]
132+
for i, m_name in enumerate(method_names)
133+
]
134+
135+
136+
bundled_config = BundledConfig(
137+
method_names, inputs, expected_outputs
138+
)
139+
140+
```
141+
142+
### Step 3: Generate BundledProgram
143+
144+
To create BundledProgram, we provice `create_bundled_program` under `executorch/bundled_program/core.py` to generate BundledProgram by bundling the emitted executorch program with the bundled_config:
145+
146+
```python
147+
148+
def create_bundled_program(
149+
program: Program,
150+
bundled_config: BundledConfig,
151+
) -> BundledProgram:
152+
"""
153+
Args:
154+
program: The program to be bundled.
155+
bundled_config: The config to be bundled.
156+
"""
157+
```
158+
159+
Example:
160+
161+
```python
162+
from executorch.bundled_program.core import create_bundled_program
163+
164+
bundled_program = create_bundled_program(program, bundled_config)
165+
```
166+
167+
### Step 4: Serialize BundledProgram to Flatbuffer.
168+
169+
To serialize BundledProgram to make runtime APIs use it, we provide two APIs, both under `executorch/bundled_program/serialize/__init__.py`.
170+
171+
Serialize BundledProgram to flatbuffer:
172+
173+
```python
174+
def serialize_from_bundled_program_to_flatbuffer(
175+
bundled_program: BundledProgram,
176+
) -> bytes
177+
```
178+
179+
Deserialize flatbuffer to BundledProgram:
180+
181+
```python
182+
def deserialize_from_flatbuffer_to_bundled_program(
183+
flatbuffer: bytes
184+
) -> BundledProgram
185+
```
186+
187+
Example:
188+
```python
189+
from executorch.bundled_program.serialize import (
190+
serialize_from_bundled_program_to_flatbuffer,
191+
deserialize_from_flatbuffer_to_bundled_program,
192+
)
193+
194+
serialized_bundled_program = serialize_from_bundled_program_to_flatbuffer(bundled_program)
195+
regenerate_bundled_program = deserialize_from_flatbuffer_to_bundled_program(serialized_bundled_program)
196+
197+
```
198+
199+
## Runtime Stage
200+
This stage mainly focuses on executing the model with the bundled inputs and and comparing the model's output with the bundled expected output. We provide multiple APIs to handle the key parts of it.
201+
202+
### Get executorch program ptr from BundledProgram buffer
203+
We need the pointer to executorch program to do the execution. To unify the process of loading and executing BundledProgram and Program flatbuffer, we create an API:
204+
```c++
205+
206+
/**
207+
* Finds the serialized ExecuTorch program data in the provided file data.
208+
*
209+
* The returned buffer is appropriate for constructing a
210+
* torch::executor::Program.
211+
*
212+
* Calling this is only necessary if the file could be a bundled program. If the
213+
* file will only contain an unwrapped ExecuTorch program, callers can construct
214+
* torch::executor::Program with file_data directly.
215+
*
216+
* @param[in] file_data The contents of an ExecuTorch program or bundled program
217+
* file.
218+
* @param[in] file_data_len The length of file_data, in bytes.
219+
* @param[out] out_program_data The serialized Program data, if found.
220+
* @param[out] out_program_data_len The length of out_program_data, in bytes.
221+
*
222+
* @returns Error::Ok if the program was found, and
223+
* out_program_data/out_program_data_len point to the data. Other values
224+
* on failure.
225+
*/
226+
Error GetProgramData(
227+
void* file_data,
228+
size_t file_data_len,
229+
const void** out_program_data,
230+
size_t* out_program_data_len);
231+
```
232+
233+
Here's an example of how to use the GetProgramData API:
234+
```c++
235+
std::shared_ptr<char> buff_ptr;
236+
size_t buff_len;
237+
238+
// FILE_PATH here can be either BundledProgram or Program flatbuffer file.
239+
Error status = torch::executor::util::read_file_content(
240+
FILE_PATH, &buff_ptr, &buff_len);
241+
ET_CHECK_MSG(
242+
status == Error::Ok,
243+
"read_file_content() failed with status 0x%" PRIx32,
244+
status);
245+
246+
uint32_t prof_tok = EXECUTORCH_BEGIN_PROF("de-serialize model");
247+
248+
const void* program_ptr;
249+
size_t program_len;
250+
status = torch::executor::util::GetProgramData(
251+
buff_ptr.get(), buff_len, &program_ptr, &program_len);
252+
ET_CHECK_MSG(
253+
status == Error::Ok,
254+
"GetProgramData() failed with status 0x%" PRIx32,
255+
status);
256+
```
257+
258+
### Load bundled input to ExecutionPlan
259+
To execute the program on the bundled input, we need to load the bundled input into the ExecutionPlan. Here we provided an API called `torch::executor::util::LoadBundledInput`:
260+
261+
```c++
262+
263+
/**
264+
* Load testset_idx-th bundled input of method_idx-th Method test in
265+
* bundled_program_ptr to given Method.
266+
*
267+
* @param[in] method The Method to verify.
268+
* @param[in] bundled_program_ptr The bundled program contains expected output.
269+
* @param[in] method_name The name of the Method being verified.
270+
* @param[in] testset_idx The index of input needs to be set into given Method.
271+
*
272+
* @returns Return Error::Ok if load successfully, or the error happens during
273+
* execution.
274+
*/
275+
__ET_NODISCARD Error LoadBundledInput(
276+
Method& method,
277+
serialized_bundled_program* bundled_program_ptr,
278+
MemoryAllocator* memory_allocator,
279+
const char* method_name,
280+
size_t testset_idx);
281+
```
282+
283+
### Verify the plan's output.
284+
We call `torch::executor::util::VerifyResultWithBundledExpectedOutput` to verify the method's output with bundled expected outputs. Here's the details of this API:
285+
286+
```c++
287+
/**
288+
* Compare the Method's output with testset_idx-th bundled expected
289+
* output in method_idx-th Method test.
290+
*
291+
* @param[in] method The Method to extract outputs from.
292+
* @param[in] bundled_program_ptr The bundled program contains expected output.
293+
* @param[in] method_name The name of the Method being verified.
294+
* @param[in] testset_idx The index of expected output needs to be compared.
295+
* @param[in] rtol Relative tolerance used for data comparsion.
296+
* @param[in] atol Absolute tolerance used for data comparsion.
297+
*
298+
* @returns Return Error::Ok if two outputs match, or the error happens during
299+
* execution.
300+
*/
301+
__ET_NODISCARD Error VerifyResultWithBundledExpectedOutput(
302+
Method& method,
303+
serialized_bundled_program* bundled_program_ptr,
304+
MemoryAllocator* memory_allocator,
305+
const char* method_name,
306+
size_t testset_idx,
307+
double rtol = 1e-5,
308+
double atol = 1e-8);
309+
310+
```
311+
312+
### Example
313+
314+
Here we provide an example about how to run the bundled program step by step. Most of the code are borrowed from "fbcode/executorch/sdk/runners/executor_runner.cpp" and please review that file if you need more info and context:
315+
316+
```c++
317+
// method_name is the name for the method we want to test
318+
// memory_manager is the executor::MemoryManager variable for executor memory allocation.
319+
// program is the executorch program.
320+
Result<Method> method = program->load_method(method_name, &memory_manager);
321+
EXECUTORCH_END_PROF(prof_tok);
322+
ET_CHECK_MSG(
323+
method.ok(),
324+
"load_method() failed with status 0x%" PRIx32,
325+
method.error());
326+
327+
// Load testset_idx-th input in the buffer to plan
328+
status = torch::executor::util::LoadBundledInput(
329+
*method,
330+
program_data.bundled_program_data(),
331+
&bundled_input_allocator,
332+
method_name,
333+
FLAGS_testset_idx);
334+
ET_CHECK_MSG(
335+
status == Error::Ok,
336+
"LoadBundledInput failed with status 0x%" PRIx32,
337+
status);
338+
339+
// Execute the plan
340+
status = method->execute();
341+
ET_CHECK_MSG(
342+
status == Error::Ok,
343+
"method->execute() failed with status 0x%" PRIx32,
344+
status);
345+
346+
// Verify the result.
347+
status = torch::executor::util::VerifyResultWithBundledExpectedOutput(
348+
*method,
349+
program_data.bundled_program_data(),
350+
&bundled_input_allocator,
351+
method_name,
352+
FLAGS_testset_idx,
353+
FLAGS_rtol,
354+
FLAGS_atol);
355+
ET_CHECK_MSG(
356+
status == Error::Ok,
357+
"Bundle verification failed with status 0x%" PRIx32,
358+
status);
359+
360+
```

docs/website/docs/tutorials/bundled_program.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
DEPRECATED: This document is moving to //executorch/docs/source/sdk-bundled-io.md
2+
13
# Bundled Program
24

35
## Introduction

0 commit comments

Comments
 (0)