char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
4dd127584
char/aliceai-t5-moe
1// Microbenchmarks for the functions on the agentic-thread streaming hot path. 2// 3// Every function here is invoked from a `$derived` that is invalidated on each 4// streamed token, for every tool-call section in the message. These numbers give 5// the per-call cost; multiply by (tokens x sections) for the real damage. 6// 7// Run: npx vitest bench --project=unit tests/unit/agentic-hotpath.bench.ts 8 9import { classifyToolResult , parseToolResultWithMedia } from '$lib/utils/agentic' ; 10import { detectIncompleteCodeBlock , highlightCode } from '$lib/utils/code' ; 11import { computeLineDiff } from '$lib/utils/compute-line-diff' ; 12import { preprocessLaTeX } from '$lib/utils/latex-protection' ; 13import { parsePartialJsonArgs } from '$lib/utils/parse-partial-json-args' ; 14import { extractSearchQuery , extractSearchResults } from '$lib/utils/search-results' ; 15import { all as lowlightAll } from 'lowlight' ; 16import rehypeHighlight from 'rehype-highlight' ; 17import rehypeKatex from 'rehype-katex' ; 18import rehypeStringify from 'rehype-stringify' ; 19import { remark } from 'remark' ; 20import remarkBreaks from 'remark-breaks' ; 21import remarkGfm from 'remark-gfm' ; 22import remarkMath from 'remark-math' ; 23import remarkRehype from 'remark-rehype' ; 24import { bench , describe } from 'vitest' ; 25 26// --- fixtures ------------------------------------------------------------- 27 28function lines ( n :number , seed :string ) :string { 29const out :string [] = []; 30 31for ( let i = 0 ; i < n ; i ++ ) out . push ( ` ${ seed } line ${ i } const value_ ${ i } = compute( ${ i } );` ); 32 33return out . join ( '\n' ); 34} 35 36const SHELL_OUTPUT_1KB = lines ( 12 , 'out' ); 37const SHELL_OUTPUT_200KB = lines ( 2600 , 'out' ); 38const SHELL_OUTPUT_2MB = lines ( 26000 , 'out' ); 39// Realistic exec_shell_command result: the exit-code marker is the final line, 40// which is what the un-anchored EXIT_CODE regex has to scan the whole blob for. 41const SHELL_2MB_WITH_EXIT = ` ${ SHELL_OUTPUT_2MB } \n[exit code: 0]` ; 42const EDIT_OLD_400 = lines ( 400 , 'old' ); 43const EDIT_NEW_400 = lines ( 400 , 'new' ); 44const EDIT_OLD_50 = lines ( 50 , 'old' ); 45const EDIT_NEW_50 = lines ( 50 , 'new' ); 46const WRITE_FILE_ARGS = JSON . stringify ({ 47content :lines ( 1500 , 'src' ), 48path :'/src/lib/thing.ts' 49}); 50const MARKDOWN_50KB = Array . from ( 51{ length :400 }, 52( _ , i ) => 53`## Section ${ i } \n\nSome **bold** prose with a [link](https://example.com) and \`inline\` code.\n\n- bullet one\n- bullet two\n\n\`\`\`ts\nconst x ${ i } = ${ i } ;\n\`\`\`\n` 54). join ( '\n' ); 55const CODE_BLOCK_5KB = lines ( 60 , 'code' ); 56 57// --- A: computeLineDiff (O(m*n) LCS, allocates a full matrix) -------------- 58 59describe ( 'computeLineDiff' , () => { 60bench ( '50 x 50 lines' , () => { 61computeLineDiff ( EDIT_OLD_50 , EDIT_NEW_50 ); 62}); 63 64bench ( '400 x 400 lines' , () => { 65computeLineDiff ( EDIT_OLD_400 , EDIT_NEW_400 ); 66}); 67}); 68 69// --- B: the unified processor, rebuilt per call --------------------------- 70// Mirrors MarkdownContent.svelte:144-176. The local rehype/remark plugins are 71// omitted (they pull in browser-only modules); rehypeHighlight + lowlightAll is 72// the dominant term, so this is a lower bound on the real cost. 73 74function buildProcessor () { 75// eslint-disable-next-line @typescript-eslint/no-explicit-any 76let proc :any = remark (). use ( remarkGfm ); 77 78proc = proc . use ( remarkMath ). use ( remarkBreaks ). use ( remarkRehype ). use ( rehypeKatex ); 79 80return proc . use ( rehypeHighlight , { languages :lowlightAll }). use ( rehypeStringify , { 81allowDangerousHtml :true 82}); 83} 84 85describe ( 'markdown processor' , () => { 86bench ( 'build processor (per processMarkdown call, x2)' , () => { 87buildProcessor (); 88}); 89 90const prebuilt = buildProcessor (); 91 92bench ( 'parse 50KB markdown with a prebuilt processor' , () => { 93prebuilt . parse ( MARKDOWN_50KB ); 94}); 95}); 96 97// Isolates the O(n^2) term measured in Tier 1: processMarkdown re-parses the 98// WHOLE accumulated string every frame, while only the last block can have 99// changed. If parse() dominates at these sizes, incremental parsing (re-parsing 100// only the tail after the last stable block) is the fix; if not, the cost is 101// downstream in transform/stringify/DOM. 102describe ( 'markdown parse scaling (whole-string reparse per frame)' , () => { 103const proc = buildProcessor (); 104const prose = ( kb :number ) => 105Array . from ( 106{ length :Math . ceil (( kb * 1024 ) / 64 ) }, 107( _ , i ) => `The quick brown fox jumps over the lazy dog. Sentence ${ i } .` 108). join ( ' ' ); 109const MD_3KB = prose ( 3 ); 110const MD_11KB = prose ( 11 ); 111const MD_26KB = prose ( 26 ); 112 113bench ( 'parse 3KB' , () => { 114proc . parse ( MD_3KB ); 115}); 116 117bench ( 'parse 11KB' , () => { 118proc . parse ( MD_11KB ); 119}); 120 121bench ( 'parse 26KB' , () => { 122proc . parse ( MD_26KB ); 123}); 124 125bench ( 'parse only a 200-char tail (proposed incremental)' , () => { 126proc . parse ( MD_26KB . slice ( - 200 )); 127}); 128}); 129 130// processMarkdown runs these over the WHOLE accumulated string every frame too, 131// before parse() even starts. Measure them before assuming parse is the target. 132describe ( 'other whole-string passes per frame' , () => { 133const prose = ( kb :number ) => 134Array . from ( 135{ length :Math . ceil (( kb * 1024 ) / 64 ) }, 136( _ , i ) => `The quick brown fox jumps over the lazy dog. Sentence ${ i } .` 137). join ( ' ' ); 138const MD_3KB = prose ( 3 ); 139const MD_11KB = prose ( 11 ); 140const MD_26KB = prose ( 26 ); 141const MD_26KB_LATEX = ` ${ MD_26KB } and some math $x^2 + y^2 = z^2$ inline.` ; 142 143bench ( 'preprocessLaTeX 3KB (no latex present)' , () => { 144preprocessLaTeX ( MD_3KB ); 145}); 146 147bench ( 'preprocessLaTeX 11KB (no latex present)' , () => { 148preprocessLaTeX ( MD_11KB ); 149}); 150 151bench ( 'preprocessLaTeX 26KB (no latex present)' , () => { 152preprocessLaTeX ( MD_26KB ); 153}); 154 155bench ( 'preprocessLaTeX 26KB (latex present)' , () => { 156preprocessLaTeX ( MD_26KB_LATEX ); 157}); 158 159bench ( 'detectIncompleteCodeBlock 26KB' , () => { 160detectIncompleteCodeBlock ( MD_26KB ); 161}); 162}); 163 164// --- C: extractSearchResults, run for EVERY tool of every type ------------ 165 166describe ( 'extractSearchResults (only .length > 0 is consumed)' , () => { 167bench ( '1KB non-search tool result' , () => { 168extractSearchResults ( SHELL_OUTPUT_1KB ); 169}); 170 171bench ( '200KB non-search tool result' , () => { 172extractSearchResults ( SHELL_OUTPUT_200KB ); 173}); 174 175bench ( '2MB non-search tool result' , () => { 176extractSearchResults ( SHELL_OUTPUT_2MB ); 177}); 178}); 179 180describe ( 'extractSearchQuery' , () => { 181bench ( 'write_file args (throws on the JSON.parse path)' , () => { 182extractSearchQuery ( WRITE_FILE_ARGS ); 183}); 184}); 185 186// --- E: the un-anchored exit-code regex ----------------------------------- 187// Reproduced inline so the bench is independent of the current call site. 188 189const EXIT_CODE_TAIL = / \[exit code: (-?\d+)\]\s*$ / ; 190 191describe ( 'exit-code regex' , () => { 192bench ( 'whole 2MB blob (current behaviour)' , () => { 193SHELL_2MB_WITH_EXIT . match ( EXIT_CODE_TAIL ); 194}); 195 196bench ( 'last 64 chars only (proposed)' , () => { 197SHELL_2MB_WITH_EXIT . slice ( - 64 ). match ( EXIT_CODE_TAIL ); 198}); 199}); 200 201// --- per-line result parsers ---------------------------------------------- 202 203describe ( 'parseToolResultWithMedia' , () => { 204bench ( '1KB' , () => { 205parseToolResultWithMedia ( SHELL_OUTPUT_1KB , []); 206}); 207 208bench ( '200KB' , () => { 209parseToolResultWithMedia ( SHELL_OUTPUT_200KB , []); 210}); 211 212bench ( '2MB' , () => { 213parseToolResultWithMedia ( SHELL_OUTPUT_2MB , []); 214}); 215}); 216 217describe ( 'classifyToolResult' , () => { 218bench ( '200KB plain text (falls through to looksLikeMarkdown)' , () => { 219classifyToolResult ( SHELL_OUTPUT_200KB ); 220}); 221}); 222 223describe ( 'parsePartialJsonArgs' , () => { 224bench ( 'write_file args, ~60KB (char-by-char scan)' , () => { 225parsePartialJsonArgs ( WRITE_FILE_ARGS ); 226}); 227}); 228 229// --- F / I: highlight.js --------------------------------------------------- 230 231describe ( 'highlightCode' , () => { 232bench ( 'short bash command (title row, per token)' , () => { 233highlightCode ( 'grep -rn "foo" src/ | head -50' , 'bash' ); 234}); 235 236bench ( '5KB known language' , () => { 237highlightCode ( CODE_BLOCK_5KB , 'typescript' ); 238}); 239 240bench ( '5KB unknown language -> highlightAuto' , () => { 241highlightCode ( CODE_BLOCK_5KB , 'not-a-language' ); 242}); 243});