#414 left this crate as the reason the `-D warnings` gate covers loader-gguf on default features only: clippy reports it through the path dependency, so gating the `fabric-meter` configuration needs it clean too. I described its warnings in #414 as "unused in THAT configuration and needed in others, so they want cfg work rather than deletion". That was wrong on two counts, and measuring it directly settled both: - the crate has NO `[features]` at all, only a macOS-conditional dependency block, so there is one configuration and the unused items are unconditionally unused; - there are 84 of them, not 43. The 43 was what leaked through loader-gguf's lint, not the crate's own count. 82 of the 84 are in mps_matmul.rs. 41 came from clippy's own suggestions (manual_is_multiple_of 23, unnecessary_cast 8, needless_option_as_deref 3, excessive_precision, doc_lazy_continuation, double_parens, chunks_exact_to_as_chunks). The other 42 -- 27 `unused_unsafe` and 15 `unused_imports` -- carry no machine-applicable suggestion at all. Neither `clippy --fix` nor `cargo fix` touches them, with or without `--all-targets`. They are plain code, not macro expansions, so they were fixed line-guided from rustc's own line numbers: - `|| unsafe { .. }` -> `|| { .. }` where objc2 0.6 made the call safe. Dropping a block rustc has already proved redundant cannot change behaviour. - `use objc2_metal::{MTLComputePipelineState as _, MTLLibrary as _}` -> just the name the function needs. The unused one is a trait brought in as `_`; the type itself is referenced fully qualified on the lines above, which is why it reads as used at a glance. Checked before deleting, because this is the shape that broke #372: each unused import was matched against every other mention of the name in its file and against the file's six `cfg(not(target_os = "macos"))` branches. None appears in one, and objc2-metal is a macOS-only dependency, so no other configuration can want them. Verified: 0 errors, 37 backend-apple tests (32 pass, 5 ignored), and loader-gguf still builds and its fabric-meter `process_calibration` passes. Co-Authored-By: Claude Opus 5 <[email protected]>
85 lines
4.4 KiB
Rust
85 lines
4.4 KiB
Rust
//! matmul2d replicating the liuliu/example_matmul_metal4 recipe:
|
|
//! A[K,M], B[N,K], C[M,N], no transpose, K = dynamic_length_v<int>
|
|
//! (NOT dynamic_extent — that caps K at one tile), plain slice(),
|
|
//! single run(). Full-K test: A column m=3 all ones, B all ones
|
|
//! => C[3,n] = K for all n; rest 0.
|
|
#[cfg(target_os = "macos")]
|
|
fn main() {
|
|
use objc2::rc::Retained;
|
|
use objc2::runtime::ProtocolObject;
|
|
use objc2_foundation::NSString;
|
|
use objc2_metal::{
|
|
MTLBuffer, MTLCommandBuffer, MTLCommandEncoder, MTLCommandQueue,
|
|
MTLComputeCommandEncoder, MTLComputePipelineState, MTLCreateSystemDefaultDevice,
|
|
MTLDevice, MTLLibrary, MTLResourceOptions, MTLSize,
|
|
};
|
|
fn f32_to_bf16(v: f32) -> u16 { let b = v.to_bits(); ((b + (((b >> 16) & 1) + 0x7fff)) >> 16) as u16 }
|
|
|
|
const KERNEL: &str = r#"
|
|
#include <metal_stdlib>
|
|
#include <MetalPerformancePrimitives/MetalPerformancePrimitives.h>
|
|
using namespace metal;
|
|
using namespace mpp::tensor_ops;
|
|
// A[K,M]=[256,128], B[N,K]=[64,256], C[M,N]=[128,64].
|
|
kernel void k(device bfloat* A_buf [[buffer(0)]],
|
|
device bfloat* B_buf [[buffer(1)]],
|
|
device float* C_buf [[buffer(2)]],
|
|
uint2 tgid [[threadgroup_position_in_grid]]) {
|
|
auto A = tensor<device bfloat, dextents<int32_t,2>, tensor_inline>(A_buf, dextents<int32_t,2>(256, 128));
|
|
auto B = tensor<device bfloat, dextents<int32_t,2>, tensor_inline>(B_buf, dextents<int32_t,2>(64, 256));
|
|
auto C = tensor<device float, dextents<int32_t,2>, tensor_inline>(C_buf, dextents<int32_t,2>(128, 64));
|
|
constexpr auto desc = matmul2d_descriptor(64, 32, dynamic_length_v<int>, false, false, false,
|
|
matmul2d_descriptor::mode::multiply);
|
|
matmul2d<desc, execution_simdgroups<4>> mm;
|
|
auto mA = A.slice(0, tgid.y * 64);
|
|
auto mB = B.slice(tgid.x * 32, 0);
|
|
auto mC = C.slice(tgid.x * 32, tgid.y * 64);
|
|
mm.run(mA, mB, mC);
|
|
}
|
|
"#;
|
|
let device = MTLCreateSystemDefaultDevice().expect("no device");
|
|
println!("device: {}", device.name());
|
|
let queue = device.newCommandQueue().unwrap();
|
|
let lib = match device.newLibraryWithSource_options_error(&NSString::from_str(KERNEL), None) {
|
|
Ok(l) => l, Err(e) => { println!("COMPILE FAIL:\n{}", e.localizedDescription()); return; }
|
|
};
|
|
let func = lib.newFunctionWithName(&NSString::from_str("k")).unwrap();
|
|
let pipe: Retained<ProtocolObject<dyn MTLComputePipelineState>> =
|
|
device.newComputePipelineStateWithFunction_error(&func).unwrap();
|
|
let (m, n, k) = (128usize, 64usize, 256usize);
|
|
// A[K,M]: a_bf[kk*M+mm]=1 iff mm==3 (column m=3 all ones).
|
|
let a_bf: Vec<u16> = (0..k * m).map(|_| f32_to_bf16(1.0)).collect();
|
|
let b_bf: Vec<u16> = (0..n * k).map(|i| f32_to_bf16(if i == 200 { 1.0 } else { 0.0 })).collect();
|
|
let mk = |d: &[u16]| unsafe { device.newBufferWithBytes_length_options(
|
|
std::ptr::NonNull::new(d.as_ptr() as *mut _).unwrap(), std::mem::size_of_val(d),
|
|
MTLResourceOptions::StorageModeShared).unwrap() };
|
|
let buf_a = mk(&a_bf);
|
|
let buf_b = mk(&b_bf);
|
|
let buf_c = device.newBufferWithLength_options(m * n * 4, MTLResourceOptions::StorageModeShared).unwrap();
|
|
let cp = MTLBuffer::contents(&*buf_c).as_ptr() as *mut f32;
|
|
unsafe { std::ptr::write_bytes(cp, 0, m * n) };
|
|
unsafe {
|
|
let cb = queue.commandBuffer().unwrap();
|
|
let enc = cb.computeCommandEncoder().unwrap();
|
|
enc.setComputePipelineState(&pipe);
|
|
enc.setBuffer_offset_atIndex(Some(&buf_a), 0, 0);
|
|
enc.setBuffer_offset_atIndex(Some(&buf_b), 0, 1);
|
|
enc.setBuffer_offset_atIndex(Some(&buf_c), 0, 2);
|
|
// grid: N/32 x M/64 = 2 x 2
|
|
enc.dispatchThreadgroups_threadsPerThreadgroup(
|
|
MTLSize { width: n / 32, height: m / 64, depth: 1 },
|
|
MTLSize { width: 128, height: 1, depth: 1 });
|
|
enc.endEncoding();
|
|
cb.commit();
|
|
cb.waitUntilCompleted();
|
|
}
|
|
let c: Vec<f32> = (0..m * n).map(|i| unsafe { *cp.add(i) }).collect();
|
|
println!("single-element B_buf[200]=1, A all ones. [N,K]:n=200/256,k=200%256; [K,N]:k=200/64,n=200%64");
|
|
let nzv: Vec<(usize, i32)> = c.iter().enumerate().filter(|(_, v)| v.abs() > 0.5).map(|(i, v)| (i, *v as i32)).collect();
|
|
println!("nonzeros: {}", nzv.len());
|
|
for (i, v) in nzv.iter().take(8) {
|
|
println!(" c[{i}] = {v} ([M,N]: m={},n={}) ([N,M]: n={},m={})", i / n, i % n, i / m, i % m);
|
|
}
|
|
}
|
|
#[cfg(not(target_os = "macos"))]
|
|
fn main() {}
|