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)