use crate::consts::{gear, Aligned, M_TABLE, N_TABLE};
use crate::Fingerprint;
#[cfg(target_arch = "x86")]
use core::arch::x86::{
__m128i, __m256i, _mm256_add_epi32, _mm256_load_si256, _mm256_min_epu32, _mm256_mullo_epi32,
_mm256_set1_epi32, _mm256_set1_epi8, _mm256_store_si256, _mm_add_epi32, _mm_load_si128,
_mm_min_epu32, _mm_mullo_eip32, _mm_set1_epi32, _mm_set1_epi8, _mm_store_si128,
};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{
__m128i, __m256i, _mm256_add_epi32, _mm256_load_si256, _mm256_min_epu32, _mm256_mullo_epi32,
_mm256_set1_epi32, _mm256_set1_epi8, _mm256_store_si256, _mm_add_epi32, _mm_load_si128,
_mm_min_epu32, _mm_mullo_epi32, _mm_set1_epi32, _mm_set1_epi8, _mm_store_si128,
};
#[target_feature(enable = "avx")]
#[target_feature(enable = "avx2")]
pub unsafe fn fingerprint_avx2(bytes: &[u8]) -> Fingerprint {
let mut mins = Aligned([_mm256_set1_epi8(i8::MIN); 3]);
let mut hash = 0;
for byte in bytes {
hash = gear(hash, *byte);
let hash_vector = _mm256_set1_epi32(hash as i32);
for (i, min) in mins.0.iter_mut().enumerate() {
let x = _mm256_mullo_epi32(
hash_vector,
_mm256_load_si256(N_TABLE.0[i * 8..].as_ptr() as *const __m256i),
);
let x = _mm256_add_epi32(
x,
_mm256_load_si256(M_TABLE.0[i * 8..].as_ptr() as *const __m256i),
);
*min = _mm256_min_epu32(x, *min);
}
}
let mut ret = Aligned([0_u32; 24]);
for (i, min) in mins.0.iter().enumerate() {
_mm256_store_si256((&mut ret.0[i * 8..]).as_mut_ptr() as *mut __m256i, *min);
}
Fingerprint { fingerprint: ret.0 }
}
#[target_feature(enable = "sse2")]
#[target_feature(enable = "sse4.1")]
pub unsafe fn fingerprint_sse41(bytes: &[u8]) -> Fingerprint {
let mut mins = Aligned([_mm_set1_epi8(i8::MIN); 6]);
let mut hash = 0;
for byte in bytes {
hash = gear(hash, *byte);
let hash_vector = _mm_set1_epi32(hash as i32);
for (i, min) in mins.0.iter_mut().enumerate() {
let x = _mm_mullo_epi32(
hash_vector,
_mm_load_si128(N_TABLE.0[i * 4..].as_ptr() as *const __m128i),
);
let x = _mm_add_epi32(
x,
_mm_load_si128(M_TABLE.0[i * 4..].as_ptr() as *const __m128i),
);
*min = _mm_min_epu32(x, *min);
}
}
let mut ret = Aligned([0_u32; 24]);
for (i, min) in mins.0.iter().enumerate() {
_mm_store_si128((&mut ret.0[i * 4..]).as_mut_ptr() as *mut __m128i, *min);
}
Fingerprint { fingerprint: ret.0 }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::raw::fingerprint_scalar;
use crate::test_utils::make_data;
use rand::prelude::*;
use rand_chacha::*;
#[test]
fn avx2_once() {
let mut data = [0_u8; 16001];
let mut rand = ChaChaRng::seed_from_u64(0);
rand.fill(&mut data[..]);
let scan = fingerprint_scalar(&data[..]);
let scan_avx2 = unsafe { fingerprint_avx2(&data[..]) };
assert_eq!(scan, scan_avx2);
}
#[test]
fn sse41_once() {
let mut data = [0_u8; 16001];
let mut rand = ChaChaRng::seed_from_u64(0);
rand.fill(&mut data[..]);
let scan = fingerprint_scalar(&data[..]);
let scan_avx2 = unsafe { fingerprint_sse41(&data[..]) };
assert_eq!(scan, scan_avx2);
}
#[test]
fn avx2_100() {
for i in 0..100 {
let data = make_data(i);
let scan = fingerprint_scalar(&data[..]);
let scan_avx2 = unsafe { fingerprint_avx2(&data[..]) };
println!("Iteration: {}, data length: {}", i, data.len());
assert_eq!(scan, scan_avx2);
}
}
#[test]
fn sse41_100() {
for i in 0..100 {
let data = make_data(i);
let scan = fingerprint_scalar(&data[..]);
let scan_avx2 = unsafe { fingerprint_sse41(&data[..]) };
println!("Iteration: {}, data length: {}", i, data.len());
assert_eq!(scan, scan_avx2);
}
}
}