Платформа оценки latency и стоимости ML‑инференса
Внутренний инструмент для профилировки latency, throughput и $/req моделей в проде

One-liner: За 8 недель превратили ML-инфраструктуру из черного ящика в прозрачную и управляемую систему с точной стоимостью каждого запроса.
1. Почему бизнесу была нужна платформа
В начале проекта ML-инфраструктура была разрозненной и неуправляемой:
- Нет единого подхода к деплою моделей: одни команды деплоили через TorchServe, другие использовали ONNX Runtime, третьи работали через KServe.
- Отсутствие прозрачности: никто не понимал точную стоимость одного inference-запроса. GPU простаивали ночью, а днем перегревались, вызывая пиковые скачки latency выше SLA.
- Бюджет горел: инфраструктурная команда временно ограничивала количество нод вручную, чтобы уложиться в лимиты. Иногда это ломало прод, но отследить было невозможно.
2. Что было сделано и зачем
Мы построили универсальный инструмент мониторинга и оптимизации затрат, полностью интегрированный в Kubernetes-среду компании. Платформа собирает все критически важные метрики ML-инфраструктуры:
- Latency: измерение задержек с разбивкой по percentiles (p50, p90, p99).
- Throughput: количество запросов в секунду, деградация при разной нагрузке.
- GPU/CPU-utilization: мониторинг загрузки ресурсов, выявление простоев.
- $/req: точная стоимость каждого ML-запроса на основе Spot API и Kubecost.
Платформа дает actionable-рекомендации:
- замена типа GPU-инстансов (V100 → T4)
- оптимальный batching
- квантизация и pruning моделей
3. Архитектура решения
Платформа мониторинга построена вокруг Kubernetes и сервисов-экспортеров:
Inference-Service (Torch / ONNX)
├── /metrics endpoint (Prometheus-compatible)
│ ├── latency_histogram (p50, p90, p99)
│ ├── throughput_rps
│ └── gpu_utilization / cpu_utilization
│
├── Torch / ONNX Profiler
│ └── performance traces (JSON → Perfetto)
│
└── Kubecost Exporter (via Spot API)
├── spot-instance cost mapping
└── billing aggregation by namespace / model_id labels
↓
┌────────────────────┐
│ Prometheus DB │
└────────────────────┘
↓
┌────────────────┐
│ Alertmanager │ ◄──── SLA breaches (e.g. p99 > 800ms)
└────────────────┘
↓
┌────────────────────┐
│ Grafana Panels │
├────────────────────┤
│ • $/req by model │
│ • p95 latency heat │
│ • throughput trend │
│ • GPU utilization │
└────────────────────┘
↓
Auto-scaling policy input
(via KEDA / HPA / Argo)
Пример метрик в Prometheus:
histogram_quantile(0.99, sum(rate(model_inference_latency_seconds_bucket[5m])) by (le))
Эта метрика вычисляет p99 latency, помогая отлавливать пики и своевременно реагировать на аномалии.
4. Профилировка и нагрузочные тесты
Отдельно были настроены регулярные профилировки моделей:
- PyTorch Profiler: В рамках платформы реализовали отдельный модуль для профилировки latency и ресурсного потребления моделей. Он используется в dev- и CI-средах, чтобы выявлять узкие места после дообучения или при деградации SLA. Ниже приведен пример скрипта, применяемого для такой диагностики. Профили сохраняются в TensorBoard или Perfetto, результаты анализируются вручную или автоматически через threshold-based алерты.
from __future__ import annotations
import argparse, os, sys, time, torch
from pathlib import Path
from contextlib import nullcontext
from torch import nn
from torch.profiler import profile, record_function, tensorboard_trace_handler, ProfilerActivity
def cfg():
p=argparse.ArgumentParser()
p.add_argument("--model",default="resnet50")
p.add_argument("-b","--batch",type=int,default=32)
p.add_argument("--steps",type=int,default=50)
p.add_argument("--device",default="auto")
p.add_argument("--no-amp",dest="amp",action="store_false")
p.add_argument("--no-compile",dest="comp",action="store_false")
p.add_argument("--log",type=Path,default=Path("./logs"))
p.add_argument("--seed",type=int,default=42)
p.add_argument("--disable-prof",dest="prof",action="store_false")
return p.parse_args()
def pick(d):
if d!="auto": return torch.device(d)
if torch.cuda.is_available(): return torch.device("cuda")
if torch.backends.mps.is_available(): return torch.device("mps")
return torch.device("cpu")
def load(m,dev,comp):
net=torch.load(m,map_location=dev) if os.path.isfile(m) else torch.hub.load("pytorch/vision",m,pretrained=True)
net=net.to(dev).eval()
if comp and torch.__version__>="2.0":
try: net=torch.compile(net,mode="reduce-overhead",fullgraph=False)
except: pass
if torch.cuda.device_count()>1 and dev.type=="cuda": net=nn.DataParallel(net)
return net
def prof(c):
if not c.prof: return nullcontext()
kw=dict(activities=[ProfilerActivity.CPU],schedule=torch.profiler.schedule(wait=2,warmup=2,active=c.steps),
record_shapes=True,profile_memory=True,with_stack=True,with_modules=True)
if torch.cuda.is_available(): kw["activities"].append(ProfilerActivity.CUDA)
if torch.__version__>="2.3": kw["with_flops"]=True
return profile(on_trace_ready=tensorboard_trace_handler(str(c.log)),**kw)
def main():
c=cfg(); dev=pick(c.device); torch.manual_seed(c.seed); torch.backends.cudnn.deterministic=True
c.log.mkdir(parents=True,exist_ok=True)
net=load(c.model,dev,c.comp); x=torch.randn(c.batch,3,224,224,device=dev)
ac=torch.autocast(dev.type,enabled=c.amp)
pc=prof(c); start=time.perf_counter()
with pc as p:
for _ in range(c.steps):
with torch.no_grad(),ac,record_function("infer"): net(x)
if c.prof: p.step()
total=time.perf_counter()-start
print(f"{c.steps} steps {total:.2f}s {c.steps/total:.2f} it/s on {dev.type.upper()}")
if c.prof:
key="self_cuda_time_total" if dev.type=="cuda" else "self_cpu_time_total"
print(p.key_averages().table(sort_by=key,row_limit=20))
if __name__=="__main__":
try: main()
except KeyboardInterrupt: sys.exit(0)
- ONNX Profiler: для ONNX Runtime использовали tracing API, экспортируя профили в JSON и визуализируя их через Perfetto UI.
Эти профили позволяли увидеть горячие точки в моделях. Например, выделение времени CPU-преобразования (image → tensor → device) занимало до 40% времени inference, что оперативно устранялось.
5. Результаты после запуска платформы
За месяц после запуска платформы получили:
Метрика | До внедрения | После внедрения | Дельта |
---|---|---|---|
Latency p99 | 400–900 мс (нестаб.) | стабильно ~420 мс | стабилизирован |
GPU-utilization | ~36 % | 54 % | +18 pp ↑ |
$/req (на модели LLM) | базовая | -43 % (V100 → T4) | −43 % ↓ |
Точечные алерты | отсутствовали | автоматизированы | внедрено |
Пример реальной ситуации:
- Spot-инстанс упал ночью → платформа сразу отправила алерт и подсветила скачок стоимости в 3 раза → быстро откатили конфигурацию, минимизировав финансовые потери.
6. Почему это важно для бизнеса и технических команд
Платформа изменила подход к работе с ML-инфраструктурой:
- Полная прозрачность: CFO и CTO получили детальную аналитику по стоимости ML-проектов в реальном времени.
- Контролируемые бюджеты: теперь можно открыть Grafana и за 10 секунд узнать: сколько стоил 1M запросов на конкретной модели за сутки
- Управляемый performance: на основе платформы принимаются решения о смене GPU, выборе batch size и запуске retrain при снижении throughput.
Bottom line
За 8 недель мы превратили фрагментированную ML-инфраструктуру в управляемую систему с прозрачными метриками latency, throughput и $/req. Платформа позволила сократить затраты на 43 %, стабилизировать p99 latency и внедрить алерты, срабатывающие при SLA-деградациях и росте стоимости. Теперь каждое решение о запуске модели опирается на цифры на уровне модели, инстанса и бюджета.