GitHub Actions commited on
Commit
43cc902
Β·
1 Parent(s): cf2a028

sync from abhijitramesh/webgpu-bench@8b6281b77d

Browse files
Files changed (1) hide show
  1. js/run/bench-worker.js +35 -20
js/run/bench-worker.js CHANGED
@@ -267,6 +267,27 @@ function describeError(err) {
267
  return String(err);
268
  }
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  self.onmessage = async (e) => {
271
  const { type } = e.data || {};
272
  if (type !== 'run') {
@@ -387,7 +408,7 @@ async function runOne({ params, opfsPath }) {
387
 
388
  // ─── Init backend ───
389
  status('initializing', 'Initializing llama.cpp backends...');
390
- const initResult = await Module.ccall('bench_init', 'number', [], [], { async: true });
391
  if (initResult !== 0) throw new Error(`bench_init failed: ${initResult}`);
392
  log('Backends initialized');
393
 
@@ -396,12 +417,12 @@ async function runOne({ params, opfsPath }) {
396
  // accidental mmap attempt surfaces as a clear error rather than a
397
  // silent heap copy.
398
  status('loading_model', `Loading model (ctx=${nCtx}, gpu_layers=${nGpuLayers}, mmap=0)...`);
399
- const loadResult = await Module.ccall(
 
400
  'bench_load',
401
  'number',
402
  ['string', 'number', 'number', 'number'],
403
  ['/model.gguf', nCtx, nGpuLayers, 0],
404
- { async: true },
405
  );
406
  if (loadResult !== 0) throw new Error(`bench_load failed: ${loadResult}`);
407
  log('Model loaded');
@@ -412,7 +433,7 @@ async function runOne({ params, opfsPath }) {
412
  // in try/catch β€” if the C side or a backend errors, the run can still
413
  // produce perf numbers, just without memoryInfo on the record.
414
  try {
415
- const raw = await Module.ccall('bench_memory_info', 'string', [], [], { async: true });
416
  result.memoryInfo = parseBenchResult('bench_memory_info', raw);
417
  const dev = (result.memoryInfo.devices || [])
418
  .map(d => `${d.name}(${d.type}) free=${(d.free / (1024 * 1024)).toFixed(0)}MB total=${(d.total / (1024 * 1024)).toFixed(0)}MB`)
@@ -431,24 +452,18 @@ async function runOne({ params, opfsPath }) {
431
  try {
432
  status('consistency', 'Running consistency check...', Date.now());
433
  log(`bench_run("...", ${consistencyNPredict}) β€” consistency phase`);
434
- const raw = await Module.ccall(
435
- 'bench_run', 'string',
436
  ['string', 'number'],
437
- [consistencyPrompt, consistencyNPredict],
438
- { async: true },
439
- );
440
  const r = parseBenchResult('bench_run', raw);
441
  result.output = r.output || '';
442
  result.consistency = { token_ids: r.token_ids || [] };
443
 
444
  if (refTokenIds) {
445
  log('bench_eval_tokens β€” forced-decode vs CPU baseline');
446
- const evalRaw = await Module.ccall(
447
- 'bench_eval_tokens', 'string',
448
  ['string', 'string'],
449
- [consistencyPrompt, refTokenIds],
450
- { async: true },
451
- );
452
  const ev = parseBenchResult('bench_eval_tokens', evalRaw);
453
  result.consistency = { ...result.consistency, ...ev };
454
  if (ev.n_tokens < CONSISTENCY_MIN_TOKENS) {
@@ -482,7 +497,7 @@ async function runOne({ params, opfsPath }) {
482
  // so reps 2..N at the same depth restore from snapshot instead of
483
  // re-running the prefill (mirroring llama-bench's `cstate` reuse).
484
  const setDepth = async (label) => {
485
- const raw = await Module.ccall('bench_set_depth', 'string', ['number'], [nDepth], { async: true });
486
  const r = parseBenchResult(`bench_set_depth(${nDepth}) ${label}`, raw);
487
  if (nDepth > 0) {
488
  log(`bench_set_depth(${nDepth}) ${label}: ${r.cached ? 'restored snapshot' : 'prefilled'}`);
@@ -497,7 +512,7 @@ async function runOne({ params, opfsPath }) {
497
  status('perf', `warmup pp${nPrompt}${depthSuffix}`, Date.now());
498
  await setDepth('pp warmup');
499
  log(`bench_pp(${nPrompt})${depthSuffix} β€” warmup`);
500
- const raw = await Module.ccall('bench_pp', 'string', ['number'], [nPrompt], { async: true });
501
  parseBenchResult('bench_pp warmup', raw);
502
  }
503
  const samples_ns = [];
@@ -505,7 +520,7 @@ async function runOne({ params, opfsPath }) {
505
  status('perf', `pp${nPrompt}${depthSuffix} ${i + 1}/${nReps}`, Date.now());
506
  await setDepth(`pp rep ${i + 1}/${nReps}`);
507
  const t0 = performance.now();
508
- const raw = await Module.ccall('bench_pp', 'string', ['number'], [nPrompt], { async: true });
509
  const t_ns = (performance.now() - t0) * 1e6;
510
  parseBenchResult('bench_pp', raw);
511
  samples_ns.push(t_ns);
@@ -528,7 +543,7 @@ async function runOne({ params, opfsPath }) {
528
  status('perf', `warmup tg${nGen}${depthSuffix}`, Date.now());
529
  await setDepth('tg warmup');
530
  log(`bench_tg(${nGen})${depthSuffix} β€” warmup`);
531
- const raw = await Module.ccall('bench_tg', 'string', ['number'], [nGen], { async: true });
532
  parseBenchResult('bench_tg warmup', raw);
533
  }
534
  const samples_ns = [];
@@ -536,7 +551,7 @@ async function runOne({ params, opfsPath }) {
536
  status('perf', `tg${nGen}${depthSuffix} ${i + 1}/${nReps}`, Date.now());
537
  await setDepth(`tg rep ${i + 1}/${nReps}`);
538
  const t0 = performance.now();
539
- const raw = await Module.ccall('bench_tg', 'string', ['number'], [nGen], { async: true });
540
  const t_ns = (performance.now() - t0) * 1e6;
541
  parseBenchResult('bench_tg', raw);
542
  samples_ns.push(t_ns);
@@ -560,7 +575,7 @@ async function runOne({ params, opfsPath }) {
560
  }
561
  }
562
 
563
- await Module.ccall('bench_exit', null, [], [], { async: true });
564
 
565
  // Close the sync handle so OPFS can release its lock on the file (and
566
  // so a subsequent run can open a fresh handle without colliding).
 
267
  return String(err);
268
  }
269
 
270
+ function formatPhaseError(phase, err) {
271
+ const detail = describeError(err);
272
+ return detail ? `${phase} threw WASM exception (${detail})` : `${phase} threw WASM exception`;
273
+ }
274
+
275
+ async function ccallPhase(Module, phase, returnType, argTypes, args) {
276
+ try {
277
+ return await Module.ccall(phase, returnType, argTypes, args, { async: true });
278
+ } catch (err) {
279
+ throw new Error(formatPhaseError(phase, err));
280
+ }
281
+ }
282
+
283
+ async function ccallPhaseLabel(Module, phaseLabel, exportName, returnType, argTypes, args) {
284
+ try {
285
+ return await Module.ccall(exportName, returnType, argTypes, args, { async: true });
286
+ } catch (err) {
287
+ throw new Error(formatPhaseError(phaseLabel, err));
288
+ }
289
+ }
290
+
291
  self.onmessage = async (e) => {
292
  const { type } = e.data || {};
293
  if (type !== 'run') {
 
408
 
409
  // ─── Init backend ───
410
  status('initializing', 'Initializing llama.cpp backends...');
411
+ const initResult = await ccallPhase(Module, 'bench_init', 'number', [], []);
412
  if (initResult !== 0) throw new Error(`bench_init failed: ${initResult}`);
413
  log('Backends initialized');
414
 
 
417
  // accidental mmap attempt surfaces as a clear error rather than a
418
  // silent heap copy.
419
  status('loading_model', `Loading model (ctx=${nCtx}, gpu_layers=${nGpuLayers}, mmap=0)...`);
420
+ const loadResult = await ccallPhase(
421
+ Module,
422
  'bench_load',
423
  'number',
424
  ['string', 'number', 'number', 'number'],
425
  ['/model.gguf', nCtx, nGpuLayers, 0],
 
426
  );
427
  if (loadResult !== 0) throw new Error(`bench_load failed: ${loadResult}`);
428
  log('Model loaded');
 
433
  // in try/catch β€” if the C side or a backend errors, the run can still
434
  // produce perf numbers, just without memoryInfo on the record.
435
  try {
436
+ const raw = await ccallPhase(Module, 'bench_memory_info', 'string', [], []);
437
  result.memoryInfo = parseBenchResult('bench_memory_info', raw);
438
  const dev = (result.memoryInfo.devices || [])
439
  .map(d => `${d.name}(${d.type}) free=${(d.free / (1024 * 1024)).toFixed(0)}MB total=${(d.total / (1024 * 1024)).toFixed(0)}MB`)
 
452
  try {
453
  status('consistency', 'Running consistency check...', Date.now());
454
  log(`bench_run("...", ${consistencyNPredict}) β€” consistency phase`);
455
+ const raw = await ccallPhase(Module, 'bench_run', 'string',
 
456
  ['string', 'number'],
457
+ [consistencyPrompt, consistencyNPredict]);
 
 
458
  const r = parseBenchResult('bench_run', raw);
459
  result.output = r.output || '';
460
  result.consistency = { token_ids: r.token_ids || [] };
461
 
462
  if (refTokenIds) {
463
  log('bench_eval_tokens β€” forced-decode vs CPU baseline');
464
+ const evalRaw = await ccallPhase(Module, 'bench_eval_tokens', 'string',
 
465
  ['string', 'string'],
466
+ [consistencyPrompt, refTokenIds]);
 
 
467
  const ev = parseBenchResult('bench_eval_tokens', evalRaw);
468
  result.consistency = { ...result.consistency, ...ev };
469
  if (ev.n_tokens < CONSISTENCY_MIN_TOKENS) {
 
497
  // so reps 2..N at the same depth restore from snapshot instead of
498
  // re-running the prefill (mirroring llama-bench's `cstate` reuse).
499
  const setDepth = async (label) => {
500
+ const raw = await ccallPhaseLabel(Module, `bench_set_depth(${nDepth}) ${label}`, 'bench_set_depth', 'string', ['number'], [nDepth]);
501
  const r = parseBenchResult(`bench_set_depth(${nDepth}) ${label}`, raw);
502
  if (nDepth > 0) {
503
  log(`bench_set_depth(${nDepth}) ${label}: ${r.cached ? 'restored snapshot' : 'prefilled'}`);
 
512
  status('perf', `warmup pp${nPrompt}${depthSuffix}`, Date.now());
513
  await setDepth('pp warmup');
514
  log(`bench_pp(${nPrompt})${depthSuffix} β€” warmup`);
515
+ const raw = await ccallPhaseLabel(Module, `bench_pp warmup (${nPrompt}${depthSuffix})`, 'bench_pp', 'string', ['number'], [nPrompt]);
516
  parseBenchResult('bench_pp warmup', raw);
517
  }
518
  const samples_ns = [];
 
520
  status('perf', `pp${nPrompt}${depthSuffix} ${i + 1}/${nReps}`, Date.now());
521
  await setDepth(`pp rep ${i + 1}/${nReps}`);
522
  const t0 = performance.now();
523
+ const raw = await ccallPhaseLabel(Module, `bench_pp rep ${i + 1}/${nReps} (${nPrompt}${depthSuffix})`, 'bench_pp', 'string', ['number'], [nPrompt]);
524
  const t_ns = (performance.now() - t0) * 1e6;
525
  parseBenchResult('bench_pp', raw);
526
  samples_ns.push(t_ns);
 
543
  status('perf', `warmup tg${nGen}${depthSuffix}`, Date.now());
544
  await setDepth('tg warmup');
545
  log(`bench_tg(${nGen})${depthSuffix} β€” warmup`);
546
+ const raw = await ccallPhaseLabel(Module, `bench_tg warmup (${nGen}${depthSuffix})`, 'bench_tg', 'string', ['number'], [nGen]);
547
  parseBenchResult('bench_tg warmup', raw);
548
  }
549
  const samples_ns = [];
 
551
  status('perf', `tg${nGen}${depthSuffix} ${i + 1}/${nReps}`, Date.now());
552
  await setDepth(`tg rep ${i + 1}/${nReps}`);
553
  const t0 = performance.now();
554
+ const raw = await ccallPhaseLabel(Module, `bench_tg rep ${i + 1}/${nReps} (${nGen}${depthSuffix})`, 'bench_tg', 'string', ['number'], [nGen]);
555
  const t_ns = (performance.now() - t0) * 1e6;
556
  parseBenchResult('bench_tg', raw);
557
  samples_ns.push(t_ns);
 
575
  }
576
  }
577
 
578
+ await ccallPhase(Module, 'bench_exit', null, [], []);
579
 
580
  // Close the sync handle so OPFS can release its lock on the file (and
581
  // so a subsequent run can open a fresh handle without colliding).