pub fn enabled() -> boolExamples found in repository?
examples/qwen_image_denoiser_speed_fixture.rs (line 1549)
1496fn main() -> Result<(), Box<dyn Error>> {
1497 let mut args = std::env::args_os().skip(1);
1498 let cmf_path = PathBuf::from(
1499 args.next()
1500 .unwrap_or_else(|| "/tmp/qwen-image-speed-q4tp.cmf".into()),
1501 );
1502 let b = args
1503 .next()
1504 .map(|v| v.to_string_lossy().parse::<usize>())
1505 .transpose()?
1506 .unwrap_or(5120);
1507 let hidden = args
1508 .next()
1509 .map(|v| v.to_string_lossy().parse::<usize>())
1510 .transpose()?
1511 .unwrap_or(3072);
1512 let inter = args
1513 .next()
1514 .map(|v| v.to_string_lossy().parse::<usize>())
1515 .transpose()?
1516 .unwrap_or(12288);
1517 let mode = args
1518 .next()
1519 .unwrap_or_default()
1520 .to_string_lossy()
1521 .into_owned();
1522 let qkv = mode == "qkv";
1523 let qwen = mode == "qwen";
1524 let qwen_block = mode == "qwen-block";
1525 let qwen_chain = mode == "qwen-chain";
1526 if !mode.is_empty() && !qkv && !qwen && !qwen_block && !qwen_chain {
1527 return Err("optional mode must be qkv, qwen, qwen-block, or qwen-chain".into());
1528 }
1529 if b < 32 || hidden == 0 || inter == 0 || hidden % 32 != 0 || inter % 32 != 0 {
1530 return Err("geometry requires b>=32 and hidden/inter multiples of 32".into());
1531 }
1532
1533 write_cmf(
1534 &cmf_path,
1535 hidden,
1536 inter,
1537 qkv,
1538 qwen || qwen_block || qwen_chain,
1539 qwen_block || qwen_chain,
1540 )?;
1541 let model = Arc::new(CmfModel::open(&cmf_path)?);
1542 if !model.verify().is_empty() {
1543 return Err(format!("fixture CMF failed verification: {:?}", model.verify()).into());
1544 }
1545 unsafe {
1546 std::env::set_var("CMF_GPU", "wgpu");
1547 std::env::set_var("CMF_GPU_PROBE", "0");
1548 }
1549 if !cortiq_engine::gpu::enabled() {
1550 return Err(
1551 "WGPU backend did not initialize; run with a real Vulkan/Metal WGPU adapter".into(),
1552 );
1553 }
1554
1555 let x = input_values(b, hidden);
1556 let bias_in = bias_values(inter, 13);
1557 let bias_out = bias_values(hidden, 29);
1558 let x_mb = (x.len() * 4) as f64 / (1024.0 * 1024.0);
1559 let mid_mb = (b * inter * 4) as f64 / (1024.0 * 1024.0);
1560 println!(
1561 "speed fixture: backend=wgpu b={b} hidden={hidden} inter={inter} x_f32_mib={x_mb:.1} intermediate_f32_mib={mid_mb:.1} qkv={qkv} qwen={qwen}"
1562 );
1563
1564 if qwen_chain {
1565 let text_tokens = (b / 4).max(1);
1566 qwen_chain_ab(&model, b, text_tokens, hidden, inter)?;
1567 return Ok(());
1568 }
1569 if qwen_block {
1570 let text_tokens = (b / 4).max(1);
1571 qwen_block_ab(&model, b, text_tokens, hidden, inter)?;
1572 return Ok(());
1573 }
1574
1575 if qwen {
1576 let text_tokens = (b / 4).max(1);
1577 // The full image geometry is reserved for the resident attention
1578 // timing; a bounded 128-row panel proves the second Qwen sub-block
1579 // against its decomposed host reference without another large
1580 // activation allocation.
1581 qwen_mlp_ab(&model, b.min(128).max(32), hidden, inter)?;
1582 qwen_attention_ab(&model, b, text_tokens, hidden)?;
1583 return Ok(());
1584 }
1585
1586 let (fallback, fallback_ms) = fallback_mlp(&model, &x, b, hidden, inter, &bias_in, &bias_out)?;
1587 let (fused, fused_ms) = fused_mlp(&model, &x, b, hidden, inter, &bias_in, &bias_out)?;
1588 let mlp_abs = max_abs(&fused, &fallback);
1589 let mlp_rel = rel_rms(&fused, &fallback);
1590 println!(
1591 "mlp A/B: fallback_scalar_ms={fallback_ms:.3} fused_resident_ms={fused_ms:.3} max_abs={mlp_abs:.8e} rel_rms={mlp_rel:.8e}"
1592 );
1593 if !fused.iter().all(|v| v.is_finite()) || mlp_abs > 0.25 || mlp_rel > 5.0e-3 {
1594 return Err(format!(
1595 "fused MLP parity failed: max_abs={mlp_abs:.8e} rel_rms={mlp_rel:.8e}"
1596 )
1597 .into());
1598 }
1599
1600 if qkv {
1601 let (qkv_fallback, fallback_ms) = fallback_qkv(&model, &x, b, hidden)?;
1602 let (qkv_fused, fused_ms) = fused_qkv(&model, &x, b, hidden)?;
1603 let abs = max_abs(&qkv_fused, &qkv_fallback);
1604 let rel = rel_rms(&qkv_fused, &qkv_fallback);
1605 println!(
1606 "qkv A/B: fallback_3x_scalar_ms={fallback_ms:.3} fused_one_submit_ms={fused_ms:.3} max_abs={abs:.8e} rel_rms={rel:.8e}"
1607 );
1608 if !qkv_fused.iter().all(|v| v.is_finite()) || abs > 0.25 || rel > 5.0e-3 {
1609 return Err(
1610 format!("fused QKV parity failed: max_abs={abs:.8e} rel_rms={rel:.8e}").into(),
1611 );
1612 }
1613 }
1614 Ok(())
1615}More examples
examples/qwen_image_denoiser_metal_fixture.rs (line 393)
382fn main() -> Result<(), Box<dyn Error>> {
383 if !cfg!(target_os = "macos") {
384 return Err("this fixture requires the native macOS Metal backend".into());
385 }
386 let require_metal = !matches!(
387 std::env::var("CMF_REQUIRE_METAL").as_deref(),
388 Ok("0") | Ok("off")
389 );
390 if require_metal && std::env::var("CMF_GPU").as_deref() != Ok("1") {
391 return Err("run with CMF_GPU=1 to exercise native Metal (or set CMF_REQUIRE_METAL=0 for an explicit CPU-only oracle run)".into());
392 }
393 let metal_active = gpu::enabled();
394 if require_metal && !metal_active {
395 #[cfg(target_os = "macos")]
396 return Err(format!(
397 "CMF_GPU=1 did not initialize a Metal backend: {:?}",
398 cortiq_engine::gpu_metal::initialization_error()
399 )
400 .into());
401 #[cfg(not(target_os = "macos"))]
402 return Err("CMF_GPU=1 did not initialize a Metal backend".into());
403 }
404 // Enable the existing q4tp phase counters; the fixture reports counts but
405 // never claims a performance result from this correctness run.
406 unsafe { std::env::set_var("CMF_METAL_MMPROF", "1") };
407
408 let mut args = std::env::args_os().skip(1);
409 let fixture_path = PathBuf::from(args.next().ok_or("missing oracle JSON path")?);
410 let f16_path = PathBuf::from(args.next().ok_or("missing F16 CMF path")?);
411 let q4tp_path = PathBuf::from(args.next().ok_or("missing Q4TP CMF path")?);
412 let fixture: Fixture = serde_json::from_slice(&std::fs::read(&fixture_path)?)?;
413 let g = geometry(&fixture)?;
414 let image_tokens = fixture.image.len() / g.in_channels;
415 let combined = image_tokens
416 .checked_add(fixture.text_len)
417 .ok_or("combined sequence overflow")?;
418 if image_tokens < 32 || combined < 128 || g.hidden < 64 || g.head_dim % 2 != 0 {
419 return Err(format!(
420 "fixture does not cross Metal gates: image_tokens={image_tokens} combined={combined} hidden={} head_dim={}",
421 g.hidden, g.head_dim
422 )
423 .into());
424 }
425 write_cmf(&fixture, g, &f16_path, false)?;
426 write_cmf(&fixture, g, &q4tp_path, true)?;
427
428 let f16_model = Arc::new(CmfModel::open(&f16_path)?);
429 if !f16_model.verify().is_empty() {
430 return Err(format!("F16 CMF failed verification: {:?}", f16_model.verify()).into());
431 }
432 let q4tp_model = Arc::new(CmfModel::open(&q4tp_path)?);
433 if !q4tp_model.verify().is_empty() {
434 return Err(format!("Q4TP CMF failed verification: {:?}", q4tp_model.verify()).into());
435 }
436 let f16_transformer = QwenImageTransformer::from_cmf(&f16_model)?;
437 let q4tp_transformer = QwenImageTransformer::from_cmf(&q4tp_model)?;
438
439 #[cfg(target_os = "macos")]
440 let submit_before =
441 cortiq_engine::gpu_metal::METAL_SUBMITS.load(std::sync::atomic::Ordering::Relaxed);
442 #[cfg(target_os = "macos")]
443 let mm_before = cortiq_engine::gpu_metal::MM_N.load(std::sync::atomic::Ordering::Relaxed);
444
445 let (f16_gpu, f16_gpu_time) = run(&f16_transformer, &fixture, false)?;
446 let (f16_cpu, f16_cpu_time) = run(&f16_transformer, &fixture, true)?;
447 let (q4tp_gpu, q4tp_gpu_time) = run(&q4tp_transformer, &fixture, false)?;
448 let (q4tp_cpu, q4tp_cpu_time) = run(&q4tp_transformer, &fixture, true)?;
449
450 #[cfg(target_os = "macos")]
451 let submit_after =
452 cortiq_engine::gpu_metal::METAL_SUBMITS.load(std::sync::atomic::Ordering::Relaxed);
453 #[cfg(target_os = "macos")]
454 let mm_after = cortiq_engine::gpu_metal::MM_N.load(std::sync::atomic::Ordering::Relaxed);
455
456 if f16_gpu.len() != fixture.expected.len() || f16_cpu.len() != fixture.expected.len() {
457 return Err(format!(
458 "F16 output length GPU/CPU={}/{} != oracle {}",
459 f16_gpu.len(),
460 f16_cpu.len(),
461 fixture.expected.len()
462 )
463 .into());
464 }
465 let f16_oracle_err = max_abs(&f16_cpu, &fixture.expected);
466 let f16_cpu_gpu_err = max_abs(&f16_cpu, &f16_gpu);
467 let q4tp_cpu_gpu_err = max_abs(&q4tp_cpu, &q4tp_gpu);
468 let q4tp_vs_f16_err = max_abs(&q4tp_cpu, &f16_cpu);
469 let q4tp_oracle = fixture
470 .expected_q4tp
471 .as_deref()
472 .ok_or("oracle JSON is missing expected_q4tp")?;
473 if q4tp_cpu.len() != q4tp_oracle.len() {
474 return Err(format!(
475 "Q4TP output length {} != dequantized oracle {}",
476 q4tp_cpu.len(),
477 q4tp_oracle.len()
478 )
479 .into());
480 }
481 let q4tp_oracle_err = max_abs(&q4tp_cpu, q4tp_oracle);
482 if f16_gpu.iter().any(|v| !v.is_finite())
483 || f16_cpu.iter().any(|v| !v.is_finite())
484 || q4tp_gpu.iter().any(|v| !v.is_finite())
485 || q4tp_cpu.iter().any(|v| !v.is_finite())
486 {
487 return Err("Metal or CPU full-forward output contained non-finite values".into());
488 }
489 // The native attention kernels use f32 arithmetic but reduction ordering
490 // differs from the CPU path. Keep this a correctness gate, not a perf
491 // claim; a multi-ulp spread is expected on a 4k sequence.
492 if f16_cpu_gpu_err > 2e-3 {
493 return Err(format!("F16 CPU/GPU max_abs={f16_cpu_gpu_err:.8e} > 2e-3").into());
494 }
495 if f16_oracle_err > 2e-3 {
496 return Err(
497 format!("F16 CPU/dequantized Torch max_abs={f16_oracle_err:.8e} > 2e-3").into(),
498 );
499 }
500 if q4tp_cpu_gpu_err > 5e-3 {
501 return Err(format!("Q4TP CPU/GPU max_abs={q4tp_cpu_gpu_err:.8e} > 5e-3").into());
502 }
503 if q4tp_oracle_err > 2e-3 {
504 return Err(
505 format!("Q4TP CPU/dequantized Torch max_abs={q4tp_oracle_err:.8e} > 2e-3").into(),
506 );
507 }
508
509 #[cfg(target_os = "macos")]
510 let (metal_submits, q4tp_matmat_calls) = (submit_after - submit_before, mm_after - mm_before);
511 #[cfg(not(target_os = "macos"))]
512 let (metal_submits, q4tp_matmat_calls) = (0, 0);
513 if metal_active {
514 if metal_submits == 0 {
515 return Err("GPU forward made no Metal command submissions".into());
516 }
517 if q4tp_matmat_calls == 0 {
518 return Err("Q4TP forward did not enter the Metal q4tp matmat path".into());
519 }
520 }
521
522 println!(
523 "qwen_image_denoiser_metal_fixture: backend={} image_tokens={image_tokens} combined={combined} hidden={} layers={} f16_oracle={f16_oracle_err:.8e} f16_cpu_gpu={f16_cpu_gpu_err:.8e} q4tp_oracle={q4tp_oracle_err:.8e} q4tp_cpu_gpu={q4tp_cpu_gpu_err:.8e} q4tp_vs_f16={q4tp_vs_f16_err:.8e} f16_gpu_ms={:.3} f16_cpu_ms={:.3} q4tp_gpu_ms={:.3} q4tp_cpu_ms={:.3} metal_submits={metal_submits} q4tp_matmat_calls={q4tp_matmat_calls} f16_peak={:.3e} q4tp_peak={:.3e}",
524 if metal_active { "metal" } else { "cpu-only" },
525 g.hidden,
526 g.layers,
527 f16_gpu_time.as_secs_f64() * 1e3,
528 f16_cpu_time.as_secs_f64() * 1e3,
529 q4tp_gpu_time.as_secs_f64() * 1e3,
530 q4tp_cpu_time.as_secs_f64() * 1e3,
531 max_abs_finite(&f16_cpu),
532 max_abs_finite(&q4tp_cpu),
533 );
534 Ok(())
535}