|
22 | 22 | import os |
23 | 23 | import tempfile |
24 | 24 | import unittest |
| 25 | +from typing import Optional |
25 | 26 |
|
26 | 27 | import torch |
27 | 28 | from executorch.backends.cuda.cuda_backend import CudaBackend |
@@ -51,14 +52,25 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: |
51 | 52 | "loads through the Python runtime, which the ROCm build does not include", |
52 | 53 | ) |
53 | 54 | class TestMissingWeightsBlob(unittest.TestCase): |
54 | | - def _lower(self, outdir: str, seed: int = 0, name: str = "model.pte") -> str: |
| 55 | + def _lower( |
| 56 | + self, |
| 57 | + outdir: str, |
| 58 | + seed: int = 0, |
| 59 | + name: str = "model.pte", |
| 60 | + data_name: Optional[str] = None, |
| 61 | + module: Optional[torch.nn.Module] = None, |
| 62 | + ) -> str: |
55 | 63 | torch.manual_seed(seed) |
56 | | - module = ModuleWithConstants().eval().cuda() |
| 64 | + if module is None: |
| 65 | + module = ModuleWithConstants() |
| 66 | + module = module.eval().cuda() |
57 | 67 | inputs = (torch.randn(4, 64, device="cuda"),) |
58 | 68 | exported = export(module, inputs) |
59 | | - spec = CudaBackend.generate_method_name_compile_spec("forward") |
| 69 | + specs = [CudaBackend.generate_method_name_compile_spec("forward")] |
| 70 | + if data_name is not None: |
| 71 | + specs.append(CudaBackend.generate_data_name_compile_spec(data_name)) |
60 | 72 | lowered = to_edge_transform_and_lower( |
61 | | - exported, partitioner=[CudaPartitioner([spec])] |
| 73 | + exported, partitioner=[CudaPartitioner(specs)] |
62 | 74 | ) |
63 | 75 | program = lowered.to_executorch() |
64 | 76 | path = os.path.join(outdir, name) |
@@ -177,6 +189,39 @@ def test_load_fails_with_another_programs_blob(self) -> None: |
177 | 189 | ): |
178 | 190 | program.load_method("forward") |
179 | 191 |
|
| 192 | + def test_programs_with_their_own_data_names_share_a_folder(self) -> None: |
| 193 | + from executorch.runtime import Runtime |
| 194 | + |
| 195 | + with tempfile.TemporaryDirectory() as outdir: |
| 196 | + for seed, name in ((0, "first"), (1, "second")): |
| 197 | + self._lower(outdir, seed=seed, name=f"{name}.pte", data_name=name) |
| 198 | + # A program with no constants still writes a data file. |
| 199 | + self._lower( |
| 200 | + outdir, name="third.pte", data_name="third", module=torch.nn.ReLU() |
| 201 | + ) |
| 202 | + self.assertEqual( |
| 203 | + sorted(f for f in os.listdir(outdir) if f.endswith(".ptd")), |
| 204 | + ["first.ptd", "second.ptd", "third.ptd"], |
| 205 | + ) |
| 206 | + for seed, name in ((0, "first"), (1, "second")): |
| 207 | + # The same draws _lower made, so the same weights and input. |
| 208 | + torch.manual_seed(seed) |
| 209 | + module = ModuleWithConstants().eval().cuda() |
| 210 | + value = torch.randn(4, 64, device="cuda") |
| 211 | + method = ( |
| 212 | + Runtime.get() |
| 213 | + .load_program( |
| 214 | + os.path.join(outdir, f"{name}.pte"), |
| 215 | + data_path=os.path.join(outdir, f"{name}.ptd"), |
| 216 | + ) |
| 217 | + .load_method("forward") |
| 218 | + ) |
| 219 | + out = method.execute([value.cpu()]) |
| 220 | + torch.testing.assert_close( |
| 221 | + (out[0] if isinstance(out, (list, tuple)) else out).cpu(), |
| 222 | + module(value).cpu(), |
| 223 | + ) |
| 224 | + |
180 | 225 | def test_load_succeeds_when_the_model_has_no_constants(self) -> None: |
181 | 226 | """A model with nothing to bind still loads without its weights blob. |
182 | 227 |
|
|
0 commit comments