CoolFace
Modelpublic

patdev/k3-a40-bootstrap

sourceHugging Faceotherupdated 20d agoView on Hugging Face
0likes1.3kdownloads
balayage4.py320 linesDownload Raw Back to root
1# Balayage 3 : reprise du 2, avec DEUX corrections.2#3# CORRECTION 1 -- le detecteur d'echec. Le balayage 2 declarait morte toute4# configuration dont le journal contenait "is not supported", et il a mordu sur5# un AVERTISSEMENT benin :6#     WARNING [compilation.py:1414] CUDAGraphMode.FULL is not supported with7#     Qwen3_8FlashNextQSAFlashAttentionBackend (support: UNIFORM_BATCH)8# Le moteur repliait gracieusement en PIECEWISE et demarrait normalement ; la9# preuve, l'abandon tombait 5,2 min apres le lancement, soit APRES le10# chargement des poids et la compilation. Desormais on n'abandonne que sur une11# ligne ERROR / Traceback / EngineDead, et JAMAIS sur une ligne WARNING.12#13# CORRECTION 2 -- une seule variable a la fois, pour de vrai. Passer14# --compilation-config remet `cudagraph_mode` a son defaut FULL au lieu du15# FULL_AND_PIECEWISE que vLLM choisit tout seul pour ce modele. Le balayage 216# changeait donc DEUX choses a la fois sans que je le voie. On restate donc17# cudagraph_mode dans chaque JSON, et la premiere configuration est un TEMOIN18# qui ne change que ca : s'il retrouve les chiffres de la reference, la syntaxe19# est bonne et tout ecart ulterieur est imputable au vrai levier.20import json21import os22import re23import subprocess24import time25import urllib.request26from concurrent.futures import ThreadPoolExecutor27 28BASE = "http://127.0.0.1:8000"29MODELE = "RadixArk/Qwen3.8-Flash-Next-NVFP4"30LOG = "/travail/vllm.log"31JOURNAL = "/travail/banc.log"32DEBUT = time.time()33PLAFOND = 330034 35SOCLE = {36    "--served-model-name": "flashnext",37    "--host": "127.0.0.1",38    "--port": "8000",39    "--max-model-len": "262144",40    "--max-num-seqs": "16",41    "--gpu-memory-utilization": "0.93",42    "--distributed-executor-backend": "mp",43    "--reasoning-parser": "qwen3",44    "--tool-call-parser": "qwen3_coder",45    "--limit-mm-per-prompt": '{"image":0,"video":0}',46}47FANIONS = ["--trust-remote-code", "--enable-prefix-caching",48           "--enable-auto-tool-choice", "--no-enable-flashinfer-autotune"]49 50CG = {"cudagraph_mode": "FULL_AND_PIECEWISE"}51 52 53def cc(**extra):54    d = dict(CG)55    d.update(extra)56    return json.dumps(d)57 58 59CONFIGS = [60    ("ctrl_cg",   {"--compilation-config": cc()}, [], []),61    ("fusions",   {"--compilation-config": cc(pass_config={62        "fuse_norm_quant": True, "fuse_attn_quant": True,63        "enable_qk_norm_rope_fusion": True,64        "fuse_qk_norm_rope_kvcache": True})}, [], []),65    ("customops", {"--compilation-config": cc(66        custom_ops=["+rms_norm", "+fused_add_rms_norm"])}, [], []),67    ("autotune",  {}, ["--enable-flashinfer-autotune"],68                      ["--no-enable-flashinfer-autotune"]),69]70 71 72def dire(msg):73    ligne = "[%5.1f min] %s" % ((time.time() - DEBUT) / 60, msg)74    print(ligne, flush=True)75    with open(JOURNAL, "a") as f:76        f.write(ligne + "\n")77 78 79def metrique(nom):80    try:81        t = urllib.request.urlopen(BASE + "/metrics", timeout=5).read().decode()82        m = re.search(r"^vllm:%s\S*\s+([0-9.eE+-]+)$" % nom, t, re.M)83        return float(m.group(1)) if m else None84    except Exception:85        return None86 87 88def attendre_vide(limite=180):89    t0, libre = time.time(), 090    while time.time() - t0 < limite:91        r, w = metrique("num_requests_running"), metrique("num_requests_waiting")92        libre = libre + 1 if (r == 0 and (w or 0) == 0) else 093        if libre >= 2:94            return True95        time.sleep(5)96    return False97 98 99def arreter():100    out = subprocess.run(["ps", "-eo", "pid,rss,comm", "--no-headers"],101                         capture_output=True, text=True).stdout102    for l in out.splitlines():103        p = l.split(None, 2)104        if len(p) != 3:105            continue106        pid, rss, comm = int(p[0]), int(p[1]), p[2].strip()107        if re.match(r"^(vllm|VLLM|Ple|EngineCore)", comm) or \108           (rss > 10_000_000 and re.match(r"^python3?$", comm)):109            try:110                os.kill(pid, 9)111            except Exception:112                pass113    time.sleep(12)114 115 116# Seuls ces trois motifs sont sans ambiguite. Le balayage 2 abandonnait sur117# "is not supported" (un WARNING de repli des graphes CUDA) et la premiere118# correction abandonnait sur "ERROR" (un lint de docstring de transformers).119# Un vrai echec produit TOUJOURS "Engine core initialization failed" -- verifie120# sur le plantage MTP et sur les trois refus de quantification de KV.121FATAL = re.compile(r"Engine core initialization failed|EngineDeadError|"122                   r"Traceback \(most recent call last\)")123 124 125def raison(txt):126    m = re.findall(r"^.*(?:Error|Exception|not supported|NotImplemented).*$",127                   txt, re.M)128    return " || ".join(x.strip()[:200] for x in m[-3:]) or "cause non identifiee"129 130 131def lancer(nom, remplace, ajout, retire):132    if os.path.exists(LOG):133        os.replace(LOG, "/travail/vllm-%s-precedent.log" % nom)134    opts = dict(SOCLE)135    opts.update(remplace)136    cmd = ["vllm", "serve", MODELE]137    for k, v in opts.items():138        cmd += [k, v]139    cmd += [f for f in FANIONS if f not in retire] + ajout140    env = dict(os.environ, VLLM_PLE_CPU_OFFLOAD="1")141    with open(LOG, "w") as sortie:142        subprocess.Popen(cmd, stdout=sortie, stderr=subprocess.STDOUT,143                         stdin=subprocess.DEVNULL, start_new_session=True, env=env)144    for i in range(45):145        time.sleep(20)146        try:147            urllib.request.urlopen(BASE + "/v1/models", timeout=5)148            return True, ""149        except Exception:150            pass151        try:152            txt = open(LOG, errors="replace").read()153        except Exception:154            txt = ""155        if FATAL.search(txt):156            return False, raison(txt)157    return False, "jamais pret en 15 min"158 159 160def kv_annonce():161    try:162        txt = open(LOG, errors="replace").read()163        m = re.findall(r"GPU KV cache size: ([\d,]+) tokens.*?concurrency "164                       r"for [\d,]+ tokens per request: ([\d.]+)x", txt)165        if m:166            return int(m[-1][0].replace(",", "")), float(m[-1][1])167    except Exception:168        pass169    return None, None170 171 172def preuve():173    """Le levier a-t-il MORDU ? Sans ca, un reglage ignore ressemble a un174    reglage sans effet -- exactement le piege d'--async-scheduling."""175    try:176        txt = open(LOG, errors="replace").read()177        out = []178        for motif in (r"cudagraph_mode[^,}\n]{0,40}",179                      r"enabled_custom_ops[^\n]{0,120}",180                      r"custom_ops'?: ?\[[^\]]{0,80}\]",181                      r"'fuse_norm_quant': ?\w+",182                      r"Capturing CUDA graphs \(\w+\)"):183            t = re.findall(motif, txt)184            if t:185                out.append(t[-1][:120])186        return " ; ".join(out)[:300]187    except Exception:188        return ""189 190 191def appel(prompt, n, ignore=True):192    corps = json.dumps({"model": "flashnext", "prompt": prompt, "max_tokens": n,193                        "ignore_eos": ignore, "temperature": 0.7}).encode()194    t = time.time()195    r = json.loads(urllib.request.urlopen(urllib.request.Request(196        BASE + "/v1/completions", data=corps,197        headers={"content-type": "application/json"}), timeout=400).read().decode())198    u = r["usage"]199    return (u["completion_tokens"], u["prompt_tokens"], time.time() - t,200            r["choices"][0]["text"])201 202 203def div4(texte):204    mots = texte.split()205    if len(mots) < 40:206        return None207    q = [tuple(mots[i:i + 4]) for i in range(len(mots) - 3)]208    return len(set(q)) / len(q)209 210 211def banc(nom):212    r = {"nom": nom}213    r["kv"], r["conc"] = kv_annonce()214    r["preuve"] = preuve()215    if not attendre_vide():216        r["erreur"] = "moteur jamais libre"217        return r218    try:219        appel("Bonjour.", 64)220        n, _, d, _ = appel("Ecris un texte long et detaille sur la mer.", 300)221        r["solo"] = round(n / d, 1)222        r["ms_pas"] = round(d * 1000 / n, 2)223        t = time.time()224        with ThreadPoolExecutor(max_workers=8) as ex:225            res = list(ex.map(lambda i: appel(226                "Ecris un texte long et detaille sur la mer numero %d." % i, 200),227                range(8)))228        r["agg8"] = round(sum(x[0] for x in res) / (time.time() - t), 1)229        long = " ".join("mot%d" % i for i in range(24000))230        _, p1, d1, _ = appel(long, 1)231        r["prefill"] = round(p1 / d1)232        _, _, _, txt = appel("Explique en detail comment fonctionne une maree, "233                             "en francais.", 220, ignore=False)234        r["div4"] = round(div4(txt), 3) if div4(txt) else None235    except Exception as e:236        r["erreur"] = repr(e)[:200]237    return r238 239 240resultats = []241dire("=== REFERENCE")242arreter()243ok, motif = lancer("base", {}, [], [])244if ok:245    ref = banc("base")246    resultats.append(ref)247    dire("  base : %s" % json.dumps(ref, ensure_ascii=False))248else:249    dire("  base NE DEMARRE PAS -- %s" % motif)250    resultats.append({"nom": "base", "echec": motif})251 252for nom, remplace, ajout, retire in CONFIGS:253    if time.time() - DEBUT > PLAFOND:254        dire("!!! plafond de temps, on passe a la config finale")255        break256    dire("=== %s" % nom)257    arreter()258    ok, motif = lancer(nom, remplace, ajout, retire)259    if not ok:260        dire("  %s : NE DEMARRE PAS -- %s" % (nom, motif))261        resultats.append({"nom": nom, "echec": motif})262        continue263    r = banc(nom)264    resultats.append(r)265    dire("  %s : %s" % (nom, json.dumps(r, ensure_ascii=False)))266 267with open("/travail/balayage3.json", "w") as f:268    json.dump(resultats, f, indent=1, ensure_ascii=False)269 270 271def trouve(nom):272    for r in resultats:273        if r["nom"] == nom and "echec" not in r and "erreur" not in r:274            return r275    return None276 277 278base = trouve("base") or {}279opts, ajout, retire, raisons, meilleur = {}, [], [], [], None280for nom, remplace, aj, re_ in CONFIGS:281    r = trouve(nom)282    if not r or not r.get("solo") or not r.get("agg8"):283        continue284    if (r["solo"] > (base.get("solo") or 0) * 1.02285            and r["agg8"] >= (base.get("agg8") or 0) * 0.98286            and (r.get("div4") or 0) >= 0.8):287        if not meilleur or r["solo"] > meilleur["solo"]:288            meilleur, opts, ajout, retire = r, remplace, aj, re_289            raisons = ["%s : solo %s vs %s, agg8 %s vs %s" %290                       (nom, r["solo"], base.get("solo"),291                        r["agg8"], base.get("agg8"))]292 293dire("=== CONFIG FINALE : %s" % (", ".join(raisons) or "reference telle quelle"))294arreter()295ok, motif = lancer("final", opts, ajout, retire)296if not ok:297    dire("  finale KO (%s) -- repli sur la reference" % motif)298    arreter()299    ok, motif = lancer("repli", {}, [], [])300    dire("  repli : %s %s" % ("OK" if ok else "ECHEC", motif))301kv, conc = kv_annonce()302dire("=== SERVEUR RENDU : kv=%s, concurrence %sx, options=%s %s" % (kv, conc, opts, ajout))303 304print("\n\n########## TABLEAU ##########")305print("%-11s %9s %6s %6s %8s %7s %6s" %306      ("config", "kv", "solo", "ms/pas", "agg8", "prefill", "div4"))307for r in resultats:308    if "echec" in r:309        print("%-11s  NE DEMARRE PAS : %s" % (r["nom"], r["echec"][:100]))310    elif "erreur" in r:311        print("%-11s  MESURE REFUSEE : %s" % (r["nom"], r["erreur"][:100]))312    else:313        print("%-11s %9s %6s %6s %8s %7s %6s" %314              (r["nom"], r.get("kv"), r.get("solo"), r.get("ms_pas"),315               r.get("agg8"), r.get("prefill"), r.get("div4")))316print("\n########## PREUVE QUE LE LEVIER A MORDU ##########")317for r in resultats:318    if r.get("preuve"):319        print("  %-11s %s" % (r["nom"], r["preuve"]))320