Add native self-hosted instance connection to fluxer_desktop

Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 +
tools/ci) with a "Connect to a Different Server" menu item and popout
that lets the desktop app switch to any self-hosted Fluxer instance,
plus fixes for well-known discovery on single-domain self-hosted
deployments and a false-positive ERR_ABORTED on same-origin client
redirects during the switch. Defaults to chat.fluxr.chat and uses an
isolated userData directory from the official build.
This commit is contained in:
2026-07-01 18:22:43 -04:00
commit 682afacd30
1763 changed files with 613720 additions and 0 deletions
@@ -0,0 +1,191 @@
#![feature(asm)]
#![allow(
dead_code,
non_upper_case_globals,
unused_macros,
non_snake_case,
unused_assignments
)]
use std::time::Instant;
macro_rules! r2 { ($($stat:stmt)*) => { $( $stat )* $( $stat )* } }
macro_rules! r4 { ($($stat:stmt)*) => { r2!(r2!($($stat)*)) }}
macro_rules! r8 { ($($stat:stmt)*) => { r4!(r2!($($stat)*)) }}
macro_rules! r16 { ($($stat:stmt)*) => { r4!(r4!($($stat)*)) }}
macro_rules! r32 { ($($stat:stmt)*) => { r8!(r4!($($stat)*)) }}
macro_rules! r64 { ($($stat:stmt)*) => { r8!(r8!($($stat)*)) }}
macro_rules! r128 { ($($stat:stmt)*) => { r8!(r16!($($stat)*)) }}
macro_rules! r1024 { ($($stat:stmt)*) => { r8!(r128!($($stat)*)) }}
macro_rules! r4096 { ($($stat:stmt)*) => { r4!(r1024!($($stat)*)) }}
const _F32: [f32; 1024] = [12.; 1024];
const F32: *const f32 = _F32.as_ptr();
/*
fn ruin_cache() {
let _a = (0..1000000).collect::<Vec<i32>>();
}
*/
macro_rules! b {
($f: block, $inner_loop: expr, $measures: expr) => {{
let mut values = Vec::with_capacity($measures);
for _ in 0..$measures {
// ruin_cache();
let start = Instant::now();
for _ in 0..$inner_loop {
unsafe { $f };
}
values.push(start.elapsed());
}
values.sort();
values[$measures / 2].as_nanos() as f64 / 1e9 / $inner_loop as f64
}};
}
fn main() {
let cycle = b!(
{
r1024!(asm!("orr r0, r0, r0", out("r0") _));
},
1000,
1000
) / 1024.;
let indep_fmla = b!(
{
r8!(asm!("
vmla.f32 q0, q0, q0
vmla.f32 q1, q1, q1
vmla.f32 q2, q2, q2
vmla.f32 q3, q3, q3
vmla.f32 q4, q4, q4
vmla.f32 q5, q5, q5
vmla.f32 q6, q6, q6
vmla.f32 q7, q7, q7
", out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _));
},
1000,
1000
) / 64.;
eprintln!("rcp tp: indep fmla: {}", indep_fmla / cycle);
let dep_accu_fmla = b!(
{
r16!(asm!("
vmla.f32 q15, q0, q0
vmla.f32 q15, q1, q1
vmla.f32 q15, q2, q2
vmla.f32 q15, q3, q3
vmla.f32 q15, q4, q4
vmla.f32 q15, q5, q5
vmla.f32 q15, q6, q6
vmla.f32 q15, q7, q7
vmla.f32 q15, q8, q8
vmla.f32 q15, q9, q9
vmla.f32 q15, q10, q10
vmla.f32 q15, q11, q11
vmla.f32 q15, q12, q12
vmla.f32 q15, q13, q13
vmla.f32 q15, q14, q14
", out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _,
out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _));
},
1000,
1000
) / 16.
/ 15.;
eprintln!("rcp tp: accu-dep fmla: {}", dep_accu_fmla / cycle);
let load_s_using_vld1_64 = b!(
{
let mut p = F32;
r16!(asm!("
vld1.64 {{d0-d3}}, [{0}]!
vld1.64 {{d4-d7}}, [{0}]!
vld1.64 {{d8-d11}}, [{0}]!
vld1.64 {{d12-d15}}, [{0}]!
vld1.64 {{d16-d19}}, [{0}]!
vld1.64 {{d20-d23}}, [{0}]!
vld1.64 {{d24-d27}}, [{0}]!
vld1.64 {{d28-d31}}, [{0}]!
",
inout(reg) p,
out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _,
out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _));
},
1000,
1000
) / 16.
/ 64.; // each line load 8 s
eprintln!(
"rcp tp: load s using vld1_64 ia {}",
load_s_using_vld1_64 / cycle
);
let load_s_using_vldm_q = b!(
{
let mut p = F32;
r16!(asm!("
vldm {0}!, {{q0-q3}}
vldm {0}!, {{q4-q7}}
vldm {0}!, {{q8-q11}}
vldm {0}!, {{q12-q15}}
",
inout(reg) p,
out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _,
out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _));
},
1000,
1000
) / 16.
/ 64.;
eprintln!(
"rcp tp: load s using vldmia q: {}",
load_s_using_vldm_q / cycle
);
let load = b!(
{
let mut p = F32;
r16!(asm!("
vldr.64 d0, [{0}]
vldr.64 d1, [{0}, #8]
vldr.64 d2, [{0}, #16]
vldr.64 d3, [{0}, #24]
vldr.64 d4, [{0}, #32]
vldr.64 d5, [{0}, #40]
vldr.64 d6, [{0}, #48]
vldr.64 d7, [{0}, #56]
vldr.64 d8, [{0}, #64]
vldr.64 d9, [{0}, #72]
vldr.64 d10, [{0}, #80]
vldr.64 d11, [{0}, #88]
vldr.64 d12, [{0}, #96]
vldr.64 d13, [{0}, #104]
vldr.64 d14, [{0}, #112]
vldr.64 d15, [{0}, #120]
vldr.64 d16, [{0}, #128]
vldr.64 d17, [{0}, #136]
vldr.64 d18, [{0}, #144]
vldr.64 d19, [{0}, #152]
vldr.64 d20, [{0}, #160]
vldr.64 d21, [{0}, #168]
vldr.64 d22, [{0}, #176]
vldr.64 d23, [{0}, #184]
vldr.64 d24, [{0}, #192]
vldr.64 d25, [{0}, #200]
vldr.64 d26, [{0}, #208]
vldr.64 d27, [{0}, #216]
vldr.64 d28, [{0}, #224]
vldr.64 d29, [{0}, #232]
vldr.64 d30, [{0}, #240]
vldr.64 d31, [{0}, #248]
add {0}, #256
",
inout(reg) p,
out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _,
out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _));
},
1000,
1000
) / 16.
/ 64.;
eprintln!("rcp tp: load s using vldr d + imm: {}", load / cycle);
}
@@ -0,0 +1,88 @@
use std::time::Instant;
use tract_data::prelude::*;
use tract_linalg::frame::mmm::LinearSpec;
use tract_linalg::frame::mmm::MatMatMulKer;
use tract_linalg::frame::mmm::MatMatMulKerSpec;
use tract_linalg::mmm::{InputStoreKer, OutputStoreKer};
fn ruin_cache() {
let _a = (0..1000000).collect::<Vec<i32>>();
}
fn bench_to_nanos<T: Datum + Copy + num_traits::Zero, K: MatMatMulKer<T>>(
k: usize,
loops: usize,
) -> f64 {
let item_size = T::datum_type().size_of();
let a = Tensor::zero_aligned::<T>(
&[(k + K::end_padding_packed_a()) * K::mr()],
K::alignment_bytes_packed_a(),
)
.unwrap();
let b = Tensor::zero_aligned::<T>(
&[(k + K::end_padding_packed_b()) * K::nr()],
K::alignment_bytes_packed_b(),
)
.unwrap();
let mut c = Tensor::zero::<T>(&[K::mr() * K::nr()]).unwrap();
let ref a = InputStoreKer::Packed {
ptr: unsafe { a.as_ptr_unchecked::<u8>() as _ },
};
let ref b = InputStoreKer::Packed {
ptr: unsafe { b.as_ptr_unchecked::<u8>() as _ },
};
let ref c = OutputStoreKer {
ptr: unsafe { c.as_ptr_mut_unchecked::<u8>() as _ },
item_size,
col_byte_stride: (item_size * K::mr()) as isize,
row_byte_stride: item_size as isize,
};
let ref linear = LinearSpec::Mul { k };
let op = MatMatMulKerSpec {
a,
b,
c,
linear,
non_linear: std::ptr::null(),
};
let mut values = Vec::with_capacity(loops);
for _ in 0..loops {
ruin_cache();
let start = Instant::now();
K::kernel(&op);
values.push(start.elapsed());
}
values.sort();
values[loops / 2].as_nanos() as f64
}
fn model<T: Datum + Copy + num_traits::Zero, K: MatMatMulKer<T>>() -> (f64, f64) {
let x = 1000;
let zp = bench_to_nanos::<T, K>(0, 10000);
let y = bench_to_nanos::<T, K>(x, 1000);
let slope = (y - zp) / x as f64;
(slope, zp)
}
fn as_match_line<T: Datum + Copy + num_traits::Zero, K: MatMatMulKer<T>>() {
let coeffs = model::<T, K>();
println!(
"({:?}, {}, {}) => {} * k + {},",
K::name(),
K::mr(),
K::nr(),
(coeffs.0 * 1000.).round(),
(coeffs.1 * 1000.).round()
);
}
fn main() {
use tract_linalg::arm64::*;
as_match_line::<f32, MatMatMulF32x16x4>();
as_match_line::<f32, MatMatMulF32x12x8>();
as_match_line::<f32, MatMatMulF32x8x8>();
as_match_line::<f32, MatMatMulF32x16x4A53>();
as_match_line::<f32, MatMatMulF32x12x8A53>();
as_match_line::<f32, MatMatMulF32x8x8A53>();
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
#[macro_use]
extern crate criterion;
extern crate tract_data;
extern crate tract_linalg;
use criterion::Criterion;
use tract_linalg::frame::mmm::FusedSpec;
use tract_data::internal::*;
use DatumType::F32;
fn conv(c: &mut Criterion, dilation: usize, pulse: usize, ci: usize, co: usize) {
c.bench_function(
&format!("conv_d{dilation}p{pulse}ci{ci}co{co}"),
move |be| unsafe {
let t = pulse + 2 * dilation;
let k = ci * 3;
let mm = tract_linalg::ops()
.mmm(F32, F32, F32, Some(co), Some(ci * 3), Some(pulse))
.unwrap();
mm.c_from_data_and_strides(F32.size_of(), co, t, t as _, 1);
let a = Tensor::zero_aligned::<f32>(&[mm.a_pack().len(k, co)], mm.a_pack().alignment())
.unwrap();
let input = Tensor::zero::<f32>(&[ci, t]).unwrap();
let mut output = Tensor::zero::<f32>(&[co, t]).unwrap();
be.iter(move || {
mm.run(
co,
t,
&[
FusedSpec::AddMatMul {
a: mm.a_packed(F32.size_of(), k).wrap(&a.view()),
b: mm.b_packed(F32.size_of(), k).wrap(&input.view()).unwrap(),
k,
},
FusedSpec::Store(mm.c_view(0, 1).wrap(&output.view_mut())),
],
)
.unwrap()
});
},
);
}
fn convs(c: &mut Criterion) {
conv(c, 1, 8, 16, 64);
conv(c, 2, 8, 16, 64);
conv(c, 4, 8, 16, 64);
conv(c, 8, 8, 16, 64);
}
criterion_group!(benches, convs);
criterion_main!(benches);
@@ -0,0 +1,52 @@
use criterion::*;
use tract_data::internal::*;
use tract_linalg::frame::mmm::FusedSpec;
use DatumType::F32;
fn mat_vec_mul(c: &mut Criterion) {
let mut group = c.benchmark_group("mat_vec_mul");
unsafe {
{
let (m, k) = &(768usize, 256usize);
group.throughput(Throughput::Elements((m * k) as u64));
group.bench_with_input(
BenchmarkId::from_parameter(format!("{m}x{k}")),
&(m, k),
|be, (&m, &k)| {
let mm = tract_linalg::ops()
.mmm(F32, F32, F32, Some(m), Some(k), Some(1))
.unwrap();
let pa = Tensor::uninitialized_aligned::<f32>(
&[mm.a_pack().len(k, m)],
mm.a_pack().alignment(),
)
.unwrap();
let b = tensor1(&vec![0.0; k]);
let mut c = Tensor::zero::<f32>(&[m]).unwrap();
be.iter(move || {
mm.run(
m,
1,
&[
FusedSpec::AddMatMul {
a: mm.a_packed(F32.size_of(), k).wrap(&pa.view()),
b: mm
.b_packed(b.datum_type().size_of(), k)
.wrap(&b.view())
.unwrap(),
k,
},
FusedSpec::Store(mm.c_view(0, 0).wrap(&c.view_mut())),
],
)
});
},
);
}
}
group.finish();
}
criterion_group!(benches, mat_vec_mul);
criterion_main!(benches);
@@ -0,0 +1,37 @@
use criterion::*;
mod utils;
use utils::*;
fn all(c: &mut Criterion) {
// packed_packed: co, ci, n
// direct_conv(c, "asr_2M", 24, 5, 40, 200, 1); // lda
packed_packed(c, "asr_2M", 256, 200, 24); // tdnn1
// direct_conv(c, "asr_2M", 24, 3, 256, 256, 1); // tdnn2
// direct_conv(c, "asr_2M", 24, 3, 256, 256, 3); // tdnn3
packed_packed(c, "asr_2M", 256, 256, 8); // fastlstm1 and 2 (input) x 8 (4 prod x 2 layers)
packed_packed(c, "asr_2M", 256, 128, 1); // fastlstm1 and 2 (hidden) x 64 (4 prod x 2 layers x 8 loops)
packed_packed(c, "asr_2M", 256, 256, 1); // fastlstm1 and 2 (rp) x 16 (2 layers x 8 loops)
// direct_conv(c, "asr_2M", 8, 3, 256, 256, 1); // tdnn4, tdd5 (x2)
packed_packed(c, "asr_2M", 1690, 256, 8); // output
// 8M
packed_packed(c, "asr_8M", 512, 200, 24); // tdnn1
packed_packed(c, "asr_8M", 512, 512, 24); // tdnn2
packed_packed(c, "asr_8M", 512, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec)
packed_vec(c, "asr_8M", 512, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec)
// pseudo 15M
packed_packed(c, "asr_pseudo15M", 768, 200, 24); // tdnn1
packed_packed(c, "asr_pseudo15M", 768, 2304, 24); // tdnn2
packed_packed(c, "asr_pseudo15M", 768, 2304, 8); // tdnn3,4,5
packed_packed(c, "asr_pseudo15M", 768, 768, 8); // fastlstm1 and 2 (four parts, rec mat*mat)
packed_packed(c, "asr_pseudo15M", 768, 384, 1); // fastlstm1 and 2 (four parts, rec mat*vec)
packed_vec(c, "asr_pseudo15M", 768, 384, 1); // fastlstm1 and 2 (four parts, rec mat*vec)
// 15M
packed_vec(c, "asr_15M", 768, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec)
}
criterion_group!(benches, all);
criterion_main!(benches);
@@ -0,0 +1,47 @@
extern crate criterion;
use criterion::*;
use tract_data::internal::*;
use tract_linalg::frame::mmm::FusedSpec;
use DatumType::F32;
fn mat_mul_smmm(be: &mut criterion::Bencher, &(m, k, n): &(usize, usize, usize)) {
unsafe {
let mm = tract_linalg::ops()
.mmm(F32, F32, F32, Some(m), Some(k), Some(n))
.unwrap();
let pa =
Tensor::uninitialized_aligned::<f32>(&[mm.a_pack().len(k, m)], mm.a_pack().alignment())
.unwrap();
let pb =
Tensor::uninitialized_aligned::<f32>(&[mm.b_pack().len(k, n)], mm.b_pack().alignment())
.unwrap();
let mut c = Tensor::zero::<f32>(&[m, n]).unwrap();
be.iter(move || {
mm.run(
m,
n,
&[
FusedSpec::AddMatMul {
a: mm.a_packed(F32.size_of(), k).wrap(&pa.view()),
b: mm.b_packed(F32.size_of(), k).wrap(&pb.view()).unwrap(),
k,
},
FusedSpec::Store(mm.c_view(0, 1).wrap(&c.view_mut())),
],
)
});
}
}
fn mat_mul_prepacked(c: &mut Criterion, m: usize, k: usize, n: usize) {
let mut group = c.benchmark_group("mat_mul_prepacked");
group.bench_function("smmm", |be| mat_mul_smmm(be, &(m, k, n)));
}
fn s64x288x21609(c: &mut Criterion) {
mat_mul_prepacked(c, 64, 288, 21609)
}
criterion::criterion_group!(benches, s64x288x21609);
criterion::criterion_main!(benches);
@@ -0,0 +1,12 @@
use criterion::*;
mod utils;
use utils::*;
fn s16x60x8(c: &mut Criterion) {
packed_packed(c, "wavenet", 32, 32, 8); // postproc
packed_packed(c, "wavenet", 16, 60, 8);
}
criterion_group!(benches, s16x60x8);
criterion_main!(benches);
@@ -0,0 +1,113 @@
#![allow(unused_macros)]
use std::time::Duration;
use std::time::Instant;
#[macro_export]
macro_rules! r1 { ($($stat:stmt)*) => { $( $stat )* } }
#[macro_export]
macro_rules! r2 { ($($stat:stmt)*) => { $( $stat )* $( $stat )* } }
#[macro_export]
macro_rules! r4 { ($($stat:stmt)*) => { r2!(r2!($($stat)*)) }}
#[macro_export]
macro_rules! r8 { ($($stat:stmt)*) => { r2!(r4!($($stat)*)) }}
#[macro_export]
macro_rules! r16 { ($($stat:stmt)*) => { r2!(r8!($($stat)*)) }}
#[macro_export]
macro_rules! r32 { ($($stat:stmt)*) => { r2!(r16!($($stat)*)) }}
#[macro_export]
macro_rules! r64 { ($($stat:stmt)*) => { r2!(r32!($($stat)*)) }}
#[macro_export]
macro_rules! r128 { ($($stat:stmt)*) => { r2!(r64!($($stat)*)) }}
#[macro_export]
macro_rules! r256 { ($($stat:stmt)*) => { r2!(r128!($($stat)*)) }}
#[macro_export]
macro_rules! r512 { ($($stat:stmt)*) => { r2!(r256!($($stat)*)) }}
#[macro_export]
macro_rules! r1024 { ($($stat:stmt)*) => { r2!(r512!($($stat)*)) }}
#[macro_export]
macro_rules! r2048 { ($($stat:stmt)*) => { r2!(r1024!($($stat)*)) }}
#[macro_export]
macro_rules! r4096 { ($($stat:stmt)*) => { r2!(r2048!($($stat)*)) }}
#[macro_export]
macro_rules! r8192 { ($($stat:stmt)*) => { r2!(r4096!($($stat)*)) }}
#[macro_export]
macro_rules! b1 { ($($stat:stmt)*) => { nano::run_bench(|| { r1!($($stat)*); }) / 1.0 } }
#[macro_export]
macro_rules! b2 { ($($stat:stmt)*) => { nano::run_bench(|| { r2!($($stat)*); }) / 2.0 } }
#[macro_export]
macro_rules! b4 { ($($stat:stmt)*) => { nano::run_bench(|| { r4!($($stat)*); }) / 4.0 } }
#[macro_export]
macro_rules! b8 { ($($stat:stmt)*) => { nano::run_bench(|| { r8!($($stat)*); }) / 8.0 } }
#[macro_export]
macro_rules! b16 { ($($stat:stmt)*) => { nano::run_bench(|| { r16!($($stat)*); }) / 16.0 } }
#[macro_export]
macro_rules! b32 { ($($stat:stmt)*) => { nano::run_bench(|| { r32!($($stat)*); }) / 32.0 } }
#[macro_export]
macro_rules! b64 { ($($stat:stmt)*) => { nano::run_bench(|| { r64!($($stat)*); }) / 64.0 } }
#[macro_export]
macro_rules! b128 { ($($stat:stmt)*) => { nano::run_bench(|| { r128!($($stat)*); }) / 128.0 } }
#[macro_export]
macro_rules! b256 { ($($stat:stmt)*) => { nano::run_bench(|| { r256!($($stat)*); }) / 256.0 } }
#[macro_export]
macro_rules! b512 { ($($stat:stmt)*) => { nano::run_bench(|| { r512!($($stat)*); }) / 512.0 } }
#[macro_export]
macro_rules! b1024 { ($($stat:stmt)*) => { nano::run_bench(|| { r1024!($($stat)*); }) / 1024.0 } }
#[macro_export]
macro_rules! b2048 { ($($stat:stmt)*) => { nano::run_bench(|| { r2048!($($stat)*); }) / 2048.0 } }
#[macro_export]
macro_rules! b4096 { ($($stat:stmt)*) => { nano::run_bench(|| { r4096!($($stat)*); }) / 4096.0 } }
#[macro_export]
macro_rules! b8192 { ($($stat:stmt)*) => { nano::run_bench(|| { r8192!($($stat)*); }) / 8192.0 } }
fn black_box<T>(dummy: T) -> T {
unsafe {
let ret = std::ptr::read_volatile(&dummy);
std::mem::forget(dummy);
ret
}
}
pub fn run_bench<T, F: FnMut() -> T>(mut f: F) -> f64 {
let start = Instant::now();
black_box(f());
let once = start.elapsed();
let evaled = if once < Duration::from_millis(1) {
let start = Instant::now();
for _ in 0..1000 {
black_box(f());
}
start.elapsed().as_secs_f64() / 1000.
} else {
once.as_secs_f64()
};
let warmup = (0.3 / evaled) as usize;
let iters = (0.3 / evaled) as usize;
let chunks = 1000;
let chunk = (iters / chunks).max(50);
let chunks = (iters / chunk).max(50);
let mut measures = vec![0.0; chunks];
for _ in 0..warmup {
black_box(f());
}
for m in &mut measures {
let start = Instant::now();
for _ in 0..chunk {
black_box(f());
}
*m = start.elapsed().as_secs_f64() / chunk as f64
}
measures.sort_by(|a, b| {
if a < b {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
});
let q1 = measures[chunks / 4];
let q3 = measures[chunks - chunks / 4];
let iq = q3 - q1;
measures.retain(|&x| x >= q1 - 3. * iq && x <= q3 + 3. * iq);
measures.iter().copied().sum::<f64>() / measures.len() as f64
}
@@ -0,0 +1,356 @@
#![allow(non_snake_case)]
use criterion::measurement::WallTime;
use criterion::*;
use tract_data::internal::*;
use tract_linalg::frame::MatMatMulImpl;
use tract_linalg::mmm::FusedSpec;
use tract_linalg::mmm::MatMatMul;
use tract_linalg::mmm::MatMatMulKer;
use tract_linalg::mmm::ScratchSpaceFusedNonLinear;
use Throughput::Elements;
fn packa<K: MatMatMulKer<f32>>(crit: &mut BenchmarkGroup<WallTime>, m: usize, k: usize) {
let a = Tensor::zero_dt(DatumType::F32, &[m, k]).unwrap();
unsafe {
let mmm = MatMatMulImpl::<K, f32>::new();
let mut pa = Tensor::zero_aligned_dt(
DatumType::F32,
&[mmm.a_pack().len(k, m)],
mmm.a_pack().alignment(),
)
.unwrap();
crit.throughput(Elements((m * k) as _))
.bench_function("packa", |be| {
be.iter(|| mmm.a_pack().pack(&mut pa.view_mut(), &a.view(), 1, 0));
});
}
}
fn packb<K: MatMatMulKer<f32>>(crit: &mut BenchmarkGroup<WallTime>, k: usize, n: usize) {
let b = Tensor::zero_dt(DatumType::F32, &[k, n]).unwrap();
unsafe {
let mmm = MatMatMulImpl::<K, f32>::new();
let mut pb = Tensor::zero_aligned_dt(
DatumType::F32,
&[mmm.b_pack().len(k, n)],
mmm.b_pack().alignment(),
)
.unwrap();
crit.throughput(Elements((k * n) as _))
.bench_function("packb", |be| {
be.iter(|| mmm.b_pack().pack(&mut pb.view_mut(), &b.view(), 0, 1));
});
}
}
fn packed_a<K: MatMatMulKer<f32>>(m: usize, k: usize) -> Tensor {
let mmm = MatMatMulImpl::<K, f32>::new();
Tensor::zero_aligned_dt(
DatumType::F32,
&[mmm.a_pack().len(k, m)],
mmm.a_pack().alignment(),
)
.unwrap()
}
fn packed_b<K: MatMatMulKer<f32>>(k: usize, n: usize) -> Tensor {
let mmm = MatMatMulImpl::<K, f32>::new();
Tensor::zero_aligned_dt(
DatumType::F32,
&[mmm.b_pack().len(k, n)],
mmm.b_pack().alignment(),
)
.unwrap()
}
#[inline(always)]
unsafe fn valid_tile(
scratch: &mut ScratchSpaceFusedNonLinear<f32>,
ops: &[FusedSpec],
ia: usize,
ib: usize,
) {
scratch.for_valid_tile::<K>(ops, ia, ib);
let err = K::kernel(&scratch.uspecs());
debug_assert_eq!(err, 0, "Kernel return error {}", err);
}
unsafe fn packedpacked<K>(m: usize, k: usize, n: usize) -> (Tensor, Tensor, Tensor)
where
K: MatMatMulKer<f32>,
{
let c = Tensor::zero_dt(DatumType::F32, &[m, n]).unwrap();
let pa = packed_a::<K>(m, k);
let pb = packed_b::<K>(k, n);
(pa, pb, c)
}
unsafe fn packedpacked_ops<'a, K: MatMatMulKer<f32>>(
k: usize,
pa: &'a Tensor,
pb: &'a Tensor,
c: &'a mut Tensor,
) -> TVec<FusedSpec<'a>> {
let mmm = MatMatMulImpl::<K, f32>::new();
tvec!(
FusedSpec::AddMatMul {
k,
a: mmm.a_packed(4, k).wrap(&pa.view()),
b: mmm.b_packed(4, k).wrap(&pb.view()).unwrap(),
},
FusedSpec::Store(mmm.c_view(0, 1).wrap(&mut c.view_mut())),
)
}
unsafe fn packedpacking<K>(m: usize, k: usize, n: usize) -> (Tensor, Tensor, Tensor)
where
K: MatMatMulKer<f32>,
{
let c = Tensor::zero_dt(DatumType::F32, &[m, n]).unwrap();
let pa = packed_a::<K>(m, k);
let b = Tensor::zero_dt(DatumType::F32, &[k, n]).unwrap();
(pa, b, c)
}
unsafe fn packedpacking_ops<'a, K: MatMatMulKer<f32>>(
k: usize,
pa: &'a Tensor,
b: &'a Tensor,
c: &'a mut Tensor,
) -> TVec<FusedSpec<'a>> {
let mmm = MatMatMulImpl::<K, f32>::new();
tvec!(
FusedSpec::AddMatMul {
k,
a: mmm.a_packed(4, k).wrap(&pa.view()),
b: mmm.b_late_packing().wrap(&b.view()).unwrap(),
},
FusedSpec::Store(mmm.c_view(0, 1).wrap(&mut c.view_mut())),
)
}
fn packedpacked_mr_nr<K: MatMatMulKer<f32>>(
crit: &mut BenchmarkGroup<WallTime>,
m: usize,
k: usize,
n: usize,
) {
unsafe {
let (pa, pb, mut c) = packedpacked::<K>(m, k, n);
let ops = packedpacked_ops::<K>(k, &pa, &pb, &mut c);
let mut scratch = ScratchSpaceFusedNonLinear::<f32>::default();
crit.throughput(Elements((m * k * n) as _))
.bench_function("packedpacked_mr_nr", |be| {
be.iter(|| {
scratch.prepare::<K>(&ops);
for ia in 0..m / K::mr() {
for ib in 0..n / K::nr() {
valid_tile(&mut scratch, &ops, ia, ib);
}
}
})
});
}
}
fn packedpacked_nr_mr<K: MatMatMulKer<f32>>(
crit: &mut BenchmarkGroup<WallTime>,
m: usize,
k: usize,
n: usize,
) {
unsafe {
let (pa, pb, mut c) = packedpacked::<K>(m, k, n);
let ops = packedpacked_ops::<K>(k, &pa, &pb, &mut c);
let mut scratch = ScratchSpaceFusedNonLinear::<f32>::default();
crit.throughput(Elements((m * k * n) as _))
.bench_function("packedpacked_nr_mr", |be| {
be.iter(|| {
scratch.prepare::<K>(&ops);
for ib in 0..n / K::nr() {
for ia in 0..m / K::mr() {
valid_tile(&mut scratch, &ops, ia, ib);
}
}
})
});
}
}
fn packedpacking_mr_nr<K: MatMatMulKer<f32>>(
crit: &mut BenchmarkGroup<WallTime>,
m: usize,
k: usize,
n: usize,
) {
unsafe {
let (pa, b, mut c) = packedpacking::<K>(m, k, n);
let ops = packedpacking_ops::<K>(k, &pa, &b, &mut c);
let mut scratch = ScratchSpaceFusedNonLinear::<f32>::default();
crit.throughput(Elements((m * k * n) as _))
.bench_function("packedpacking_mr_nr", |be| {
be.iter(|| {
scratch.prepare::<K>(&ops);
for ia in 0..m / K::mr() {
for ib in 0..n / K::nr() {
valid_tile(&mut scratch, &ops, ia, ib);
}
}
})
});
}
}
fn packedpacking_nr_mr<K: MatMatMulKer<f32>>(
crit: &mut BenchmarkGroup<WallTime>,
m: usize,
k: usize,
n: usize,
) {
unsafe {
let (pa, b, mut c) = packedpacking::<K>(m, k, n);
let ops = packedpacking_ops::<K>(k, &pa, &b, &mut c);
let mut scratch = ScratchSpaceFusedNonLinear::<f32>::default();
crit.throughput(Elements((m * k * n) as _))
.bench_function("packedpacking_nr_mr", |be| {
be.iter(|| {
scratch.prepare::<K>(&ops);
for ib in 0..n / K::nr() {
for ia in 0..m / K::mr() {
valid_tile(&mut scratch, &ops, ia, ib);
}
}
})
});
}
}
fn packedpacked_mc_nc_mr_nr<K: MatMatMulKer<f32>>(
crit: &mut BenchmarkGroup<WallTime>,
m: usize,
k: usize,
n: usize,
) {
unsafe {
let (pa, pb, mut c) = packedpacked::<K>(m, k, n);
let ops = packedpacked_ops::<K>(k, &pa, &pb, &mut c);
let mut scratch = ScratchSpaceFusedNonLinear::<f32>::default();
crit.throughput(Elements((m * k * n) as _)).bench_function(
"packedpacked_mc_nc_mr_nr",
|be| {
be.iter(|| {
scratch.prepare::<K>(&ops);
let mc = 128 - K::mr() % 128;
let nc = 128 - K::nr() % 128;
// eprintln!("{}x{} {}x{} {}x{}", m, n, mc, nc, K::mr(), K::nr());
for oa in 0..m.divceil(mc) {
for ob in 0..n.divceil(nc) {
for ia in 0..mc / K::mr() {
for ib in 0..nc / K::nr() {
let a = oa * mc / K::mr() + ia;
let b = ob * nc / K::nr() + ib;
if (a + 1) * K::mr() > m || (b + 1) * K::nr() > n {
continue;
}
valid_tile(&mut scratch, &ops, ia, ib);
}
}
}
}
})
},
);
}
}
fn packedpacking_mc_nc_mr_nr<K: MatMatMulKer<f32>>(
crit: &mut BenchmarkGroup<WallTime>,
m: usize,
k: usize,
n: usize,
) {
unsafe {
let (pa, b, mut c) = packedpacking::<K>(m, k, n);
let ops = packedpacking_ops::<K>(k, &pa, &b, &mut c);
let mut scratch = ScratchSpaceFusedNonLinear::<f32>::default();
crit.throughput(Elements((m * k * n) as _)).bench_function(
"packedpacking_mc_nc_mr_nr",
|be| {
be.iter(|| {
scratch.prepare::<K>(&ops);
let mc = 128 - K::mr() % 128;
let nc = 128 - K::nr() % 128;
// eprintln!("{}x{} {}x{} {}x{}", m, n, mc, nc, K::mr(), K::nr());
for oa in 0..m.divceil(mc) {
for ob in 0..n.divceil(nc) {
for ib in 0..nc / K::nr() {
for ia in 0..mc / K::mr() {
let a = oa * mc / K::mr() + ia;
let b = ob * nc / K::nr() + ib;
if (a + 1) * K::mr() > m || (b + 1) * K::nr() > n {
continue;
}
valid_tile(&mut scratch, &ops, ia, ib);
}
}
}
}
})
},
);
}
}
#[cfg(target_arch = "x86_64")]
type K = tract_linalg::x86_64_fma::mmm::MatMatMulF32x16x6;
#[cfg(target_arch = "aarch64")]
type K = tract_linalg::arm64::MatMatMulF32x12x8;
#[cfg(target_arch = "arm")]
type K = tract_linalg::arm32::armv7neon::MatMatMulF32x8x6CortexA9;
fn matmul(c: &mut Criterion, m: usize, k: usize, n: usize) {
let mut c = c.benchmark_group(format!("{}x{}x{}", m, k, n));
packa::<K>(&mut c, m, k);
packb::<K>(&mut c, k, n);
packedpacked_mr_nr::<K>(&mut c, m, k, n);
packedpacked_nr_mr::<K>(&mut c, m, k, n);
packedpacked_mc_nc_mr_nr::<K>(&mut c, m, k, n);
packedpacking_mr_nr::<K>(&mut c, m, k, n);
packedpacking_nr_mr::<K>(&mut c, m, k, n);
packedpacking_mc_nc_mr_nr::<K>(&mut c, m, k, n);
c.finish();
}
fn big(c: &mut Criterion) {
matmul(c, 512, 512, 512);
#[cfg(target_arch = "x86_64")]
matmul(c, 99, 891, 1048576);
#[cfg(target_arch = "x86_64")]
matmul(c, 128, 1024, 1048576);
}
fn wavenet(c: &mut Criterion) {
matmul(c, 64, 48, 8);
matmul(c, 16, 64, 8);
matmul(c, 32, 64, 8);
}
fn asr_15M(c: &mut Criterion) {
matmul(c, 768, 200, 18);
matmul(c, 768, 2304, 18);
matmul(c, 768, 2304, 6);
}
fn inception(c: &mut Criterion) {
matmul(c, 64, 288, 21609);
}
criterion_group!(benches, big, wavenet, asr_15M, inception);
criterion_main!(benches);
@@ -0,0 +1,22 @@
#[macro_use]
extern crate criterion;
extern crate tract_linalg;
use criterion::Criterion;
fn ssigmoid(c: &mut Criterion, n: usize) {
c.bench_function(&format!("ssigmoid_{n}"), move |be| {
let mut s = (0..n).map(|i| i as f32 / 10.0).collect::<Vec<f32>>();
let op = &(tract_linalg::ops().sigmoid_f32)();
be.iter(|| op.run(&mut s));
});
}
fn bs(c: &mut Criterion) {
ssigmoid(c, 4);
ssigmoid(c, 8);
ssigmoid(c, 128);
ssigmoid(c, 1024);
}
criterion_group!(benches, bs);
criterion_main!(benches);
@@ -0,0 +1,154 @@
#![allow(dead_code)]
use criterion::*;
use tract_data::internal::*;
use tract_linalg::frame::mmm::{FusedSpec, InputStore, PackedStore};
use tract_linalg::frame::MatMatMul;
use DatumType::*;
pub fn packed_packed(c: &mut Criterion, name: &str, m: usize, k: usize, n: usize) {
let mut group = c.benchmark_group(format!("{name}/packed_packed"));
group.throughput(Throughput::Elements((m * k * n) as u64));
let id = format!("{m}x{k}x{n}");
group.bench_with_input(
BenchmarkId::new("f32/cold", &id),
&(F32, m, k, n, true),
mat_mat,
);
group.bench_with_input(
BenchmarkId::new("f32/hot", &id),
&(F32, m, k, n, false),
mat_mat,
);
group.bench_with_input(
BenchmarkId::new("i8/cold", &id),
&(I8, m, k, n, true),
mat_mat,
);
group.bench_with_input(
BenchmarkId::new("i8/hot", &id),
&(I8, m, k, n, false),
mat_mat,
);
}
pub fn packed_vec(c: &mut Criterion, name: &str, m: usize, k: usize, n: usize) {
assert_eq!(n, 1);
let mut group = c.benchmark_group(format!("{name}/packed_vec"));
group.throughput(Throughput::Elements((m * k * n) as u64));
let id = format!("{m}x{k}x{n}");
group.bench_with_input(
BenchmarkId::new("f32/cold", &id),
&(F32, m, k, n, true),
mat_vec,
);
group.bench_with_input(
BenchmarkId::new("f32/hot", &id),
&(F32, m, k, n, false),
mat_vec,
);
group.bench_with_input(
BenchmarkId::new("i8/cold", &id),
&(I8, m, k, n, true),
mat_vec,
);
group.bench_with_input(
BenchmarkId::new("i8/hot", &id),
&(I8, m, k, n, false),
mat_vec,
);
}
pub fn ruin_cache() {
let _a = (0..1000000).collect::<Vec<i32>>();
}
#[allow(clippy::too_many_arguments)]
unsafe fn run(
m: usize,
k: usize,
n: usize,
be: &mut Bencher,
mm: &dyn MatMatMul,
pa: PackedStore,
pb: InputStore,
cold: bool,
) {
let mut scratch = mm.allocate_scratch_space();
be.iter_custom(move |iters| {
let mut dur = std::time::Duration::default();
for _ in 0..iters {
if cold {
ruin_cache();
}
let instant = std::time::Instant::now();
mm.run_with_scratch_space(
m,
n,
scratch.as_mut(),
&[FusedSpec::AddMatMul {
a: pa,
b: pb.clone(),
k,
}],
)
.unwrap();
let time = instant.elapsed();
dur += time;
}
dur
});
}
fn mat_mat(be: &mut Bencher, params: &(DatumType, usize, usize, usize, bool)) {
let (dt, m, k, n, _) = *params;
let mm = tract_linalg::ops()
.mmm(dt, dt, dt, Some(m), Some(k), Some(n))
.unwrap();
mat_mat_with_mm(be, &*mm, params)
}
pub fn mat_mat_with_mm(
be: &mut Bencher,
mm: &dyn MatMatMul,
&(dt, m, k, n, cold): &(DatumType, usize, usize, usize, bool),
) {
let pa =
Tensor::zero_aligned_dt(dt, &[mm.a_pack().len(k, m)], mm.a_pack().alignment()).unwrap();
let pb =
Tensor::zero_aligned_dt(dt, &[mm.b_pack().len(k, n)], mm.b_pack().alignment()).unwrap();
unsafe {
run(
m,
k,
n,
be,
mm,
mm.a_packed(dt.size_of(), k).wrap(&pa.view()),
mm.b_packed(dt.size_of(), k).wrap(&pb.view()).unwrap(),
cold,
);
}
}
fn mat_vec(be: &mut Bencher, &(dt, m, k, n, cold): &(DatumType, usize, usize, usize, bool)) {
assert_eq!(n, 1);
let mm = tract_linalg::ops()
.mmm(dt, dt, dt, Some(m), Some(k), Some(n))
.unwrap();
let pa =
Tensor::zero_aligned_dt(dt, &[mm.a_pack().len(k, m)], mm.a_pack().alignment()).unwrap();
let pb = Tensor::zero_dt(dt, &[k, 1]).unwrap();
unsafe {
run(
m,
k,
n,
be,
&*mm,
mm.a_packed(dt.size_of(), k).wrap(&pa.view()),
mm.b_packed(dt.size_of(), k).wrap(&pb.view()).unwrap(),
cold,
);
}
}
@@ -0,0 +1,51 @@
use criterion::measurement::WallTime;
use criterion::*;
use tract_data::internal::*;
#[allow(dead_code)]
#[path = "../tests/virtual_im2col.rs"]
mod virtual_im2col;
use virtual_im2col::ConvProblem;
fn conv(
c: &mut BenchmarkGroup<WallTime>,
ci: usize,
h: usize,
w: usize,
co: usize,
kh: usize,
kw: usize,
) {
// CHW HWIO
let input = Tensor::zero::<f32>(&[ci, h, w]).unwrap();
let filters = Tensor::zero::<f32>(&[kh, kw, ci, co]).unwrap();
let mut cv = ConvProblem {
input,
filters,
lazy_im2col: false,
};
c.bench_function("eager", |b| {
b.iter(|| {
cv.tract();
})
});
cv.lazy_im2col = true;
c.bench_function("lazy", |b| {
b.iter(|| {
cv.tract();
})
});
}
fn ex1(c: &mut Criterion) {
let mut c = c.benchmark_group("ex1");
conv(&mut c, 32, 256, 256, 32, 3, 3);
}
fn big(c: &mut Criterion) {
let mut c = c.benchmark_group("big");
conv(&mut c, 1, 1024, 1024, 99, 3, 3);
}
criterion_group!(benches, ex1, big);
criterion_main!(benches);
@@ -0,0 +1,198 @@
#![allow(
dead_code,
non_upper_case_globals,
unused_macros,
non_snake_case,
unused_assignments
)]
use std::arch::asm;
mod nano;
#[repr(C, align(64))]
struct Floats([f32; 4096]);
const _F32: Floats = Floats([12.; 4096]);
const F32: *const f32 = (&_F32) as *const Floats as *const f32;
lazy_static::lazy_static! {
static ref TICK: f64 = unsafe { b8192!(asm!("or rax, rax", out("rax") _)) };
}
macro_rules! kloop {
($filter: expr, $geo: literal, $n: expr, $path: literal, $ww: expr) => {
let label = $path.split("/").last().unwrap().split_once(".").unwrap().0;
let full_label = format!("{:8} {:40}", $geo, label);
if full_label.contains($filter.unwrap_or("")) {
let time = b2!({
let mut p = F32;
let mut q = F32;
r128!(asm!(include_str!(concat!("../x86_64/fma/", $path)),
inout("rax") p, inout("rcx") q,
out("zmm0") _, out("zmm1") _, out("zmm2") _, out("zmm3") _,
out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _,
out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _,
out("zmm12") _, out("zmm13") _, out("zmm14") _, out("zmm15") _,
out("zmm20") _, out("zmm21") _, out("zmm22") _, out("zmm23") _,
out("zmm24") _, out("zmm25") _, out("zmm26") _, out("zmm27") _,
));
}) / 128.;
println!("{} {:3.0}% ({:>5.2 }/{:3 } cy) {:.2} GFLOP/s", full_label, ($n as f64 / $ww as f64) / time * 100. * *TICK, time / *TICK, $n as f64 / $ww as f64, $n as f64 / time / 1e9 );
}
}
}
unsafe fn packed_packed_1x8(f: Option<&str>) {
println!("-- 1x8 kernels");
kloop!(f, "1x8x1", (8 * 8), "8x8/packed_packed_loop1/avx.tmpli", 8);
kloop!(
f,
"1x8x2",
(8 * 8 * 2),
"8x8/packed_packed_loop1/avx-unroll.tmpli",
8
);
println!();
}
unsafe fn packed_packed_2x6(f: Option<&str>) {
println!("-- 2x6 kernels");
kloop!(
f,
"2x6x1",
(16 * 6),
"2x6/packed_packed_loop1/original.tmpli",
8
);
kloop!(
f,
"2x6x2",
(16 * 6 * 2),
"2x6/packed_packed_loop1/original-unroll.tmpli",
8
);
// if std::is_x86_feature_detected!("avx512f") {
// kloop!(f, "2x6x1", (32 * 6), "2x6/packed_packed_loop1/avx-512.tmpli", 16);
// kloop!(f, "2x6x2", (32 * 6 * 2), "2x6/packed_packed_loop1/avx-512-unroll.tmpli", 16);
// }
println!();
}
unsafe fn packed_packed_2x5(f: Option<&str>) {
println!("-- 2x5 kernels");
kloop!(f, "2x5x1", (16 * 5), "2x5/packed_packed_loop1/avx.tmpli", 8);
kloop!(
f,
"2x5x2",
(16 * 5 * 2),
"2x5/packed_packed_loop1/avx-unroll.tmpli",
8
);
// if std::is_x86_feature_detected!("avx512f") {
// kloop!(f, "2x5x1", (32 * 5), "2x5/packed_packed_loop1/avx-512.tmpli", 16);
// kloop!(f, "2x5x2", (32 * 5 * 2), "2x5/packed_packed_loop1/avx-512-unroll.tmpli", 16);
// }
println!();
}
unsafe fn packed_packed_3x4(f: Option<&str>) {
println!("-- 3x4 kernels");
kloop!(f, "3x4x1", (24 * 4), "3x4/packed_packed_loop1/avx.tmpli", 8);
kloop!(
f,
"3x4x2",
(24 * 4 * 2),
"3x4/packed_packed_loop1/avx-unroll.tmpli",
8
);
// if std::is_x86_feature_detected!("avx512f") {
// kloop!(f, "3x4x1", (48 * 4), "3x4/packed_packed_loop1/avx-512.tmpli", 16);
// kloop!(f, "3x4x2", (48 * 4 * 2), "3x4/packed_packed_loop1/avx-512-unroll.tmpli", 16);
// }
println!();
}
unsafe fn packed_packed_4x3(f: Option<&str>) {
println!("-- 4x3 kernels");
kloop!(f, "4x3x1", (32 * 3), "4x3/packed_packed_loop1/avx.tmpli", 8);
kloop!(
f,
"4x3x2",
(32 * 3 * 2),
"4x3/packed_packed_loop1/avx-unroll.tmpli",
8
);
// if std::is_x86_feature_detected!("avx512f") {
// kloop!(f, "4x3x1", (64 * 3), "4x3/packed_packed_loop1/avx-512.tmpli", 16);
// kloop!(f, "4x3x2", (64 * 3 * 2), "4x3/packed_packed_loop1/avx-512-unroll.tmpli", 16);
// }
println!();
}
unsafe fn packed_packed_5x2(f: Option<&str>) {
println!("-- 5x2 kernels");
kloop!(f, "5x2x1", (40 * 2), "5x2/packed_packed_loop1/avx.tmpli", 8);
kloop!(
f,
"5x2x1",
(40 * 2 * 2),
"5x2/packed_packed_loop1/avx-unroll.tmpli",
8
);
// if std::is_x86_feature_detected!("avx512f") {
// kloop!(f, "5x2x1", (80 * 2), "5x2/packed_packed_loop1/avx-512.tmpli", 16);
// kloop!(f, "5x2x2", (80 * 2 * 2), "5x2/packed_packed_loop1/avx-512-unroll.tmpli", 16);
// }
println!();
}
unsafe fn packed_packed_6x2(f: Option<&str>) {
println!("-- 6x2 kernels");
kloop!(f, "6x2x1", (40 * 2), "6x2/packed_packed_loop1/avx.tmpli", 8);
kloop!(
f,
"6x2x2",
(40 * 2 * 2),
"6x2/packed_packed_loop1/avx-unroll.tmpli",
8
);
// if std::is_x86_feature_detected!("avx512f") {
// kloop!(f, "6x2x1", (80 * 2), "6x2/packed_packed_loop1/avx-512.tmpli", 16);
// kloop!(f, "6x2x2", (80 * 2 * 2), "6x2/packed_packed_loop1/avx-512-unroll.tmpli", 16);
// }
println!();
}
#[allow(clippy::identity_op)]
unsafe fn packed_packed_8x1(f: Option<&str>) {
println!("-- 8x1 kernels");
kloop!(f, "8x1x1", (64 * 1), "8x1/packed_packed_loop1/avx.tmpli", 8);
kloop!(
f,
"8x1x2",
(64 * 1 * 2),
"8x1/packed_packed_loop1/avx-unroll.tmpli",
8
);
// if std::is_x86_feature_detected!("avx512f") {
// kloop!(f, "8x1x1", (128 * 1), "8x1/packed_packed_loop1/avx-512.tmpli", 16);
// kloop!(f, "8x1x2", (128 * 1 * 2), "8x1/packed_packed_loop1/avx-512-unroll.tmpli", 16);
// }
println!();
}
fn main() {
let filter = std::env::args().skip(1).find(|a| a != "--bench");
unsafe {
packed_packed_1x8(filter.as_deref());
packed_packed_2x6(filter.as_deref());
packed_packed_2x5(filter.as_deref());
packed_packed_3x4(filter.as_deref());
packed_packed_4x3(filter.as_deref());
packed_packed_5x2(filter.as_deref());
packed_packed_6x2(filter.as_deref());
packed_packed_8x1(filter.as_deref());
}
}