File size: 6,102 Bytes
236083b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/env python3

from __future__ import annotations

import os
import shutil
import subprocess
import sys
import textwrap
from pathlib import Path


EXPORT = Path(os.environ.get(
    "EXPORT",
    "/home/metris/hf-releases/Multiscreen-8.7M-Base",
)).resolve()

RUNTIME = EXPORT / "runtime"
OUTPUT = EXPORT / "runtime-minimal"
TRACE_DIR = EXPORT / ".runtime-trace"

CPU_RUNNER = EXPORT / "run_cpu_without_triton.py"
GPU_RUNNER = EXPORT / "run_gpu_triton.py"


def fail(message: str) -> None:
    raise SystemExit(f"ERROR: {message}")


if not RUNTIME.is_dir():
    fail(f"Missing runtime directory: {RUNTIME}")

if not CPU_RUNNER.is_file():
    fail(f"Missing CPU runner: {CPU_RUNNER}")

if not GPU_RUNNER.is_file():
    fail(f"Missing GPU runner: {GPU_RUNNER}")


shutil.rmtree(TRACE_DIR, ignore_errors=True)
shutil.rmtree(OUTPUT, ignore_errors=True)

TRACE_DIR.mkdir(parents=True)
OUTPUT.mkdir(parents=True)

sitecustomize = TRACE_DIR / "sitecustomize.py"

sitecustomize.write_text(
    textwrap.dedent(
        r'''
        from __future__ import annotations

        import atexit
        import os
        import sys
        from pathlib import Path


        ROOT = Path(os.environ["TRACE_RUNTIME_ROOT"]).resolve()
        OUTPUT = Path(os.environ["TRACE_OUTPUT"]).resolve()


        def source_path(module):
            candidates = []

            spec = getattr(module, "__spec__", None)
            if spec is not None:
                origin = getattr(spec, "origin", None)
                if origin:
                    candidates.append(origin)

            module_file = getattr(module, "__file__", None)
            if module_file:
                candidates.append(module_file)

            for candidate in candidates:
                try:
                    path = Path(candidate).resolve()
                except Exception:
                    continue

                if path.suffix == ".pyc":
                    cache_parts = list(path.parts)
                    if "__pycache__" in cache_parts:
                        cache_index = cache_parts.index("__pycache__")
                        parent = Path(*cache_parts[:cache_index])
                        stem = path.name.split(".cpython-")[0]
                        possible_source = parent / f"{stem}.py"
                        if possible_source.exists():
                            path = possible_source.resolve()

                if path.suffix != ".py":
                    continue

                try:
                    path.relative_to(ROOT)
                except ValueError:
                    continue

                return path

            return None


        @atexit.register
        def write_trace():
            found = set()

            for module in list(sys.modules.values()):
                if module is None:
                    continue

                path = source_path(module)
                if path is None:
                    continue

                found.add(path.relative_to(ROOT).as_posix())

            OUTPUT.parent.mkdir(parents=True, exist_ok=True)
            OUTPUT.write_text(
                "".join(f"{item}\n" for item in sorted(found)),
                encoding="utf-8",
            )
        '''
    ).strip()
    + "\n",
    encoding="utf-8",
)


def run_trace(name: str, runner: Path, extra_env: dict[str, str]) -> Path:
    trace_file = TRACE_DIR / f"{name}.txt"

    env = os.environ.copy()
    env.update(extra_env)
    env["TRACE_RUNTIME_ROOT"] = str(RUNTIME)
    env["TRACE_OUTPUT"] = str(trace_file)

    existing_pythonpath = env.get("PYTHONPATH", "")
    pythonpath_parts = [
        str(TRACE_DIR),
        str(RUNTIME),
    ]

    if existing_pythonpath:
        pythonpath_parts.append(existing_pythonpath)

    env["PYTHONPATH"] = os.pathsep.join(pythonpath_parts)

    print(f"\n===== TRACING {name.upper()} =====")
    print(f"Runner: {runner}")

    subprocess.run(
        [sys.executable, str(runner)],
        cwd=EXPORT,
        env=env,
        check=True,
    )

    if not trace_file.is_file():
        fail(f"Trace was not created: {trace_file}")

    return trace_file


cpu_trace = run_trace(
    "cpu",
    CPU_RUNNER,
    {
        "CUDA_VISIBLE_DEVICES": "",
        "MULTISCREEN_BACKEND": "torch",
    },
)

gpu_trace = run_trace(
    "gpu",
    GPU_RUNNER,
    {
        "MULTISCREEN_BACKEND": "triton",
    },
)


required: set[Path] = set()

for trace_file in (cpu_trace, gpu_trace):
    for line in trace_file.read_text(encoding="utf-8").splitlines():
        line = line.strip()
        if not line:
            continue
        required.add(Path(line))


# Package __init__.py files may not always appear independently in traces.
for relative_path in list(required):
    parent = relative_path.parent

    while str(parent) not in ("", "."):
        init_file = parent / "__init__.py"

        if (RUNTIME / init_file).is_file():
            required.add(init_file)

        parent = parent.parent


# Explicitly preserve the custom backend modules even if a lazy path was not
# exercised by one particular smoke prompt.
for relative_path in (
    Path("litgpt/__init__.py"),
    Path("litgpt/multiscreen_triton.py"),
    Path("litgpt/multiscreen_projection_triton.py"),
):
    if (RUNTIME / relative_path).is_file():
        required.add(relative_path)


for relative_path in sorted(required):
    source = RUNTIME / relative_path

    if not source.is_file():
        continue

    destination = OUTPUT / relative_path
    destination.parent.mkdir(parents=True, exist_ok=True)
    shutil.copy2(source, destination)


manifest = OUTPUT / "MINIMAL_RUNTIME_FILES.txt"
manifest.write_text(
    "".join(f"{path.as_posix()}\n" for path in sorted(required)),
    encoding="utf-8",
)

print("\n===== MINIMAL RUNTIME BUILT =====")
print(f"Location: {OUTPUT}")
print(f"Python files: {sum(1 for p in OUTPUT.rglob('*.py'))}")

subprocess.run(["du", "-sh", str(RUNTIME), str(OUTPUT)], check=False)

print("\nDo not delete the original runtime yet.")
print("Rename it, test the minimal runtime, and only then remove the backup.")