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:
+191
@@ -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);
|
||||
}
|
||||
+88
@@ -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>();
|
||||
}
|
||||
+1095
File diff suppressed because it is too large
Load Diff
Vendored
+52
@@ -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);
|
||||
+52
@@ -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);
|
||||
+37
@@ -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);
|
||||
Vendored
+47
@@ -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);
|
||||
Vendored
+12
@@ -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);
|
||||
+113
@@ -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
|
||||
}
|
||||
+356
@@ -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);
|
||||
+22
@@ -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);
|
||||
+154
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
+51
@@ -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);
|
||||
+198
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user