mod dispatch;
pub mod postings;
mod scalar;
#[cfg(any(test, feature = "test-seams"))]
#[doc(hidden)]
pub mod vector_fault;
#[cfg(target_arch = "x86_64")]
mod avx2;
#[cfg(target_arch = "aarch64")]
mod neon;
pub const MAX_DOT_I8_DIMENSION: usize = 65_536;
const _: [(); 1] = [(); (MAX_DOT_I8_DIMENSION * (i8::MIN as i16 * i8::MIN as i16) as usize
<= i32::MAX as usize) as usize];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum KernelArm {
Scalar,
Neon,
Avx2,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum InstructionTier {
Scalar,
NeonWiden,
NeonDotprod,
Avx2,
NeonI8mmReserved,
Sme2Reserved,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(not(any(test, feature = "benchmark-kernels")), allow(dead_code))]
enum KernelBackendTag {
Scalar,
#[cfg(target_arch = "aarch64")]
NeonWiden,
#[cfg(target_arch = "aarch64")]
NeonDotprodU4,
#[cfg(target_arch = "aarch64")]
NeonI8mm,
#[cfg(target_arch = "aarch64")]
NeonDotprodU2,
#[cfg(target_arch = "aarch64")]
NeonDotprodU6,
#[cfg(target_arch = "aarch64")]
NeonDotprodU8,
#[cfg(target_arch = "aarch64")]
NeonDotprodU4Prefetch,
#[cfg(target_arch = "x86_64")]
Avx2,
}
#[cfg(any(test, feature = "benchmark-kernels"))]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[doc(hidden)]
pub enum KernelBackendId {
Scalar,
NeonWiden,
NeonDotprodU4,
NeonI8mm,
NeonDotprodU2,
NeonDotprodU6,
NeonDotprodU8,
NeonDotprodU4Prefetch,
Avx2,
}
#[cfg(any(test, feature = "benchmark-kernels"))]
impl KernelBackendId {
const fn from_tag(tag: KernelBackendTag) -> Self {
match tag {
KernelBackendTag::Scalar => Self::Scalar,
#[cfg(target_arch = "aarch64")]
KernelBackendTag::NeonWiden => Self::NeonWiden,
#[cfg(target_arch = "aarch64")]
KernelBackendTag::NeonDotprodU4 => Self::NeonDotprodU4,
#[cfg(target_arch = "aarch64")]
KernelBackendTag::NeonI8mm => Self::NeonI8mm,
#[cfg(target_arch = "aarch64")]
KernelBackendTag::NeonDotprodU2 => Self::NeonDotprodU2,
#[cfg(target_arch = "aarch64")]
KernelBackendTag::NeonDotprodU6 => Self::NeonDotprodU6,
#[cfg(target_arch = "aarch64")]
KernelBackendTag::NeonDotprodU8 => Self::NeonDotprodU8,
#[cfg(target_arch = "aarch64")]
KernelBackendTag::NeonDotprodU4Prefetch => Self::NeonDotprodU4Prefetch,
#[cfg(target_arch = "x86_64")]
KernelBackendTag::Avx2 => Self::Avx2,
}
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Scalar => "scalar",
Self::NeonWiden => "neon-widen",
Self::NeonDotprodU4 => "neon-dotprod-u4",
Self::NeonI8mm => "neon-i8mm",
Self::NeonDotprodU2 => "neon-dotprod-u2",
Self::NeonDotprodU6 => "neon-dotprod-u6",
Self::NeonDotprodU8 => "neon-dotprod-u8",
Self::NeonDotprodU4Prefetch => "neon-dotprod-u4-prefetch",
Self::Avx2 => "avx2",
}
}
#[cfg(any(test, feature = "test-seams"))]
const fn arm(self) -> KernelArm {
match self {
Self::Scalar => KernelArm::Scalar,
Self::NeonWiden
| Self::NeonDotprodU4
| Self::NeonI8mm
| Self::NeonDotprodU2
| Self::NeonDotprodU6
| Self::NeonDotprodU8
| Self::NeonDotprodU4Prefetch => KernelArm::Neon,
Self::Avx2 => KernelArm::Avx2,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct KernelFeatures {
pub neon: bool,
pub dotprod: bool,
pub fp16: bool,
pub i8mm: bool,
pub sme2: bool,
pub avx2: bool,
pub popcnt: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum KernelInitError {
UnknownOverride {
value: String,
},
NonUnicodeOverride,
UnsupportedArm {
requested: KernelArm,
},
AlreadyInitialized {
selected: KernelArm,
requested: KernelArm,
},
}
impl std::fmt::Display for KernelInitError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnknownOverride { value } => write!(
formatter,
"unknown ZE_KERNEL value {value:?}; expected scalar, neon, or avx2"
),
Self::NonUnicodeOverride => formatter.write_str("ZE_KERNEL is not valid Unicode"),
Self::UnsupportedArm { requested } => {
write!(
formatter,
"forced kernel arm {requested:?} is unsupported on this CPU"
)
}
Self::AlreadyInitialized {
selected,
requested,
} => write!(
formatter,
"kernel dispatch already selected {selected:?}; cannot force {requested:?}"
),
}
}
}
impl std::error::Error for KernelInitError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct KnobSpace {
pub unroll: &'static [usize],
pub accumulators: &'static [usize],
pub rows_per_block: &'static [usize],
pub prefetch_dist: &'static [usize],
pub tier: &'static [InstructionTier],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BaselineKernelConfig {
pub unroll: usize,
pub accumulators: usize,
pub rows_per_block: usize,
pub prefetch_dist: usize,
}
pub const BASELINE_KERNEL_CONFIG: BaselineKernelConfig = BaselineKernelConfig {
unroll: 4,
accumulators: 4,
rows_per_block: 1,
prefetch_dist: 0,
};
const UNROLL_KNOBS: [usize; 4] = [2, 4, 6, 8];
const ACCUMULATOR_KNOBS: [usize; 4] = [2, 4, 6, 8];
const ROW_BLOCK_KNOBS: [usize; 5] = [1, 2, 4, 8, 16];
const PREFETCH_KNOBS: [usize; 5] = [0, 1, 2, 4, 8];
const TIER_KNOBS: [InstructionTier; 6] = [
InstructionTier::Scalar,
InstructionTier::NeonWiden,
InstructionTier::NeonDotprod,
InstructionTier::Avx2,
InstructionTier::NeonI8mmReserved,
InstructionTier::Sme2Reserved,
];
pub const KERNEL_KNOB_SPACE: KnobSpace = KnobSpace {
unroll: &UNROLL_KNOBS,
accumulators: &ACCUMULATOR_KNOBS,
rows_per_block: &ROW_BLOCK_KNOBS,
prefetch_dist: &PREFETCH_KNOBS,
tier: &TIER_KNOBS,
};
type DotI8Fn = fn(&[i8], &[i8]) -> i32;
type DotF32Fn = fn(&[f32], &[f32]) -> f32;
type DotF16Fn = fn(&[u16], &[u16]) -> f32;
type HammingU1Fn = fn(&[u8], &[u8]) -> u32;
type DotI8BatchFn = fn(&[i8], &[i8], usize, &mut [i32]);
type HammingU1BatchFn = fn(&[u8], &[u8], usize, &mut [u32]);
type DotPackedFn = fn(&[i8], &[u8]) -> i32;
type DotBit4PreparedFn = fn(&[i8], i32, &[u8]) -> i32;
type DotPackedBatchFn = fn(&[i8], &[u8], usize, &mut [i32]);
type ScoreBit4PreparedBatchFn =
fn(&[i8], i32, f64, &[u8], usize, &[crate::quant::Bit4Factors], &mut [f32]);
type ScoreBit4PtrsFn = unsafe fn(
&[i8],
i32,
f64,
&[*const u8; 4],
usize,
&[crate::quant::Bit4Factors; 4],
&mut [f32; 4],
);
#[derive(Clone, Copy)]
struct KernelTable {
#[cfg_attr(not(any(test, feature = "benchmark-kernels")), allow(dead_code))]
backend: KernelBackendTag,
arm: KernelArm,
tier: InstructionTier,
dot_i8: DotI8Fn,
dot_f32: DotF32Fn,
dot_f16: DotF16Fn,
hamming_u1: HammingU1Fn,
dot_i8_batch: DotI8BatchFn,
hamming_u1_batch: HammingU1BatchFn,
dot_bit4: DotPackedFn,
dot_bit4_prepared: DotBit4PreparedFn,
dot_bit4_batch: DotPackedBatchFn,
score_bit4_prepared_batch: ScoreBit4PreparedBatchFn,
score_bit4_ptrs: ScoreBit4PtrsFn,
}
#[derive(Clone, Copy)]
pub struct Bit4Row<'a> {
bytes: &'a [u8],
}
impl std::fmt::Debug for Bit4Row<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Bit4Row")
.field("len", &self.bytes.len())
.finish()
}
}
impl<'a> Bit4Row<'a> {
#[must_use]
pub fn from_mapped_region(region: &'a [u8], offset: usize, row_bytes: usize) -> Option<Self> {
let end = offset.checked_add(row_bytes)?;
region.get(offset..end).map(|bytes| Self { bytes })
}
#[must_use]
pub(crate) const fn from_validated_bytes(bytes: &'a [u8]) -> Self {
Self { bytes }
}
const fn as_ptr(self) -> *const u8 {
self.bytes.as_ptr()
}
const fn len(self) -> usize {
self.bytes.len()
}
}
#[derive(Clone, Copy)]
pub struct Bit4Rows4<'a> {
pointers: [*const u8; 4],
row_bytes: usize,
lifetime: std::marker::PhantomData<&'a [u8]>,
}
impl std::fmt::Debug for Bit4Rows4<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Bit4Rows4")
.field("row_bytes", &self.row_bytes)
.finish()
}
}
impl<'a> Bit4Rows4<'a> {
pub fn from_rows(rows: [Bit4Row<'a>; 4], row_bytes: usize) -> Result<Self, GatherShapeError> {
for (index, row) in rows.iter().enumerate() {
if row.len() != row_bytes {
return Err(GatherShapeError::RowLength {
index,
expected: row_bytes,
actual: row.len(),
});
}
}
Ok(Self {
pointers: rows.map(Bit4Row::as_ptr),
row_bytes,
lifetime: std::marker::PhantomData,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GatherShapeError {
DimensionTooLarge {
actual: usize,
maximum: usize,
},
RowBytes {
expected: usize,
actual: usize,
},
RowLength {
index: usize,
expected: usize,
actual: usize,
},
}
impl std::fmt::Display for GatherShapeError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DimensionTooLarge { actual, maximum } => write!(
formatter,
"gather dimension {actual} exceeds supported maximum {maximum}"
),
Self::RowBytes { expected, actual } => write!(
formatter,
"gather row width mismatch: expected {expected}, got {actual}"
),
Self::RowLength {
index,
expected,
actual,
} => write!(
formatter,
"gather row {index} has {actual} readable bytes, expected {expected}"
),
}
}
}
impl std::error::Error for GatherShapeError {}
pub fn prefetch_bit4_rows(rows: &[Bit4Row<'_>]) {
#[cfg(target_arch = "aarch64")]
for row in rows.iter().filter(|row| row.len() != 0) {
let address = row.as_ptr();
unsafe {
std::arch::asm!(
"prfm pldl1keep, [{address}]",
address = in(reg) address,
options(readonly, nostack)
);
}
}
#[cfg(not(target_arch = "aarch64"))]
let _ = rows;
}
pub fn prefetch_bit4_row_group(rows: &Bit4Rows4<'_>) {
#[cfg(target_arch = "aarch64")]
if rows.row_bytes != 0 {
for &address in &rows.pointers {
unsafe {
std::arch::asm!(
"prfm pldl1keep, [{address}]",
address = in(reg) address,
options(readonly, nostack)
);
}
}
}
#[cfg(not(target_arch = "aarch64"))]
let _ = rows;
}
#[derive(Clone, Copy)]
pub struct KernelVariant {
table: KernelTable,
}
impl std::fmt::Debug for KernelVariant {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("KernelVariant")
.field("arm", &self.table.arm)
.field("tier", &self.table.tier)
.finish()
}
}
impl KernelVariant {
#[must_use]
pub fn scalar() -> Self {
Self {
table: scalar::table(),
}
}
#[doc(hidden)]
#[must_use]
pub fn selected() -> Self {
Self {
table: *dispatch::active_table(),
}
}
#[cfg(any(test, feature = "benchmark-kernels"))]
pub fn available() -> impl Iterator<Item = Self> {
dispatch::variant_tables()
.into_iter()
.flatten()
.map(|table| Self { table })
}
#[must_use]
pub fn arm(self) -> KernelArm {
self.table.arm
}
#[must_use]
pub fn tier(self) -> InstructionTier {
self.table.tier
}
#[cfg(any(test, feature = "benchmark-kernels"))]
#[doc(hidden)]
#[must_use]
pub const fn backend_id(self) -> KernelBackendId {
KernelBackendId::from_tag(self.table.backend)
}
#[must_use]
pub fn dot_i8(self, a: &[i8], b: &[i8]) -> i32 {
(self.table.dot_i8)(a, b)
}
#[must_use]
pub fn dot_f32(self, a: &[f32], b: &[f32]) -> f32 {
(self.table.dot_f32)(a, b)
}
#[must_use]
pub fn dot_f16(self, a: &[u16], b: &[u16]) -> f32 {
(self.table.dot_f16)(a, b)
}
#[must_use]
pub fn hamming_u1(self, a: &[u8], b: &[u8]) -> u32 {
(self.table.hamming_u1)(a, b)
}
pub fn dot_i8_batch(self, q: &[i8], rows: &[i8], d: usize, out: &mut [i32]) {
(self.table.dot_i8_batch)(q, rows, d, out);
}
pub fn hamming_u1_batch(self, q: &[u8], rows: &[u8], d_bytes: usize, out: &mut [u32]) {
(self.table.hamming_u1_batch)(q, rows, d_bytes, out);
}
#[must_use]
pub fn dot_bit4(self, q: &[i8], codes: &[u8]) -> i32 {
(self.table.dot_bit4)(q, codes)
}
#[doc(hidden)]
#[must_use]
pub fn dot_bit4_prepared(self, q: &[i8], query_sum: i32, codes: &[u8]) -> i32 {
(self.table.dot_bit4_prepared)(q, query_sum, codes)
}
pub fn dot_bit4_batch(self, q: &[i8], rows: &[u8], d: usize, out: &mut [i32]) {
(self.table.dot_bit4_batch)(q, rows, d, out);
}
#[doc(hidden)]
pub fn score_bit4_prepared_batch(
self,
query: (&[i8], i32, f64),
rows: &[u8],
d: usize,
factors: &[crate::quant::Bit4Factors],
out: &mut [f32],
) {
let (q, query_sum, query_scale_half) = query;
(self.table.score_bit4_prepared_batch)(
q,
query_sum,
query_scale_half,
rows,
d,
factors,
out,
);
}
pub fn score_bit4_ptrs(
&self,
query: (&[i8], i32, f64),
rows: &Bit4Rows4<'_>,
factors: &[crate::quant::Bit4Factors; 4],
out: &mut [f32; 4],
) -> Result<(), GatherShapeError> {
score_bit4_ptrs_with_table(&self.table, query, rows, factors, out)
}
}
fn score_bit4_ptrs_with_table(
table: &KernelTable,
query: (&[i8], i32, f64),
rows: &Bit4Rows4<'_>,
factors: &[crate::quant::Bit4Factors; 4],
out: &mut [f32; 4],
) -> Result<(), GatherShapeError> {
let (q, query_sum, query_scale_half) = query;
if q.len() > MAX_DOT_I8_DIMENSION {
return Err(GatherShapeError::DimensionTooLarge {
actual: q.len(),
maximum: MAX_DOT_I8_DIMENSION,
});
}
let expected_row_bytes = q.len().div_ceil(2);
if rows.row_bytes != expected_row_bytes {
return Err(GatherShapeError::RowBytes {
expected: expected_row_bytes,
actual: rows.row_bytes,
});
}
debug_assert!(q.len() <= MAX_DOT_I8_DIMENSION);
debug_assert_eq!(rows.row_bytes, q.len().div_ceil(2));
unsafe {
(table.score_bit4_ptrs)(
q,
query_sum,
query_scale_half,
&rows.pointers,
rows.row_bytes,
factors,
out,
);
}
Ok(())
}
pub fn initialize() -> Result<KernelArm, KernelInitError> {
dispatch::initialize()
}
#[must_use]
pub fn selected_arm() -> KernelArm {
dispatch::active_table().arm
}
#[must_use]
pub fn is_arm_supported(arm: KernelArm) -> bool {
dispatch::table_for_arm(arm).is_some()
}
#[must_use]
pub fn detected_features() -> KernelFeatures {
dispatch::features()
}
#[doc(hidden)]
#[must_use]
pub fn platform_wide_stream_checksum(bytes: &[u8]) -> Option<u64> {
#[cfg(target_arch = "aarch64")]
{
dispatch::features()
.neon
.then(|| neon::wide_stream_checksum(bytes))
}
#[cfg(not(target_arch = "aarch64"))]
{
let _ = bytes;
None
}
}
#[must_use]
pub fn dot_i8(a: &[i8], b: &[i8]) -> i32 {
let table = dispatch::active_table();
let result = (table.dot_i8)(a, b);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(table, vector_fault_kernel_id_dot_i8(), a.len());
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::I32(result));
result
}
#[must_use]
pub fn dot_f32(a: &[f32], b: &[f32]) -> f32 {
let table = dispatch::active_table();
let result = (table.dot_f32)(a, b);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(table, vector_fault_kernel_id_dot_f32(), a.len());
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::F32(result.to_bits()));
result
}
#[must_use]
pub fn dot_f16(a: &[u16], b: &[u16]) -> f32 {
let table = dispatch::active_table();
let result = (table.dot_f16)(a, b);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(table, vector_fault_kernel_id_dot_f16(), a.len());
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::F32(result.to_bits()));
result
}
#[must_use]
pub fn hamming_u1(a: &[u8], b: &[u8]) -> u32 {
let table = dispatch::active_table();
let result = (table.hamming_u1)(a, b);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(table, vector_fault_kernel_id_hamming(), a.len());
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::U32(result));
result
}
pub fn dot_i8_batch(q: &[i8], rows: &[i8], d: usize, out: &mut [i32]) {
let table = dispatch::active_table();
(table.dot_i8_batch)(q, rows, d, out);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(
table,
vector_fault_kernel_id_dot_i8_batch(),
d.saturating_mul(out.len()),
);
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::I32s(out.to_vec()));
}
pub fn hamming_u1_batch(q: &[u8], rows: &[u8], d_bytes: usize, out: &mut [u32]) {
let table = dispatch::active_table();
(table.hamming_u1_batch)(q, rows, d_bytes, out);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(
table,
vector_fault_kernel_id_hamming_batch(),
d_bytes.saturating_mul(out.len()),
);
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::U32s(out.to_vec()));
}
#[must_use]
pub fn dot_bit4(q: &[i8], codes: &[u8]) -> i32 {
let table = dispatch::active_table();
let result = (table.dot_bit4)(q, codes);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(table, vector_fault_kernel_id_dot_bit4(), q.len());
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::I32(result));
result
}
pub(crate) fn dot_bit4_prepared(q: &[i8], query_sum: i32, codes: &[u8]) -> i32 {
let table = dispatch::active_table();
let result = (table.dot_bit4_prepared)(q, query_sum, codes);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(table, vector_fault_kernel_id_dot_bit4_prepared(), q.len());
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::I32(result));
result
}
pub(crate) fn score_bit4_prepared_batch(
q: &[i8],
query_sum: i32,
query_scale_half: f64,
rows: &[u8],
d: usize,
factors: &[crate::quant::Bit4Factors],
out: &mut [f32],
) {
let table = dispatch::active_table();
(table.score_bit4_prepared_batch)(q, query_sum, query_scale_half, rows, d, factors, out);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(
table,
vector_fault_kernel_id_score_bit4_batch(),
d.saturating_mul(out.len()),
);
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::F32s(
out.iter().map(|value| value.to_bits()).collect(),
));
}
#[cfg(any(test, feature = "test-seams"))]
fn observe_selected_score(
table: &KernelTable,
kernel: vector_fault::KernelOperationId,
work_items: usize,
) {
vector_fault::observe_selected_score(table.backend, kernel, work_items);
}
macro_rules! vector_fault_kernel_id {
($name:ident, $variant:ident) => {
#[cfg(any(test, feature = "test-seams"))]
const fn $name() -> vector_fault::KernelOperationId {
vector_fault::KernelOperationId::$variant
}
};
}
vector_fault_kernel_id!(vector_fault_kernel_id_dot_i8, DotI8);
vector_fault_kernel_id!(vector_fault_kernel_id_hamming, HammingU1);
vector_fault_kernel_id!(vector_fault_kernel_id_dot_f32, DotF32);
vector_fault_kernel_id!(vector_fault_kernel_id_dot_f16, DotF16);
vector_fault_kernel_id!(vector_fault_kernel_id_dot_i8_batch, DotI8Batch);
vector_fault_kernel_id!(vector_fault_kernel_id_hamming_batch, HammingU1Batch);
vector_fault_kernel_id!(vector_fault_kernel_id_dot_bit4, DotBit4);
vector_fault_kernel_id!(vector_fault_kernel_id_dot_bit4_prepared, DotBit4Prepared);
vector_fault_kernel_id!(vector_fault_kernel_id_dot_bit4_batch, DotBit4Batch);
vector_fault_kernel_id!(
vector_fault_kernel_id_score_bit4_batch,
ScoreBit4PreparedBatch
);
vector_fault_kernel_id!(vector_fault_kernel_id_score_bit4_ptrs, ScoreBit4Ptrs);
pub fn score_bit4_ptrs(
q: &[i8],
query_sum: i32,
query_scale_half: f64,
rows: &Bit4Rows4<'_>,
factors: &[crate::quant::Bit4Factors; 4],
out: &mut [f32; 4],
) -> Result<(), GatherShapeError> {
let table = dispatch::active_table();
let result =
score_bit4_ptrs_with_table(table, (q, query_sum, query_scale_half), rows, factors, out);
#[cfg(any(test, feature = "test-seams"))]
if result.is_ok() {
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(
table,
vector_fault_kernel_id_score_bit4_ptrs(),
q.len().saturating_mul(out.len()),
);
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::F32s(
out.iter().map(|value| value.to_bits()).collect(),
));
}
result
}
pub fn dot_bit4_batch(q: &[i8], rows: &[u8], d: usize, out: &mut [i32]) {
let table = dispatch::active_table();
(table.dot_bit4_batch)(q, rows, d, out);
#[cfg(any(test, feature = "test-seams"))]
observe_selected_score(
table,
vector_fault_kernel_id_dot_bit4_batch(),
d.saturating_mul(out.len()),
);
#[cfg(any(test, feature = "test-seams"))]
vector_fault::observe_selected_result(vector_fault::KernelScoreValue::I32s(out.to_vec()));
}