File size: 12,379 Bytes
952e1e7 7027070 952e1e7 7027070 952e1e7 7027070 952e1e7 7027070 952e1e7 7027070 | 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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | # SPDX-License-Identifier: MIT
"""Native K and U frontends in one isolated process/GPU, changed weight storage only."""
import os,sys,json,time,threading,traceback,contextlib
from pathlib import Path
assert Path('/.dockerenv').exists() and os.environ.get('DUO_ISOLATED')=='1'
import torch,numpy as np
from autotune_guard import install as install_autotune_guard,pin_answer_configs
install_autotune_guard()
import fast_fp8_gemm
fast_fp8_gemm.install()
import transformers
from concurrent.futures import ThreadPoolExecutor
K_STREAM=None
G_PATH=os.environ['REAX_PART_G']
Q_PATH=os.environ['REAX_PART_Q']
from transformers import AutoConfig,AutoTokenizer,AutoModelForCausalLM,AutoModelForImageTextToText,FineGrainedFP8Config,BitsAndBytesConfig
sys.path.insert(0,str(Path(__file__).resolve().parent/'third_party/knowline'))
import knowline_server as ks
from knowline_layout import Layout
mode,scheme="duo","fp8";torch.set_num_threads(4)
def quant():
if scheme=='bf16':return None
if scheme.startswith('fp8'):
return FineGrainedFP8Config(weight_block_size=(128,128) if scheme=='fp8' else (64,64),modules_to_not_convert=['lm_head','visual','model.visual','vision_tower','model.vision_tower','multi_modal_projector','model.multi_modal_projector','audio_tower','model.audio_tower','embed_vision','model.embed_vision','embed_audio','model.embed_audio','in_proj_a','in_proj_b'])
return BitsAndBytesConfig(load_in_8bit=True,llm_int8_threshold=6.0,llm_int8_skip_modules=['lm_head','visual','model.visual','vision_tower','model.vision_tower','multi_modal_projector','model.multi_modal_projector','audio_tower','model.audio_tower','embed_vision','model.embed_vision','embed_audio','model.embed_audio','in_proj_a','in_proj_b'])
def info(m):
from collections import Counter
return {'classes':dict(Counter(type(x).__name__ for x in m.modules())),'parameter_bytes':sum(p.numel()*p.element_size() for p in m.parameters()),'dtypes':dict(Counter(str(p.dtype) for p in m.parameters()))}
def kload():
cfg=AutoConfig.from_pretrained(G_PATH);cls=getattr(transformers,'AutoModelForMultimodalLM',AutoModelForImageTextToText) if hasattr(cfg,'vision_config') or hasattr(cfg,'audio_config') else AutoModelForCausalLM
kw={'dtype':torch.bfloat16,'device_map':{'':'cuda:0'},'attn_implementation':'flex_attention','local_files_only':True}
if quant() is not None:kw['quantization_config']=quant()
m=cls.from_pretrained(G_PATH,**kw).eval();tok=AutoTokenizer.from_pretrained(G_PATH)
class Backend:
def warm(self,*a):pass
def score(self,text,media,ids):
if media:raise ValueError('text only')
with torch.inference_mode():
inputs=torch.tensor([tok.encode(text,add_special_tokens=False)],device='cuda:0')
logits=m(input_ids=inputs,logits_to_keep=1,use_cache=False,kernel_options={'BLOCK_M':32,'BLOCK_N':32}).logits[0,-1].float().log_softmax(-1)
return [float(logits[i]) for i in ids],int(inputs.shape[1])
temps=json.load(open(Path(G_PATH)/'temperatures.json' if (Path(G_PATH)/'temperatures.json').exists() else Path(__file__).resolve().parent/'third_party/knowline/temperatures.json'));engine=ks.KnowLine(tok,Backend(),1.0,1,temps,Layout(read_twice=True,twice_max_tokens=3000))
return engine,m
try:
k,km=kload();u=None
tok=AutoTokenizer.from_pretrained(G_PATH);tiny=torch.tensor([tok.encode('Hello',add_special_tokens=False)],device='cuda:0')
with torch.inference_mode():
one=km(input_ids=tiny,logits_to_keep=1,use_cache=True,kernel_options={'BLOCK_M':32,'BLOCK_N':32}).logits.float()
two=km(input_ids=tiny,logits_to_keep=1,use_cache=False,kernel_options={'BLOCK_M':32,'BLOCK_N':32}).logits.float()
cache_parity=float((one-two).abs().max());assert torch.allclose(one,two,atol=.04,rtol=.04), 'cache-free prefill parity'
del one,two,tiny
if mode=='duo':
sys.path.insert(0,Q_PATH)
import d3_runtime
original=d3_runtime.backbone_class
def backbone(kind):
cls=original(kind)
class QuantizedLoader:
@staticmethod
def from_pretrained(*a,**kw):
kw['quantization_config']=quant();kw['local_files_only']=True
return cls.from_pretrained(*a,**kw)
return QuantizedLoader
d3_runtime.backbone_class=backbone
# Same class used by AutoModel's native d3 entrypoint; wrapper delegates system_one directly.
from modeling_d3 import D3Model
u=D3Model.from_pretrained(Q_PATH,device='cuda:0',local_files_only=True,batch_size=1)
original_run=u.runtime._run
def bounded_run(prepared):
wanted=u.runtime.batch_size
longest=max([len(x) for x in prepared.sequences.values()]+[len(x) for x in prepared.reversed_sequences.values()]+[1])
u.runtime.batch_size=min(wanted,max(1,4096//longest))
try:return original_run(prepared)
finally:u.runtime.batch_size=wanted
u.runtime._run=bounded_run
pin_answer_configs()
ba_patches=[]
state_once_backend=d3_runtime.StateOnce(u.backbone.language_model)
opt_active='plain'
from transformers.models.qwen3_5 import modeling_qwen3_5 as qwen_modeling
from causal_conv1d import causal_conv1d_fn as official_conv
def optimized_conv(x,weight,bias=None,activation=None,**kw):return official_conv(x,weight,bias,activation=activation)
def reference_conv(x,weight,bias=None,activation=None,**kw):
out=torch.nn.functional.conv1d(x.to(weight.dtype),weight[:,None,:],bias,padding=weight.shape[-1]-1,groups=weight.shape[0])[...,:x.shape[-1]]
return (torch.nn.functional.silu(out) if activation in ['silu','swish'] else out).to(x.dtype)
qwen_modeling.causal_conv1d_fn=reference_conv
cx=torch.randn(1,512,1024,device='cuda',dtype=torch.bfloat16);cw=torch.randn(512,4,device='cuda',dtype=torch.bfloat16)
ca=reference_conv(cx,cw,activation='silu');cb=optimized_conv(cx,cw,activation='silu')
conv_probe={'max_abs':float((ca-cb).abs().max()),'mean_abs':float((ca-cb).abs().mean())}
assert torch.allclose(ca,cb,atol=.08,rtol=.02), 'convolution parity prerequisite'
del cx,cw,ca,cb
if scheme!='bf16':
for model in [km]+([u.backbone] if u else []):
assert any('float8' in str(p.dtype) or p.dtype==torch.int8 for p in model.parameters()), 'quantization silently dequantized'
torch.cuda.synchronize()
fast_fp8_gemm.precompile()
print(json.dumps({'ready':True,'autotuner_guard':True,'mode':mode,'scheme':scheme,'cache_parity_max_abs':cache_parity,'gpu':torch.cuda.get_device_name(0),'k':info(km),'u':info(u.backbone) if u else None,'allocated':torch.cuda.memory_allocated(),'reserved':torch.cuda.memory_reserved(),'peak_load_bytes':torch.cuda.max_memory_allocated()}),flush=True)
except BaseException:
traceback.print_exc();raise
K_DONE=threading.Event()
def invoke_k(r):
try:
a,usage=k.run(r['state'],r['questions'])
if opt_active in ['longcache','fastconv_longcache','fast_all_longcache']:torch.cuda.current_stream().synchronize()
return {'answers':a,'usage':usage}
finally:K_DONE.set()
def invoke_u(r):
u.runtime.fast_route={'state_once':False,'oom_fallback':False,'serialized_for_memory':False}
if opt_active in ['state_once','fastconv_state_once','fast_all','longcache','fastconv_longcache','fast_all_longcache'] and len(r['questions'])>1:
prepared=u.runtime.prepare(r['state'],r['questions'])
prefix=max(list(prepared.states.values())+[0]);long_mode=opt_active in ['longcache','fastconv_longcache','fast_all_longcache'];use=0<prefix<=(24576 if long_mode else 16384)
u.runtime.fast_route.update(prefix_tokens=prefix,state_once=use)
if use and long_mode and prefix>16384:
# General length policy: avoid overlapping peak activations for large native prefix caches.
if not K_DONE.wait(300):raise RuntimeError('K completion wait exceeded memory-scheduling budget')
u.runtime.fast_route['serialized_for_memory']=True
u.runtime.state_once=state_once_backend if use else None
try:
result=None
if use:
try:result=u.runtime.run(prepared)
except torch.OutOfMemoryError:pass
if result is None:
if use:
u.runtime.state_once=None;u.runtime.fast_route.update(state_once=False,oom_fallback=True);torch.cuda.empty_cache()
result=u.runtime.run(prepared)
probabilities,tokens=result
return u.runtime.respond(prepared,probabilities,tokens)
finally:u.runtime.state_once=None
return u.system_one(state=r['state'],questions=r['questions'])
from probabilities import mix
norm_patches=[]
for mod_name,mod in km.named_modules():
if type(mod).__name__.endswith('RMSNorm'):
norm_patches.append((mod,mod.forward,None))
def apply_compile_norm(on):
global norm_patches
updated=[]
for mod,plain,compiled in norm_patches:
if on and compiled is None:compiled=torch.compile(plain,dynamic=True)
mod.forward=compiled if on else plain
updated.append((mod,plain,compiled))
norm_patches=updated
stream_pool=ThreadPoolExecutor(2)
stream_k=torch.cuda.Stream();stream_u=torch.cuda.Stream()
stream_k.wait_stream(torch.cuda.current_stream());stream_u.wait_stream(torch.cuda.current_stream())
def probe_graph():
apply_compile_norm(False)
static=torch.tensor([AutoTokenizer.from_pretrained(G_PATH).encode('Hello, how are you?',add_special_tokens=False)],device='cuda:0')
def forward():return km(input_ids=static,logits_to_keep=1,use_cache=False,kernel_options={'BLOCK_M':32,'BLOCK_N':32}).logits.float()
with torch.inference_mode():
for _ in range(3):ref=forward()
torch.cuda.synchronize();ref=ref.clone()
t=time.perf_counter()
for _ in range(20):out=forward()
torch.cuda.synchronize();eager=1000*(time.perf_counter()-t)/20
graph=torch.cuda.CUDAGraph()
with torch.cuda.graph(graph):captured=forward()
graph.replay();torch.cuda.synchronize();diff=float((ref-captured).abs().max())
t=time.perf_counter()
for _ in range(20):graph.replay()
torch.cuda.synchronize();graphed=1000*(time.perf_counter()-t)/20
return {'status':'ok','scope':'diagnostic repeated tiny synthetic K forward only, not representative latency','tokens':static.shape[1],'eager_ms':eager,'graph_ms':graphed,'max_abs_logit_diff':diff}
for line in sys.stdin:
try:
r=json.loads(line);kind=r.pop('_kind','concurrent')
if kind=='graph_probe':
try:probe=probe_graph()
except BaseException as e:probe={'status':'error','scope':'K tiny diagnostic graph capture','error':repr(e)}
print(json.dumps(probe),flush=True);continue
ubatch=int(r.pop('_ubatch',8));opt_active=r.pop('_opt','fastconv_longcache');u.runtime.batch_size=ubatch
for mod,plain,fast in ba_patches:mod.forward=fast if opt_active in ['miner_ba','fast_all','fast_all_longcache'] else plain
apply_compile_norm(opt_active in ['compile_norm','fast_all','fast_all_longcache'])
qwen_modeling.causal_conv1d_fn=optimized_conv if opt_active in ['fastconv','fastconv_state_once','fast_all','fastconv_longcache','fast_all_longcache'] else reference_conv
K_DONE.clear()
if kind=='single_u':K_DONE.set()
torch.cuda.synchronize();torch.cuda.reset_peak_memory_stats();start=time.perf_counter()
a=b=None
if kind=='concurrent':
def call_k():
with torch.cuda.stream(stream_k):
torch._dynamo.config.recompile_limit=128
return invoke_k(r)
def call_u():
with torch.cuda.stream(stream_u):return invoke_u(r)
fa=stream_pool.submit(call_k);fb=stream_pool.submit(call_u)
# Drain both futures even on an error before accepting any next request.
ae=be=None
try:a=fa.result()
except BaseException as e:ae=e
try:b=fb.result()
except BaseException as e:be=e
torch.cuda.synchronize()
if ae or be:raise (ae or be)
mid=start
elif kind=='single_k':
a=invoke_k(r);torch.cuda.synchronize();mid=time.perf_counter()
elif kind=='single_u':
mid=start;b=invoke_u(r)
else:
a=invoke_k(r);torch.cuda.synchronize();mid=time.perf_counter();b=invoke_u(r)
torch.cuda.synchronize();end=time.perf_counter()
combined=mix(r,a,b) if a and b else None;duo_end=time.perf_counter()
result={'K':a,'U':b,'Duo':combined,'timings_ms':{'K':1000*(mid-start),'U':1000*(end-mid),'Duo':1000*(duo_end-start)},'peak_bytes':torch.cuda.max_memory_allocated(),'peak_reserved_bytes':torch.cuda.max_memory_reserved(),'optimization':{'name':opt_active,'miner_ba_patched_modules':len(ba_patches),'compile_norm_modules':len(norm_patches),'conv_probe':conv_probe,'u_route':getattr(u.runtime,'fast_route',None)}}
print(json.dumps({'status':'ok',**result}),flush=True)
except BaseException as e:
traceback.print_exc();torch.cuda.synchronize();torch.cuda.empty_cache()
print(json.dumps({'status':'error','error':str(e),'exception':type(e).__name__}),flush=True)
|