Add 2 tests to check SIMD code against URI_CHARS

This commit is contained in:
Anthony Ramine
2021-04-02 09:57:03 +02:00
committed by Sean McArthur
parent 9643c936be
commit c394735ec4
3 changed files with 150 additions and 24 deletions
+42
View File
@@ -117,3 +117,45 @@ unsafe fn match_header_value_char_32_avx(buf: &[u8]) -> usize {
unsafe fn match_header_value_char_32_avx(_: &[u8]) -> usize {
unreachable!("AVX2 detection should be disabled for x86");
}
#[test]
fn avx2_code_matches_uri_chars_table() {
match super::detect() {
super::AVX_2 | super::AVX_2_AND_SSE_42 => {},
_ => return,
}
unsafe {
assert!(byte_is_allowed(b'_'));
for (b, allowed) in ::URI_MAP.iter().cloned().enumerate() {
assert_eq!(
byte_is_allowed(b as u8), allowed,
"byte_is_allowed({:?}) should be {:?}", b, allowed,
);
}
}
}
#[cfg(test)]
unsafe fn byte_is_allowed(byte: u8) -> bool {
let slice = [
b'_', b'_', b'_', b'_',
b'_', b'_', b'_', b'_',
b'_', b'_', b'_', b'_',
b'_', b'_', b'_', b'_',
b'_', b'_', b'_', b'_',
b'_', b'_', b'_', b'_',
b'_', b'_', byte, b'_',
b'_', b'_', b'_', b'_',
];
let mut bytes = Bytes::new(&slice);
parse_uri_batch_32(&mut bytes);
match bytes.pos() {
32 => true,
26 => false,
_ => unreachable!(),
}
}
+70 -24
View File
@@ -38,6 +38,38 @@ mod sse42;
))]
mod avx2;
#[cfg(all(
httparse_simd,
any(
target_arch = "x86",
target_arch = "x86_64",
),
))]
pub const SSE_42: usize = 1;
#[cfg(all(
httparse_simd,
any(not(httparse_simd_target_feature_sse42), httparse_simd_target_feature_avx2),
any(
target_arch = "x86",
target_arch = "x86_64",
),
))]
pub const AVX_2: usize = 2;
#[cfg(all(
httparse_simd,
any(
not(httparse_simd_target_feature_sse42),
httparse_simd_target_feature_avx2,
test,
),
any(
target_arch = "x86",
target_arch = "x86_64",
),
))]
pub const AVX_2_AND_SSE_42: usize = 3;
#[cfg(httparse_simd)]
const NONE: usize = ::core::usize::MAX;
#[cfg(all(
httparse_simd,
not(any(
@@ -62,27 +94,23 @@ mod runtime {
static FEATURE: AtomicUsize = ATOMIC_USIZE_INIT;
const INIT: usize = 0;
const SSE_42: usize = 1;
const AVX_2: usize = 2;
const AVX_2_AND_SSE_42: usize = 3;
const NONE: usize = ::core::usize::MAX;
fn detect() -> usize {
pub fn detect() -> usize {
let feat = FEATURE.load(Ordering::Relaxed);
if feat == INIT {
if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") {
if is_x86_feature_detected!("sse4.2") {
FEATURE.store(AVX_2_AND_SSE_42, Ordering::Relaxed);
return AVX_2_AND_SSE_42;
FEATURE.store(super::AVX_2_AND_SSE_42, Ordering::Relaxed);
return super::AVX_2_AND_SSE_42;
} else {
FEATURE.store(AVX_2, Ordering::Relaxed);
return AVX_2;
FEATURE.store(super::AVX_2, Ordering::Relaxed);
return super::AVX_2;
}
} else if is_x86_feature_detected!("sse4.2") {
FEATURE.store(SSE_42, Ordering::Relaxed);
return SSE_42;
FEATURE.store(super::SSE_42, Ordering::Relaxed);
return super::SSE_42;
} else {
FEATURE.store(NONE, Ordering::Relaxed);
FEATURE.store(super::NONE, Ordering::Relaxed);
}
}
feat
@@ -91,9 +119,9 @@ mod runtime {
pub fn match_uri_vectored(bytes: &mut ::Bytes) {
unsafe {
match detect() {
SSE_42 => super::sse42::parse_uri_batch_16(bytes),
AVX_2 => { super::avx2::parse_uri_batch_32(bytes); },
AVX_2_AND_SSE_42 => {
super::SSE_42 => super::sse42::parse_uri_batch_16(bytes),
super::AVX_2 => { super::avx2::parse_uri_batch_32(bytes); },
super::AVX_2_AND_SSE_42 => {
if let super::avx2::Scan::Found = super::avx2::parse_uri_batch_32(bytes) {
return;
}
@@ -109,9 +137,9 @@ mod runtime {
pub fn match_header_value_vectored(bytes: &mut ::Bytes) {
unsafe {
match detect() {
SSE_42 => super::sse42::match_header_value_batch_16(bytes),
AVX_2 => { super::avx2::match_header_value_batch_32(bytes); },
AVX_2_AND_SSE_42 => {
super::SSE_42 => super::sse42::match_header_value_batch_16(bytes),
super::AVX_2 => { super::avx2::match_header_value_batch_32(bytes); },
super::AVX_2_AND_SSE_42 => {
if let super::avx2::Scan::Found = super::avx2::match_header_value_batch_32(bytes) {
return;
}
@@ -149,7 +177,7 @@ pub use self::runtime::*;
))]
mod sse42_compile_time {
pub fn match_uri_vectored(bytes: &mut ::Bytes) {
if is_x86_feature_detected!("sse4.2") {
if detect() == super::SSE_42 {
unsafe {
super::sse42::parse_uri_batch_16(bytes);
}
@@ -159,7 +187,7 @@ mod sse42_compile_time {
}
pub fn match_header_value_vectored(bytes: &mut ::Bytes) {
if is_x86_feature_detected!("sse4.2") {
if detect() == super::SSE_42 {
unsafe {
super::sse42::match_header_value_batch_16(bytes);
}
@@ -167,6 +195,14 @@ mod sse42_compile_time {
// else do nothing
}
pub fn detect() -> usize {
if is_x86_feature_detected!("sse4.2") {
super::SSE_42
} else {
super::NONE
}
}
}
#[cfg(all(
@@ -191,13 +227,13 @@ pub use self::sse42_compile_time::*;
mod avx2_compile_time {
pub fn match_uri_vectored(bytes: &mut ::Bytes) {
// do both, since avx2 only works when bytes.len() >= 32
if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") {
if detect() == super::AVX_2_AND_SSE_42 {
unsafe {
super::avx2::parse_uri_batch_32(bytes);
}
}
if is_x86_feature_detected!("sse4.2") {
if detect() == super::SSE_42 {
unsafe {
super::sse42::parse_uri_batch_16(bytes);
}
@@ -208,7 +244,7 @@ mod avx2_compile_time {
pub fn match_header_value_vectored(bytes: &mut ::Bytes) {
// do both, since avx2 only works when bytes.len() >= 32
if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") {
if detect() == super::AVX_2_AND_SSE_42 {
let scanned = unsafe {
super::avx2::match_header_value_batch_32(bytes)
};
@@ -217,7 +253,7 @@ mod avx2_compile_time {
return;
}
}
if is_x86_feature_detected!("sse4.2") {
if detect() == super::SSE_42 {
unsafe {
super::sse42::match_header_value_batch_16(bytes);
}
@@ -225,6 +261,16 @@ mod avx2_compile_time {
// else do nothing
}
pub fn detect() -> usize {
if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") {
super::AVX_2_AND_SSE_42
} else if is_x86_feature_detected!("sse4.2") {
super::SSE_42
} else {
super::NONE
}
}
}
#[cfg(all(
+38
View File
@@ -97,3 +97,41 @@ unsafe fn match_header_value_char_16_sse(buf: &[u8]) -> usize {
_tzcnt_u32(res) as usize
}
#[test]
fn sse_code_matches_uri_chars_table() {
match super::detect() {
super::SSE_42 | super::AVX_2_AND_SSE_42 => {},
_ => return,
}
unsafe {
assert!(byte_is_allowed(b'_'));
for (b, allowed) in ::URI_MAP.iter().cloned().enumerate() {
assert_eq!(
byte_is_allowed(b as u8), allowed,
"byte_is_allowed({:?}) should be {:?}", b, allowed,
);
}
}
}
#[cfg(test)]
unsafe fn byte_is_allowed(byte: u8) -> bool {
let slice = [
b'_', b'_', b'_', b'_',
b'_', b'_', b'_', b'_',
b'_', b'_', byte, b'_',
b'_', b'_', b'_', b'_',
];
let mut bytes = Bytes::new(&slice);
parse_uri_batch_16(&mut bytes);
match bytes.pos() {
16 => true,
10 => false,
_ => unreachable!(),
}
}