/* in case user likes doing a byte at a time, keep it fast */ if buf.len() == 1 { return adler32_len_1(adler, buf, sum2);
}
/* initial Adler-32 value (deferred check for len == 1 speed) */ if buf.is_empty() { return adler | (sum2 << 16);
}
/* in case short lengths are provided, keep it somewhat fast */ if buf.len() < 16 { return adler32_len_16(adler, buf, sum2);
}
// Split Adler-32 into component sums, it can be supplied by the caller sites (e.g. in a PNG file). letmut pair = (adler, sum2);
// If memory is not SIMD aligned, do scalar sums to an aligned // offset, provided that doing so doesn't completely eliminate // SIMD operation. Aligned loads are still faster on ARM, even // though there's no explicit aligned load instruction const _: () = assert!(core::mem::align_of::<uint8x16_t>() == 16); let (before, middle, after) = unsafe { buf.align_to::<uint8x16_t>() };
pair = handle_tail(pair, before);
for chunk in middle.chunks(NMAX as usize / core::mem::size_of::<uint8x16_t>()) {
pair = unsafe { accum32(pair, chunk) };
pair.0 %= BASE;
pair.1 %= BASE;
}
for chunk in &mut it { let d0_d3 = vld1q_u8_x4(chunk.as_ptr() as *const u8);
// Unfortunately it doesn't look like there's a direct sum 8 bit to 32 // bit instruction, we'll have to make due summing to 16 bits first let hsum = uint16x8x2_t(vpaddlq_u8(d0_d3.0), vpaddlq_u8(d0_d3.1));
let hsum_fold = uint16x8x2_t(vpadalq_u8(hsum.0, d0_d3.2), vpadalq_u8(hsum.1, d0_d3.3));
// If we do straight widening additions to the 16 bit values, we don't incur // the usual penalties of a pairwise add. We can defer the multiplications // until the very end. These will not overflow because we are incurring at // most 408 loop iterations (NMAX / 64), and a given lane is only going to be // summed into once. This means for the maximum input size, the largest value // we will see is 255 * 102 = 26010, safely under uint16 max
s2_0 = vaddw_u8(s2_0, vget_low_u8(d0_d3.0));
s2_1 = vaddw_high_u8(s2_1, d0_d3.0);
s2_2 = vaddw_u8(s2_2, vget_low_u8(d0_d3.1));
s2_3 = vaddw_high_u8(s2_3, d0_d3.1);
s2_4 = vaddw_u8(s2_4, vget_low_u8(d0_d3.2));
s2_5 = vaddw_high_u8(s2_5, d0_d3.2);
s2_6 = vaddw_u8(s2_6, vget_low_u8(d0_d3.3));
s2_7 = vaddw_high_u8(s2_7, d0_d3.3);
adacc_prev = adacc;
}
s3acc = vshlq_n_u32(s3acc, 6);
let remainder = it.remainder();
if !remainder.is_empty() { letmut s3acc_0 = vdupq_n_u32(0); for d0 in remainder.iter().copied() { let adler: uint16x8_t = vpaddlq_u8(d0);
s2_6 = vaddw_u8(s2_6, vget_low_u8(d0));
s2_7 = vaddw_high_u8(s2_7, d0);
adacc = vpadalq_u16(adacc, adler);
s3acc_0 = vaddq_u32(s3acc_0, adacc_prev);
adacc_prev = adacc;
}
let s2acc = vaddq_u32(s2acc, s3acc); let adacc2 = vpadd_u32(vget_low_u32(adacc), vget_high_u32(adacc)); let s2acc2 = vpadd_u32(vget_low_u32(s2acc), vget_high_u32(s2acc)); let as_ = vpadd_u32(adacc2, s2acc2);
(vget_lane_u32(as_, 0), vget_lane_u32(as_, 1))
}
#[cfg(test)] mod tests { usesuper::*;
quickcheck::quickcheck! { fn adler32_neon_is_adler32_rust(v: Vec<u8>, start: u32) -> bool { let neon = adler32_neon(start, &v); let rust = crate::adler32::generic::adler32_rust(start, &v);
rust == neon
}
}
const INPUT: [u8; 1024] = { letmut array = [0; 1024]; letmut i = 0; while i < array.len() {
array[i] = i as u8;
i += 1;
}
array
};
#[test] fn start_alignment() { // SIMD algorithm is sensitive to alignment; for i in0..16 { for start in [crate::ADLER32_INITIAL_VALUE as u32, 42] { let neon = adler32_neon(start, &INPUT[i..]); let rust = crate::adler32::generic::adler32_rust(start, &INPUT[i..]);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.