Compare commits

...
Author SHA1 Message Date
Andrei Cravtov 11f9d000e5 Merge branch 'main' into andrei/force_oom 2026-05-15 13:53:11 +01:00
Andrei Cravtov 551e94ed38 fin 2026-05-15 13:52:53 +01:00
Andrei Cravtov b0834c8a2e ff 2026-05-15 13:46:26 +01:00
Andrei Cravtov d545a4ea25 fff 2026-05-15 13:42:02 +01:00
Andrei Cravtov 0e7721ba0e ffff 2026-05-15 13:28:08 +01:00
Andrei Cravtov abb4402036 fff 2026-05-15 13:26:13 +01:00
Andrei Cravtov e8b9c808a6 ff 2026-05-15 13:23:11 +01:00
Andrei Cravtov cf00cd83ed fff 2026-05-15 13:20:13 +01:00
Andrei Cravtov 41ff366b1d fff 2026-05-15 13:01:55 +01:00
Andrei Cravtov 303600616a ff 2026-05-15 12:45:12 +01:00
Andrei Cravtov 51d9912fcf ff 2026-05-15 12:37:04 +01:00
Andrei Cravtov d5da78b39b ff 2026-05-15 12:32:16 +01:00
Andrei Cravtov 88027475a5 ff 2026-05-15 12:30:50 +01:00
Andrei Cravtov 423dc407f8 ff 2026-05-15 12:24:42 +01:00
Andrei Cravtov f62b3b09d0 ff 2026-05-15 11:58:47 +01:00
Andrei Cravtov 94424d92c3 ff 2026-05-15 11:56:39 +01:00
Andrei Cravtov e029a1ea65 print 2026-05-15 11:51:13 +01:00
Andrei Cravtov 49e5350b17 ff 2026-05-15 11:47:56 +01:00
Andrei Cravtov feaf05f82d ooom 2 2026-05-15 11:43:08 +01:00
Andrei Cravtov 280031d431 Use MemoryUsage for MLX OOM fallback 2026-05-15 11:05:54 +01:00
Andrei Cravtov 58551f2c32 nix-direnv 2026-05-15 10:57:00 +01:00
Andrei Cravtov cecc37085a Move one-shot macmon sampling helper 2026-05-14 20:25:34 +01:00
Andrei Cravtov a73be3c2bb Scope MLX OOM ceil division helper 2026-05-14 20:13:28 +01:00
Andrei Cravtov acec94316d Inline one-off MLX OOM helpers 2026-05-14 20:05:33 +01:00
Andrei Cravtov 5c7068387d yes 2026-05-14 19:57:53 +01:00
Andrei Cravtov d62b333405 Fix MLX utils type ignores 2026-05-14 19:51:31 +01:00
Andrei Cravtov eeedc6c17a Add adaptive MLX force OOM helper 2026-05-14 19:49:40 +01:00
Andrei Cravtov 2d11b6520f yes 2026-05-14 19:45:07 +01:00
Andrei Cravtov 42b26af63b yes 2026-05-14 17:56:56 +01:00
Andrei Cravtov 1649708ccb yes 2026-05-14 17:55:51 +01:00
Andrei Cravtov fbe962b08e Merge branch 'main' into andrei/error-handling 2026-05-14 17:44:53 +01:00
Andrei Cravtov 38258fb5f3 Merge branch 'main' into andrei/error-handling
# Conflicts:
#	uv.lock
2026-05-14 17:33:33 +01:00
Andrei Cravtov 86398a2bd1 ffff 2026-05-14 17:31:47 +01:00
Andrei Cravtov bab245759d fff 2026-05-14 17:26:24 +01:00
Andrei Cravtov 2f506167e0 fff 2026-05-14 17:24:20 +01:00
Andrei Cravtov 88d4c37bfa fff 2026-05-14 17:12:41 +01:00
Andrei Cravtov 9ec09ec59d ff 2026-05-14 17:07:46 +01:00
Andrei Cravtov cd22516186 ff 2026-05-14 16:54:37 +01:00
Andrei Cravtov ddc6e95e68 fff 2026-05-14 16:51:20 +01:00
Andrei Cravtov feef63b98c fff 2026-05-14 16:50:39 +01:00
Andrei Cravtov 3611c33b61 ff 2026-05-14 16:06:11 +01:00
Andrei Cravtov 547b58bf48 ff 2026-05-14 15:51:02 +01:00
Andrei Cravtov 36b1b82b48 ff 2026-05-14 15:49:32 +01:00
Andrei Cravtov b1d09b026d ff 2026-05-14 15:29:40 +01:00
Andrei Cravtov faafc6f4ad yes 2026-05-14 15:17:34 +01:00
Andrei Cravtov 2969b7d4c6 yes 2026-05-14 15:16:48 +01:00
Andrei Cravtov 6c13b4550f yes 2026-05-14 15:11:17 +01:00
Andrei Cravtov 3fc56b2da5 yes 2026-05-14 15:08:58 +01:00
Andrei Cravtov 9c4cc084db yes 2026-05-14 12:36:14 +01:00
Andrei Cravtov 49d7555ea6 yes 2026-05-14 12:31:49 +01:00
Andrei Cravtov 32155bdf00 yes 2026-05-14 12:31:10 +01:00
Andrei Cravtov 163ef39bbb yes 2026-05-14 12:29:34 +01:00
Andrei Cravtov 9636e86855 yes 2026-05-14 12:25:10 +01:00
Andrei Cravtov 4ea2532dd6 yes 2026-05-14 12:14:09 +01:00
Andrei Cravtov 1d17f4de18 yes 2026-05-14 12:09:34 +01:00
Andrei Cravtov 1c7f514ca3 yes 2026-05-14 12:04:43 +01:00
Andrei Cravtov c3aa1f47fc yes 2026-05-14 11:56:00 +01:00
Andrei Cravtov 9686c02251 yes 2026-05-14 11:50:21 +01:00
Andrei Cravtov 2f9ba7d1de yes 2026-05-14 11:14:39 +01:00
Andrei Cravtov ab172322c5 Merge branch 'main' into andrei/error-handling 2026-05-12 11:49:17 +01:00
Andrei Cravtov 278bea11ad fmt 2026-05-12 11:40:07 +01:00
Andrei Cravtov dbd5ca00eb fmt 2026-05-11 20:04:58 +01:00
Andrei Cravtov 197cbf4f9d fmt 2026-05-11 19:44:38 +01:00
Andrei Cravtov d58ab113d1 fmt 2026-05-11 19:32:47 +01:00
Andrei Cravtov 9c7908f247 integrated it 2026-05-11 19:32:25 +01:00
Andrei Cravtov ad2c1e7c66 initial changes 2026-05-11 19:19:47 +01:00
6 changed files with 118 additions and 4 deletions

No files matched your search

View File
Whitespace-only changes.
+5
View File
@@ -1,3 +1,8 @@
# installs nix-direnv if doesn't exist (speeds up evaluation)
if ! has nix_direnv_version || ! nix_direnv_version 3.1.1; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.1.1/direnvrc" "sha256-p+fzQdrms/hDa7g+soShAybJNo4bN4SIAeSfqNKgD5I="
fi
use flake
# creates .venv if doesn't exist and loads its environment
+3
View File
@@ -4,4 +4,7 @@
<option name="sdkName" value="Python 3.13 (exo)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (exo)" project-jdk-type="Python SDK" />
<component name="RuffConfiguration">
<option name="enabled" value="true" />
</component>
</project>
+1
View File
@@ -13,6 +13,7 @@ from exo.shared.models.model_cards import ModelId
from exo.utils.pydantic_ext import TaggedModel
from exo.worker.runner.diagnostics import KnownRunnerDiagnostic
from ...worker.runner.diagnostics import KnownRunnerDiagnostic
from .common import CommandId
+41
View File
@@ -1,3 +1,6 @@
import os
import shutil
import subprocess
from typing import Self
from pydantic import BaseModel
@@ -68,3 +71,41 @@ class MacmonMetrics(TaggedModel):
@classmethod
def from_raw_json(cls, json: str) -> Self:
return cls.from_raw(RawMacmonMetrics.model_validate_json(json))
def read_macmon_metrics_once(
macmon_path: str | None = None,
*,
timeout: float = 5,
) -> MacmonMetrics | None:
"""
Read a single macmon sample, returning None when macmon is unavailable.
"""
resolved_macmon_path = (
macmon_path or os.getenv("EXO_MACMON_PATH") or shutil.which("macmon")
)
if resolved_macmon_path is None:
return None
try:
result = subprocess.run(
[resolved_macmon_path, "pipe", "--samples", "1", "--interval", "100"],
capture_output=True,
check=False,
text=True,
timeout=timeout,
)
except (OSError, subprocess.SubprocessError):
return None
if result.returncode != 0:
return None
lines = result.stdout.strip().splitlines()
if not lines:
return None
try:
return MacmonMetrics.from_raw_json(lines[0])
except ValueError:
return None
+68 -4
View File
@@ -1,3 +1,4 @@
from itertools import pairwise
import json
import os
import re
@@ -5,6 +6,7 @@ import sys
import tempfile
import time
from collections.abc import Generator
from math import isqrt
from pathlib import Path
from typing import TYPE_CHECKING, Any, cast
@@ -786,13 +788,75 @@ def mlx_force_oom(size: int = 200000) -> None:
a = mx.random.uniform(shape=(size, size), dtype=mx.float32)
b = mx.random.uniform(shape=(size, size), dtype=mx.float32)
mx.eval(a, b)
c = mx.matmul(a, b)
d = mx.matmul(a, c)
e = mx.matmul(b, c)
f = mx.sigmoid(d + e)
c = mx.matmul(a, b) # (size,size)
d = mx.matmul(a, c) # (size,size)
e = mx.matmul(b, c) # (size,size)
f = mx.sigmoid(d + e) # (size,size)
mx.eval(f)
def mlx_force_oom2(bytes_alloc: int = 1024**5): # the default is 1 petabyte lol
"""
Force an Out-Of-Memory (OOM) error in MLX by performing large tensor operations.
NOTE: probably only works correctly on Apple unified memory
"""
# TODO: I give up. this either uses swap (inconsistently) and only sometimes OOMs
# or if I tune it to be more aggressive then it kerenel panics entirely;
# there MIGHT be a way to make it not use swap memory but I'm not determined enough
# to figure out how :)
def get_size(memory: int):
mat_elem = -(-memory // 4) # per-matrix elements (4 bytes per elem)
# square root to get size (round up if not integer)
root = isqrt(mat_elem)
return root if root**2 == mat_elem else root + 1
def oom(size: int):
mx.set_default_device(mx.gpu)
mx.clear_cache()
# allocate a lot
z = mx.zeros(shape=(size, size), dtype=mx.float32)
t1 = [mx.random.uniform(shape=(size, size), dtype=mx.float32) for _ in range(2)]
# mat mul cycle
t2: list[mx.array] = []
for m1, m2 in pairwise(t1):
t2.append(mx.matmul(m1, m2))
print("t2-run")
mx.eval(*t2)
print("t2-eval")
# sigmoid sum
f = mx.sigmoid(sum(t1, start=z) + sum(t2, start=z))
print("f-run")
mx.eval(f)
print("f-eval")
# use supplied size, or computer appropriate size otherwise
fail_num = 0
while True:
try:
print(f"size {bytes_alloc / 1024**3} GB")
oom(get_size(bytes_alloc))
break
except RuntimeError as e:
max_bytes = re.compile(
r"\[metal::malloc\] Attempting to allocate (?:\d+) bytes which is greater than the maximum allowed buffer size of (?P<max_bytes>\d+) bytes."
).match(str(e))
if max_bytes is None:
raise RuntimeError(
"Tried to get max buffer, but wrong error format"
) from e
bytes_alloc = round(int(max_bytes.group("max_bytes")) * 0.95**fail_num)
fail_num += 1
mlx_force_oom2()
def set_wired_limit_for_model(model_size: Memory):
"""
A context manager to temporarily change the wired limit.