Skip to content

Commit da1bb51

Browse files
committed
prefetch data into cpu cache aot
1 parent 341160c commit da1bb51

5 files changed

Lines changed: 163 additions & 62 deletions

File tree

crates/yscv-kernels/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ packing loop.
6161
Cascade dispatch: 48→32→16→8→4→scalar columns. k-loop unrolled by 2
6262
with doubled accumulator sets to break FMA latency dependency chains
6363
(FMA latency = 4 cycles, 2 FMA ports on Zen 4 — spacing accumulator
64-
reuse to 4+ cycles eliminates pipeline stalls).
64+
reuse to 4+ cycles eliminates pipeline stalls). Four-row transposed-A tiles
65+
prefetch strided B panels four K iterations ahead for the measured
66+
`M >= 128, K <= 32` AVX/FMA range and in-order Cortex-A53/A55 kernels.
6567

6668
### Depthwise Conv SIMD
6769

crates/yscv-kernels/benches/kernels_cpu_ops.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::num::NonZeroUsize;
22

33
use criterion::{Criterion, black_box, criterion_group, criterion_main};
44
use yscv_kernels::{
5-
Backend, BatchNorm2dParams, LayerNormLastDimParams, ParallelElementwiseConfig,
5+
Backend, BatchNorm2dParams, BinaryKind, LayerNormLastDimParams, ParallelElementwiseConfig,
66
ParallelMatmulConfig, SeparableConv2dParams, ThreadedCpuBackend, ThreadedCpuBackendConfig, add,
7-
avg_pool2d_nhwc, batch_norm2d_nhwc, conv2d_nhwc, conv2d_nhwc_indirect_padded,
8-
conv2d_nhwc_padded, depthwise_conv2d_nhwc, layer_norm_last_dim, log_softmax_last_dim,
9-
logsumexp_last_dim, matmul_2d, matmul_2d_sequential, max_pool2d_nhwc, relu,
10-
separable_conv2d_nhwc, sigmoid, softmax_last_dim,
7+
avg_pool2d_nhwc, batch_norm2d_nhwc, binary_same_shape_dispatch, conv2d_nhwc,
8+
conv2d_nhwc_indirect_padded, conv2d_nhwc_padded, depthwise_conv2d_nhwc, layer_norm_last_dim,
9+
log_softmax_last_dim, logsumexp_last_dim, matmul_2d, matmul_2d_sequential, max_pool2d_nhwc,
10+
relu, separable_conv2d_nhwc, sigmoid, softmax_last_dim,
1111
};
1212
use yscv_tensor::Tensor;
1313

@@ -170,6 +170,27 @@ fn bench_elementwise_modes(c: &mut Criterion) {
170170
black_box(out);
171171
});
172172
});
173+
let mut raw_out = vec![0.0; lhs.data().len()];
174+
group.bench_function("add_same_shape_raw_slice", |b| {
175+
b.iter(|| {
176+
binary_same_shape_dispatch(
177+
black_box(lhs.data()),
178+
black_box(rhs.data()),
179+
black_box(&mut raw_out),
180+
BinaryKind::Add,
181+
);
182+
});
183+
});
184+
group.bench_function("mul_same_shape_raw_slice", |b| {
185+
b.iter(|| {
186+
binary_same_shape_dispatch(
187+
black_box(lhs.data()),
188+
black_box(rhs.data()),
189+
black_box(&mut raw_out),
190+
BinaryKind::Mul,
191+
);
192+
});
193+
});
173194
group.bench_function("add_same_shape_threaded_2", |b| {
174195
b.iter(|| {
175196
let out = threaded_backend

crates/yscv-kernels/src/ops/matmul/trans_a.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@
44
55
use super::*;
66

7+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
8+
const MATMUL_PREFETCH_AHEAD: usize = 4;
9+
10+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
11+
#[inline(always)]
12+
#[allow(unsafe_code, unsafe_op_in_unsafe_fn)]
13+
unsafe fn prefetch_l1_keep(p: *const f32) {
14+
#[cfg(target_arch = "x86")]
15+
std::arch::x86::_mm_prefetch::<{ std::arch::x86::_MM_HINT_T0 }>(p as *const i8);
16+
#[cfg(target_arch = "x86_64")]
17+
std::arch::x86_64::_mm_prefetch::<{ std::arch::x86_64::_MM_HINT_T0 }>(p as *const i8);
18+
#[cfg(target_arch = "aarch64")]
19+
core::arch::asm!(
20+
"prfm pldl1keep, [{p}]",
21+
p = in(reg) p,
22+
options(nostack, preserves_flags, readonly),
23+
);
24+
}
25+
726
pub(super) fn non_trans_4row_disabled() -> bool {
827
static CACHED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
928
*CACHED.get_or_init(|| std::env::var_os("YSCV_NON_TRANS_4ROW_OFF").is_some())
@@ -929,6 +948,8 @@ unsafe fn trans_a_4row_avx2(
929948
use std::arch::x86::*;
930949
#[cfg(target_arch = "x86_64")]
931950
use std::arch::x86_64::*;
951+
let prefetch = m >= 128 && n > 16 && k <= 32;
952+
let prefetch_end = k.saturating_sub(MATMUL_PREFETCH_AHEAD);
932953
unsafe {
933954
let r0 = out_4rows.as_mut_ptr();
934955
let r1 = r0.add(n);
@@ -955,6 +976,9 @@ unsafe fn trans_a_4row_avx2(
955976
let a1 = _mm256_set1_ps(*a_row.add(1));
956977
let a2 = _mm256_set1_ps(*a_row.add(2));
957978
let a3 = _mm256_set1_ps(*a_row.add(3));
979+
if prefetch && ki < prefetch_end {
980+
prefetch_l1_keep(b.as_ptr().add((ki + MATMUL_PREFETCH_AHEAD) * n + col));
981+
}
958982
let bptr = b.as_ptr().add(ki * n + col);
959983
let b0 = _mm256_loadu_ps(bptr);
960984
let b1 = _mm256_loadu_ps(bptr.add(8));
@@ -1015,6 +1039,8 @@ unsafe fn trans_a_4row_neon(
10151039
n: usize,
10161040
out_4rows: &mut [f32],
10171041
) {
1042+
let prefetch = n > 16 && crate::host_cpu().uarch.is_in_order();
1043+
let prefetch_end = k.saturating_sub(MATMUL_PREFETCH_AHEAD);
10181044
unsafe {
10191045
let r0 = out_4rows.as_mut_ptr();
10201046
let r1 = r0.add(n);
@@ -1048,6 +1074,9 @@ unsafe fn trans_a_4row_neon(
10481074
let a1 = vdupq_n_f32(*a_row.add(1));
10491075
let a2 = vdupq_n_f32(*a_row.add(2));
10501076
let a3 = vdupq_n_f32(*a_row.add(3));
1077+
if prefetch && ki < prefetch_end {
1078+
prefetch_l1_keep(b.as_ptr().add((ki + MATMUL_PREFETCH_AHEAD) * n + col));
1079+
}
10511080
let bptr = b.as_ptr().add(ki * n + col);
10521081
let b0 = vld1q_f32(bptr);
10531082
let b1 = vld1q_f32(bptr.add(4));

crates/yscv-kernels/src/ops/simd/binary.rs

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -507,23 +507,10 @@ unsafe fn binary_same_shape_avx(lhs: &[f32], rhs: &[f32], out: &mut [f32], kind:
507507
let out_ptr = out.as_mut_ptr();
508508
let mut index = 0usize;
509509

510-
// 4x unrolled: process 32 floats per iteration with software prefetch.
511-
// Matches vDSP throughput by keeping the OoO pipeline fully saturated.
510+
// 4x unrolled: process 32 floats per iteration.
512511
match kind {
513512
BinaryKind::Add => {
514513
while index + 32 <= len {
515-
#[cfg(target_arch = "x86")]
516-
{
517-
use std::arch::x86::_mm_prefetch;
518-
_mm_prefetch::<3>(left_ptr.add(index + 32) as *const i8);
519-
_mm_prefetch::<3>(right_ptr.add(index + 32) as *const i8);
520-
}
521-
#[cfg(target_arch = "x86_64")]
522-
{
523-
use std::arch::x86_64::_mm_prefetch;
524-
_mm_prefetch::<3>(left_ptr.add(index + 32) as *const i8);
525-
_mm_prefetch::<3>(right_ptr.add(index + 32) as *const i8);
526-
}
527514
let a0 = _mm256_loadu_ps(left_ptr.add(index));
528515
let b0 = _mm256_loadu_ps(right_ptr.add(index));
529516
let a1 = _mm256_loadu_ps(left_ptr.add(index + 8));
@@ -541,18 +528,6 @@ unsafe fn binary_same_shape_avx(lhs: &[f32], rhs: &[f32], out: &mut [f32], kind:
541528
}
542529
BinaryKind::Sub => {
543530
while index + 32 <= len {
544-
#[cfg(target_arch = "x86")]
545-
{
546-
use std::arch::x86::_mm_prefetch;
547-
_mm_prefetch::<3>(left_ptr.add(index + 32) as *const i8);
548-
_mm_prefetch::<3>(right_ptr.add(index + 32) as *const i8);
549-
}
550-
#[cfg(target_arch = "x86_64")]
551-
{
552-
use std::arch::x86_64::_mm_prefetch;
553-
_mm_prefetch::<3>(left_ptr.add(index + 32) as *const i8);
554-
_mm_prefetch::<3>(right_ptr.add(index + 32) as *const i8);
555-
}
556531
let a0 = _mm256_loadu_ps(left_ptr.add(index));
557532
let b0 = _mm256_loadu_ps(right_ptr.add(index));
558533
let a1 = _mm256_loadu_ps(left_ptr.add(index + 8));
@@ -570,18 +545,6 @@ unsafe fn binary_same_shape_avx(lhs: &[f32], rhs: &[f32], out: &mut [f32], kind:
570545
}
571546
BinaryKind::Mul => {
572547
while index + 32 <= len {
573-
#[cfg(target_arch = "x86")]
574-
{
575-
use std::arch::x86::_mm_prefetch;
576-
_mm_prefetch::<3>(left_ptr.add(index + 32) as *const i8);
577-
_mm_prefetch::<3>(right_ptr.add(index + 32) as *const i8);
578-
}
579-
#[cfg(target_arch = "x86_64")]
580-
{
581-
use std::arch::x86_64::_mm_prefetch;
582-
_mm_prefetch::<3>(left_ptr.add(index + 32) as *const i8);
583-
_mm_prefetch::<3>(right_ptr.add(index + 32) as *const i8);
584-
}
585548
let a0 = _mm256_loadu_ps(left_ptr.add(index));
586549
let b0 = _mm256_loadu_ps(right_ptr.add(index));
587550
let a1 = _mm256_loadu_ps(left_ptr.add(index + 8));

0 commit comments

Comments
 (0)