File size: 5,169 Bytes
33962ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Measure a CPU container; only creates/removes its own uniquely named container.
Reports CPU app startup/HTTP workload, not live inference. Requires Docker CLI.
"""
import concurrent.futures
import json
import os
import subprocess
import time
import uuid

name='visual-mentor-measure-'+uuid.uuid4().hex[:8]
image=os.environ.get('VM_RUNTIME_IMAGE','visual-mentor:validation')
def command(*args):
    if args[0]=='docker':args=('docker','--host=unix:///var/run/docker.sock',*args[1:])
    return subprocess.check_output(args,text=True).strip()
probe="import urllib.request,json; print(json.load(urllib.request.urlopen('http://127.0.0.1:7860/api/health',timeout=2))['status'])"
rss_function = """
def app_rss():
 import glob
 processes={}
 for p in glob.glob('/proc/[0-9]*/status'):
  try:
   fields={line.split(':',1)[0]:line.split(':',1)[1].strip() for line in open(p) if ':' in line}
   processes[int(fields['Pid'])]=(int(fields['PPid']),int(fields.get('VmRSS','0 kB').split()[0])*1024)
  except (OSError,ValueError):pass
 included={1}
 for _ in range(10):
  included.update(pid for pid,(parent,rss) in processes.items() if parent in included)
 return sum(rss for pid,(parent,rss) in processes.items() if pid in included)
"""
resource = "import json,importlib.util\n" + rss_function + "\nprint(json.dumps({'rss_bytes':app_rss(),'cpu_usage_usec':int(dict(line.split() for line in open('/sys/fs/cgroup/cpu.stat'))['usage_usec']),'forbidden_packages':[m for m in ['torch','transformers','sentence_transformers','vllm'] if importlib.util.find_spec(m)]}))"

try:
    start=time.monotonic()
    command('docker','run','-d','--name',name,'--memory=16g','--cpus=2','--network=none',image)
    for _ in range(100):
        try:
            p = subprocess.run(['docker','--host=unix:///var/run/docker.sock','exec',name,'python','-c',probe],text=True,capture_output=True)
            if p.returncode == 0 and p.stdout.strip() == 'ok':break
        except subprocess.CalledProcessError:pass
        time.sleep(.1)
    else:raise RuntimeError('container never became healthy')
    ready=time.monotonic()-start
    idle=json.loads(command('docker','exec',name,'python','-c',resource))
    workload=rss_function+"""
import concurrent.futures,urllib.request,threading,time,json,subprocess,os
import numpy as np
latencies=[]
ffmpeg_started=time.monotonic()
media=subprocess.Popen(['ffmpeg','-hide_banner','-loglevel','error','-i','/app/web/.next/standalone/public/sample/video.mp4','-vf','scale=480:-2','-c:v','libx264','-threads','1','-preset','veryfast','-y','/tmp/benchmark.mp4'])
rng=np.random.default_rng(0);matrix=rng.normal(size=(10000,768)).astype('float32');matrix/=np.linalg.norm(matrix,axis=1,keepdims=True)
vector=matrix[0].copy()
def numpy_work():
 for _ in range(50):
  scores=matrix@vector
  assert int(np.argmax(scores))==0
stop=threading.Event(); samples=[]
def monitor():
 while not stop.is_set():
  samples.append(app_rss());time.sleep(.025)
thread=threading.Thread(target=monitor);thread.start()
paths=['/live','/models','/','/search','/ask','/api/demo/videos','/api/demo/stats','/api/health','/api/openapi.json']
def request(i):
 started=time.monotonic()
 path=paths[i%len(paths)]
 try:
  with urllib.request.urlopen('http://127.0.0.1:7860'+path,timeout=15) as r:
   assert r.status==200
   r.read()
   latencies.append((time.monotonic()-started)*1000)
 except Exception as e:raise RuntimeError(path+' failed: '+str(e))
try:
 with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool:
  bench=pool.submit(numpy_work)
  list(pool.map(request,range(180)))
  bench.result()
 assert media.wait(timeout=120)==0
finally:
 stop.set();thread.join()
print(json.dumps({'requests':180,'peak_process_rss_bytes':max(samples),'http_p50_ms':round(float(np.percentile(latencies,50)),2),'http_p95_ms':round(float(np.percentile(latencies,95)),2),'ffmpeg_and_http_elapsed_s':round(time.monotonic()-ffmpeg_started,3),'synthetic_matrix_shape':[10000,768],'matrix_searches':50}))
"""
    workload_started=time.monotonic()
    result=json.loads(command('docker','exec','-e','OPENBLAS_NUM_THREADS=1',name,'python','-c',workload))
    after=json.loads(command('docker','exec',name,'python','-c',resource))
    peak=int(command('docker','exec',name,'cat','/sys/fs/cgroup/memory.peak'))
    output={'image_id':command('docker','inspect',name,'--format','{{.Image}}'),'image_bytes':int(command('docker','image','inspect',image,'--format','{{.Size}}')),'startup_seconds':round(ready,3),'idle_process_rss_bytes':idle['rss_bytes'],'post_workload_process_rss_bytes':after['rss_bytes'],'sampled_peak_process_rss_bytes':max(idle['rss_bytes'],after['rss_bytes'],result['peak_process_rss_bytes']),'cgroup_peak_bytes':peak,'workload_cpu_seconds':round((after['cpu_usage_usec']-idle['cpu_usage_usec'])/1000000,3),'workload_wall_seconds_including_probes':round(time.monotonic()-workload_started,3),'forbidden_packages':idle['forbidden_packages'],'workload':result,'limits':'2 CPUs / 16 GiB; no GPU; no providers configured'}
    print(json.dumps(output,indent=2))
finally:
    subprocess.run(['docker','--host=unix:///var/run/docker.sock','rm','-f',name],stdout=subprocess.DEVNULL,check=False)