Skip to main content

enabled

Function enabled 

Source
pub fn enabled() -> bool
Examples 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
Hide additional 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}