feat(agent): raise worker cap to 32 + size the HTTP pool for it (#114)
At 8 workers the GPU sat at ~5% util / <5GB VRAM — the pipeline is I/O-bound (downloading + decoding images over HTTP), so the GPU starves until many workers overlap that I/O. Raise MAX_CONCURRENCY 8→32 and make the UI worker control a number input (reaching 32 by ±1 was tedious); the cap is reported via /status so the UI clamps to it. Also size the shared requests pool (pool_maxsize=64) — the default 10 would have throttled 32 workers + spammed "connection pool is full". Verified by running; watch GPU util/VRAM climb as you dial up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
This commit is contained in:
+12
-5
@@ -75,9 +75,11 @@ _PAGE = """<!doctype html><html><head><meta charset=utf-8>
|
|||||||
<div class=row>
|
<div class=row>
|
||||||
workers
|
workers
|
||||||
<button class=step onclick=setc(-1)>−</button>
|
<button class=step onclick=setc(-1)>−</button>
|
||||||
<b id=conc style=margin:0+.5rem>1</b>
|
<input id=conc type=number min=1 value=1
|
||||||
|
style="width:3.5rem;font:700 16px system-ui;text-align:center;background:#222;color:#e8e8e8;border:1px solid #444;border-radius:6px;padding:.3rem"
|
||||||
|
onchange="setv(this.value)">
|
||||||
<button class=step onclick=setc(1)>+</button>
|
<button class=step onclick=setc(1)>+</button>
|
||||||
<span class=cap style=color:#9aa>(more = faster + more GPU)</span>
|
<span class=cap style=color:#9aa>(more = overlap I/O, fill the GPU) max <b id=capn>8</b></span>
|
||||||
</div>
|
</div>
|
||||||
<div class=row>
|
<div class=row>
|
||||||
<span class=stat><span class=n id=state>stopped</span><br>state</span>
|
<span class=stat><span class=n id=state>stopped</span><br>state</span>
|
||||||
@@ -89,16 +91,21 @@ _PAGE = """<!doctype html><html><head><meta charset=utf-8>
|
|||||||
<div class=bar><i id=gpubar style=width:0%></i></div>
|
<div class=bar><i id=gpubar style=width:0%></i></div>
|
||||||
<div class=q id=queue></div>
|
<div class=q id=queue></div>
|
||||||
<script>
|
<script>
|
||||||
|
let CAP=8
|
||||||
async function act(p){await fetch('/'+p,{method:'POST'});refresh()}
|
async function act(p){await fetch('/'+p,{method:'POST'});refresh()}
|
||||||
async function setc(d){
|
function setc(d){ setv((parseInt(conc.value||'1'))+d) }
|
||||||
const v=Math.max(1,Math.min(8,parseInt(conc.textContent||'1')+d))
|
async function setv(v){
|
||||||
|
v=Math.max(1,Math.min(CAP,parseInt(v)||1)); conc.value=v
|
||||||
await fetch('/concurrency',{method:'POST',headers:{'Content-Type':'application/json'},
|
await fetch('/concurrency',{method:'POST',headers:{'Content-Type':'application/json'},
|
||||||
body:JSON.stringify({value:v})});refresh()
|
body:JSON.stringify({value:v})});refresh()
|
||||||
}
|
}
|
||||||
async function refresh(){
|
async function refresh(){
|
||||||
const s=await (await fetch('/status')).json()
|
const s=await (await fetch('/status')).json()
|
||||||
|
CAP=s.max_concurrency||8; capn.textContent=CAP
|
||||||
state.textContent=s.state; active.textContent=s.active; done.textContent=s.processed
|
state.textContent=s.state; active.textContent=s.active; done.textContent=s.processed
|
||||||
err.textContent=s.errors; conc.textContent=s.concurrency; fc.textContent=s.fc_url
|
err.textContent=s.errors; fc.textContent=s.fc_url
|
||||||
|
if(document.activeElement!==conc) conc.value=s.concurrency
|
||||||
|
conc.max=CAP
|
||||||
cfg.textContent=s.configured?'set':'MISSING'
|
cfg.textContent=s.configured?'set':'MISSING'
|
||||||
if(s.gpu){
|
if(s.gpu){
|
||||||
gpu.textContent=`GPU — ${s.gpu.util_pct}% util · VRAM ${s.gpu.mem_used_mb}/${s.gpu.mem_total_mb} MB · ${s.gpu.temp_c}°C`
|
gpu.textContent=`GPU — ${s.gpu.util_pct}% util · VRAM ${s.gpu.mem_used_mb}/${s.gpu.mem_total_mb} MB · ${s.gpu.temp_c}°C`
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ The agent's ONLY contact with FC — lease/submit/heartbeat/fail + fetch image
|
|||||||
bytes, all over HTTP with the bearer token. No DB/Redis.
|
bytes, all over HTTP with the bearer token. No DB/Redis.
|
||||||
"""
|
"""
|
||||||
import requests
|
import requests
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
|
||||||
|
|
||||||
class FcClient:
|
class FcClient:
|
||||||
@@ -12,6 +13,11 @@ class FcClient:
|
|||||||
self.agent_id = agent_id
|
self.agent_id = agent_id
|
||||||
self.s = requests.Session()
|
self.s = requests.Session()
|
||||||
self.s.headers["Authorization"] = f"Bearer {token}"
|
self.s.headers["Authorization"] = f"Bearer {token}"
|
||||||
|
# Many worker threads share this Session; the default pool (10) would
|
||||||
|
# throttle them + spam "connection pool is full". Size it for the cap.
|
||||||
|
adapter = HTTPAdapter(pool_connections=64, pool_maxsize=64)
|
||||||
|
self.s.mount("http://", adapter)
|
||||||
|
self.s.mount("https://", adapter)
|
||||||
|
|
||||||
def lease(self, batch_size: int) -> list[dict]:
|
def lease(self, batch_size: int) -> list[dict]:
|
||||||
r = self.s.post(
|
r = self.s.post(
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ from .client import FcClient
|
|||||||
from .config import Config
|
from .config import Config
|
||||||
from .crops import crop_region
|
from .crops import crop_region
|
||||||
|
|
||||||
MAX_CONCURRENCY = 8
|
# Generous cap: the pipeline is usually I/O-bound (downloading + decoding images
|
||||||
|
# over HTTP), so the GPU stays underused until many workers overlap that I/O.
|
||||||
|
# Push it up while watching the GPU util + VRAM in the UI.
|
||||||
|
MAX_CONCURRENCY = 32
|
||||||
|
|
||||||
|
|
||||||
class _Slot:
|
class _Slot:
|
||||||
@@ -74,6 +77,7 @@ class Worker:
|
|||||||
return {
|
return {
|
||||||
"state": "running" if self._running else "stopped",
|
"state": "running" if self._running else "stopped",
|
||||||
"concurrency": self._target,
|
"concurrency": self._target,
|
||||||
|
"max_concurrency": MAX_CONCURRENCY,
|
||||||
"workers": len(self._slots),
|
"workers": len(self._slots),
|
||||||
"active": self._active,
|
"active": self._active,
|
||||||
"processed": self.processed,
|
"processed": self.processed,
|
||||||
|
|||||||
Reference in New Issue
Block a user