#[cfg(gemmology_simd)]
mod imp {
use std::os::raw::{c_char, c_void};
extern "C" {
fn gemmology_prepare_b(b_transposed: *const i8, n: usize, k: usize) -> *mut c_void;
fn gemmology_free_b(handle: *mut c_void);
fn gemmology_multiply(
handle: *mut c_void,
a: *const u8,
m: usize,
unquant: f32,
bias: *const f32,
out: *mut f32,
);
fn gemmology_prepared_bytes() -> usize;
fn gemmology_read_row(handle: *const c_void, id: usize, out: *mut i8);
fn gemmology_backend_name() -> *const c_char;
}
pub fn prepared_bytes() -> usize {
unsafe { gemmology_prepared_bytes() }
}
pub fn backend() -> &'static str {
let name = unsafe { std::ffi::CStr::from_ptr(gemmology_backend_name()) };
name.to_str().unwrap_or("unknown")
}
pub struct PreparedB {
handle: *mut c_void,
n: usize,
k: usize,
}
impl PreparedB {
pub fn new(b_transposed: &[i8], n: usize, k: usize) -> Option<PreparedB> {
assert_eq!(b_transposed.len(), n * k, "B length must be n * k");
let handle = unsafe { gemmology_prepare_b(b_transposed.as_ptr(), n, k) };
if handle.is_null() {
None
} else {
Some(PreparedB { handle, n, k })
}
}
pub fn matmul(&self, a: &[u8], m: usize, unquant: f32, bias: &[f32]) -> Vec<f32> {
let mut out = Vec::new();
self.matmul_into(a, m, unquant, bias, &mut out);
out
}
pub fn matmul_into(
&self,
a: &[u8],
m: usize,
unquant: f32,
bias: &[f32],
out: &mut Vec<f32>,
) {
assert_eq!(a.len(), m * self.k, "A length must be m * k");
assert_eq!(bias.len(), self.n, "bias length must be n");
out.clear();
out.resize(m * self.n, 0.0);
unsafe {
gemmology_multiply(
self.handle,
a.as_ptr(),
m,
unquant,
bias.as_ptr(),
out.as_mut_ptr(),
);
}
}
pub fn read_row(&self, id: usize, out: &mut [i8]) {
assert_eq!(out.len(), self.k, "out length must be k");
assert!(id < self.n, "row id {id} out of range (n = {})", self.n);
unsafe { gemmology_read_row(self.handle, id, out.as_mut_ptr()) };
}
}
impl Drop for PreparedB {
fn drop(&mut self) {
unsafe { gemmology_free_b(self.handle) };
}
}
}
#[cfg(not(gemmology_simd))]
mod imp {
pub fn prepared_bytes() -> usize {
0
}
pub fn backend() -> &'static str {
"scalar"
}
pub struct PreparedB {
_never: (),
}
impl PreparedB {
pub fn new(b_transposed: &[i8], n: usize, k: usize) -> Option<PreparedB> {
debug_assert_eq!(b_transposed.len(), n * k, "B length must be n * k");
None
}
pub fn matmul(&self, _a: &[u8], _m: usize, _unquant: f32, _bias: &[f32]) -> Vec<f32> {
unreachable!("scalar-fallback PreparedB is never constructed")
}
pub fn matmul_into(
&self,
_a: &[u8],
_m: usize,
_unquant: f32,
_bias: &[f32],
_out: &mut Vec<f32>,
) {
unreachable!("scalar-fallback PreparedB is never constructed")
}
pub fn read_row(&self, _id: usize, _out: &mut [i8]) {
unreachable!("scalar-fallback PreparedB is never constructed")
}
}
}
pub use imp::{backend, prepared_bytes, PreparedB};