use std::sync::OnceLock;
use tract_data::internal::*;
use crate::WeightType;
use crate::frame::mmm::{
EagerPackedInput, MMMInputFormat, MMMInputValue, PackedExoticFact, PackedMatrixStorage,
};
#[derive(Clone, Copy, Debug, Default)]
pub struct CacheSizes {
pub l1d_bytes: usize,
pub l2_bytes: usize,
pub l3_bytes: usize,
}
pub fn cache_sizes() -> CacheSizes {
static CACHE: OnceLock<CacheSizes> = OnceLock::new();
*CACHE.get_or_init(|| {
let mut out = CacheSizes::default();
for sub in 0..16 {
#[allow(unused_unsafe)]
let r = unsafe { std::arch::x86_64::__cpuid_count(4, sub) };
let cache_type = r.eax & 0x1F;
if cache_type == 0 {
break;
}
let level = (r.eax >> 5) & 0x7;
let ways = ((r.ebx >> 22) & 0x3FF) + 1;
let partitions = ((r.ebx >> 12) & 0x3FF) + 1;
let line_size = (r.ebx & 0xFFF) + 1;
let sets = r.ecx + 1;
let bytes =
(ways as usize) * (partitions as usize) * (line_size as usize) * (sets as usize);
match (level, cache_type) {
(1, 1) => out.l1d_bytes = bytes,
(2, 1 | 3) => out.l2_bytes = bytes,
(3, 1 | 3) => out.l3_bytes = bytes,
_ => {}
}
}
out
})
}
fn cpu_has_amx_int8() -> bool {
if !std::is_x86_feature_detected!("avx512f") {
return false;
}
#[allow(unused_unsafe)]
let r = unsafe { std::arch::x86_64::__cpuid_count(7, 0) };
const AMX_TILE: u32 = 1 << 24;
const AMX_INT8: u32 = 1 << 25;
(r.edx & AMX_TILE) != 0 && (r.edx & AMX_INT8) != 0
}
#[cfg(target_os = "linux")]
unsafe fn request_amx_xcomp_perm() -> bool {
let rc: i64;
unsafe {
std::arch::asm!(
"syscall",
in("rax") 158i64,
in("rdi") 0x1023i64,
in("rsi") 18i64,
lateout("rax") rc,
out("rcx") _,
out("r11") _,
options(nostack),
);
}
rc == 0
}
pub fn request_amx_tile_xcomp_perm() -> bool {
static GATE: OnceLock<bool> = OnceLock::new();
*GATE.get_or_init(|| {
#[cfg(target_os = "linux")]
{
unsafe { request_amx_xcomp_perm() }
}
#[cfg(not(target_os = "linux"))]
{
false
}
})
}
pub fn has_amx_int8() -> bool {
static GATE: OnceLock<bool> = OnceLock::new();
*GATE.get_or_init(|| cpu_has_amx_int8() && request_amx_tile_xcomp_perm())
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct PackedAmxA {
pub r: usize,
pub align: usize,
}
impl PackedAmxA {
pub fn new(r: usize) -> Self {
PackedAmxA { r, align: 64 }
}
fn k_padded(&self, k: usize) -> usize {
k.div_ceil(64) * 64
}
fn panel(&self, k: usize) -> usize {
self.k_padded(k) * self.r
}
pub fn single_panel_len(&self, k: usize) -> usize {
self.panel(k)
}
pub fn len(&self, k: usize, mn: usize) -> usize {
mn.div_ceil(self.r) * self.panel(k)
}
pub fn alignment(&self) -> usize {
self.align
}
pub fn pack_view(
&self,
t: &TensorView,
k_axis: usize,
mn_axis: usize,
) -> TractResult<Box<dyn MMMInputValue>> {
let k = t.shape()[k_axis];
let mn = t.shape()[mn_axis];
let kp = self.k_padded(k);
let pl = kp * self.r;
let panels = mn.div_ceil(self.r);
let st = t.strides();
let (ks, ms) = (st[k_axis], st[mn_axis]);
let mut blob = unsafe { Blob::new_for_size_and_align(panels * pl, self.align) };
blob.as_bytes_mut().fill(0);
unsafe {
let src = t.as_ptr_unchecked::<i8>();
let dst = blob.as_mut_ptr() as *mut i8;
for p in 0..panels {
let pw = self.r.min(mn - p * self.r);
let panel = dst.add(p * pl);
let mn0 = (p * self.r) as isize;
for lm in 0..pw {
let drow = panel.add(lm * kp);
let srow_base = src.offset((mn0 + lm as isize) * ms);
for kk in 0..k {
*drow.add(kk) = *srow_base.offset(kk as isize * ks);
}
}
}
}
Ok(Box::new(EagerPackedInput {
fact: PackedExoticFact { format: Box::new(self.clone()), mn: mn.to_dim(), k },
packed: blob.into(),
panel_bytes: pl,
mn,
}))
}
}
impl std::fmt::Display for PackedAmxA {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "AmxA[{}]", self.r)
}
}
impl MMMInputFormat for PackedAmxA {
fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult<Tensor> {
Ok(PackedMatrixStorage::new(self.prepare_one(t, k_axis, mn_axis)?)
.into_tensor(t.datum_type()))
}
fn prepare_one(
&self,
t: &Tensor,
k_axis: usize,
mn_axis: usize,
) -> TractResult<Box<dyn MMMInputValue>> {
self.pack_view(&t.view(), k_axis, mn_axis)
}
fn precursor(&self) -> WeightType {
WeightType::Plain(i8::datum_type())
}
fn r(&self) -> usize {
self.r
}
fn k_alignment(&self) -> usize {
64
}
fn merge_with<'o, 'a: 'o, 'b: 'o>(
&'a self,
o: &'b dyn MMMInputFormat,
) -> Option<&'o dyn MMMInputFormat> {
o.downcast_ref::<PackedAmxA>().filter(|x| x.r == self.r).map(|_| self as _)
}
fn mem_size(&self, k: TDim, mn: TDim) -> TDim {
mn.divceil(self.r) * self.panel(k.to_usize().unwrap_or(0))
}
fn extract_at_mn_f16(&self, _: &EagerPackedInput, _: usize, _: &mut [f16]) -> TractResult<()> {
bail!("no f16 extract")
}
fn extract_at_mn_f32(&self, _: &EagerPackedInput, _: usize, _: &mut [f32]) -> TractResult<()> {
bail!("no f32 extract")
}
}