add RVV support and optimized uncompress speed
This commit is contained in:
@@ -216,6 +216,31 @@ int main() {
|
|||||||
return 0;
|
return 0;
|
||||||
}" SNAPPY_HAVE_NEON)
|
}" SNAPPY_HAVE_NEON)
|
||||||
|
|
||||||
|
#check RVV 1.0 need __riscv_ prefix
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include <riscv_vector.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
int main() {
|
||||||
|
uint8_t val = 3, dup[8];
|
||||||
|
size_t vl = __riscv_vsetvl_e8m1(8);
|
||||||
|
vuint8m1_t v = __riscv_vmv_v_x_u8m1(val, vl);
|
||||||
|
return 0;
|
||||||
|
}" SNAPPY_RVV_1)
|
||||||
|
|
||||||
|
|
||||||
|
#check RVV 0.7.1 not __riscv_ prefix
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include <riscv_vector.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
int main() {
|
||||||
|
uint8_t val = 3, dup[8];
|
||||||
|
size_t vl = vsetvl_e8m1(8);
|
||||||
|
vuint8m1_t v = vmv_v_x_u8m1(val, vl);
|
||||||
|
return 0;
|
||||||
|
}" SNAPPY_RVV_0_7)
|
||||||
|
|
||||||
include(CheckSymbolExists)
|
include(CheckSymbolExists)
|
||||||
check_symbol_exists("mmap" "sys/mman.h" HAVE_FUNC_MMAP)
|
check_symbol_exists("mmap" "sys/mman.h" HAVE_FUNC_MMAP)
|
||||||
check_symbol_exists("sysconf" "unistd.h" HAVE_FUNC_SYSCONF)
|
check_symbol_exists("sysconf" "unistd.h" HAVE_FUNC_SYSCONF)
|
||||||
|
|||||||
@@ -46,7 +46,13 @@
|
|||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SNAPPY_HAVE_SSSE3 || SNAPPY_HAVE_NEON
|
#if SNAPPY_RVV_1 || SNAPPY_RVV_0_7
|
||||||
|
#define SNAPPY_HAVE_RVV 1
|
||||||
|
#include <riscv_vector.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if SNAPPY_HAVE_SSSE3 || SNAPPY_HAVE_NEON || SNAPPY_HAVE_RVV
|
||||||
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 1
|
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 1
|
||||||
#else
|
#else
|
||||||
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 0
|
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 0
|
||||||
@@ -60,8 +66,23 @@ namespace internal {
|
|||||||
using V128 = __m128i;
|
using V128 = __m128i;
|
||||||
#elif SNAPPY_HAVE_NEON
|
#elif SNAPPY_HAVE_NEON
|
||||||
using V128 = uint8x16_t;
|
using V128 = uint8x16_t;
|
||||||
|
#elif SNAPPY_HAVE_RVV
|
||||||
|
using V128 = vuint8m1_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SNAPPY_RVV_1
|
||||||
|
#define VSETVL_E8M1 __riscv_vsetvl_e8m1
|
||||||
|
#define VLE8_V_U8M1 __riscv_vle8_v_u8m1
|
||||||
|
#define VSE8_V_U8M1 __riscv_vse8_v_u8m1
|
||||||
|
#define VRGATHER_VV_U8M1 __riscv_vrgather_vv_u8m1
|
||||||
|
#define VMV_V_X_U8M1 __riscv_vmv_v_x_u8m1
|
||||||
|
#elif SNAPPY_RVV_0_7
|
||||||
|
#define VSETVL_E8M1 vsetvl_e8m1
|
||||||
|
#define VLE8_V_U8M1 vle8_v_u8m1
|
||||||
|
#define VSE8_V_U8M1 vse8_v_u8m1
|
||||||
|
#define VRGATHER_VV_U8M1 vrgather_vv_u8m1
|
||||||
|
#define VMV_V_X_U8M1 vmv_v_x_u8m1
|
||||||
|
#endif
|
||||||
// Load 128 bits of integer data. `src` must be 16-byte aligned.
|
// Load 128 bits of integer data. `src` must be 16-byte aligned.
|
||||||
inline V128 V128_Load(const V128* src);
|
inline V128 V128_Load(const V128* src);
|
||||||
|
|
||||||
@@ -110,6 +131,32 @@ inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline V128 V128_DupChar(char c) { return vdupq_n_u8(c); }
|
inline V128 V128_DupChar(char c) { return vdupq_n_u8(c); }
|
||||||
|
|
||||||
|
#elif SNAPPY_HAVE_RVV
|
||||||
|
inline V128 V128_Load(const V128* src) {
|
||||||
|
size_t vl = VSETVL_E8M1(16);
|
||||||
|
return VLE8_V_U8M1(reinterpret_cast<const uint8_t*>(src), vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline V128 V128_LoadU(const V128* src) {
|
||||||
|
size_t vl = VSETVL_E8M1(16);
|
||||||
|
return VLE8_V_U8M1(reinterpret_cast<const uint8_t*>(src), vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void V128_StoreU(V128* dst, V128 val) {
|
||||||
|
size_t vl = VSETVL_E8M1(16);
|
||||||
|
VSE8_V_U8M1(reinterpret_cast<uint8_t*>(dst), val, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) {
|
||||||
|
size_t vl = VSETVL_E8M1(16);
|
||||||
|
return VRGATHER_VV_U8M1(input, shuffle_mask, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline V128 V128_DupChar(char c) {
|
||||||
|
size_t vl = VSETVL_E8M1(16);
|
||||||
|
return VMV_V_X_U8M1(static_cast<uint8_t>(c), vl);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
|
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
|
||||||
|
|
||||||
|
|||||||
100
snappy.cc
100
snappy.cc
@@ -281,6 +281,20 @@ inline char* IncrementalCopySlow(const char* src, char* op,
|
|||||||
// 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}. These byte index sequences are generated by
|
// 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}. These byte index sequences are generated by
|
||||||
// calling MakePatternMaskBytes(0, 6, index_sequence<16>()) and
|
// calling MakePatternMaskBytes(0, 6, index_sequence<16>()) and
|
||||||
// MakePatternMaskBytes(16, 6, index_sequence<16>()) respectively.
|
// MakePatternMaskBytes(16, 6, index_sequence<16>()) respectively.
|
||||||
|
|
||||||
|
// Selects the appropriate vector size based on the current architecture
|
||||||
|
// vuint8m1_t, RISC-V vector type with fixed 128-bit size
|
||||||
|
// (sizeof not used due to variable-length vector register in RVV)
|
||||||
|
#if defined(__SSE2__) || defined(SNAPPY_HAVE_SSSE3)
|
||||||
|
constexpr size_t kVectorSize = sizeof(V128); // __m128i
|
||||||
|
#elif defined(__ARM_NEON) || defined(SNAPPY_HAVE_NEON)
|
||||||
|
constexpr size_t kVectorSize = sizeof(uint8x16_t); // uint8x16_t
|
||||||
|
#elif defined(SNAPPY_HAVE_RVV) || defined(__riscv_vector)
|
||||||
|
constexpr size_t kVectorSize = 16; // vuint8m1_t
|
||||||
|
#else
|
||||||
|
#error "Unsupported architecture. Please define __SSE2__, __ARM_NEON, or SNAPPY_HAVE_RVV/__riscv_vector."
|
||||||
|
#endif
|
||||||
|
|
||||||
template <size_t... indexes>
|
template <size_t... indexes>
|
||||||
inline constexpr std::array<char, sizeof...(indexes)> MakePatternMaskBytes(
|
inline constexpr std::array<char, sizeof...(indexes)> MakePatternMaskBytes(
|
||||||
int index_offset, int pattern_size, index_sequence<indexes...>) {
|
int index_offset, int pattern_size, index_sequence<indexes...>) {
|
||||||
@@ -290,19 +304,17 @@ inline constexpr std::array<char, sizeof...(indexes)> MakePatternMaskBytes(
|
|||||||
// Computes the shuffle control mask bytes array for given pattern-sizes and
|
// Computes the shuffle control mask bytes array for given pattern-sizes and
|
||||||
// returns an array.
|
// returns an array.
|
||||||
template <size_t... pattern_sizes_minus_one>
|
template <size_t... pattern_sizes_minus_one>
|
||||||
inline constexpr std::array<std::array<char, sizeof(V128)>,
|
inline constexpr std::array<std::array<char, kVectorSize>,
|
||||||
sizeof...(pattern_sizes_minus_one)>
|
sizeof...(pattern_sizes_minus_one)>
|
||||||
MakePatternMaskBytesTable(int index_offset,
|
MakePatternMaskBytesTable(int index_offset,
|
||||||
index_sequence<pattern_sizes_minus_one...>) {
|
index_sequence<pattern_sizes_minus_one...>) {
|
||||||
return {
|
return {MakePatternMaskBytes(index_offset, pattern_sizes_minus_one + 1,
|
||||||
MakePatternMaskBytes(index_offset, pattern_sizes_minus_one + 1,
|
make_index_sequence<kVectorSize>())...};
|
||||||
make_index_sequence</*indexes=*/sizeof(V128)>())...};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is an array of shuffle control masks that can be used as the source
|
// This is an array of shuffle control masks that can be used as the source
|
||||||
// operand for PSHUFB to permute the contents of the destination XMM register
|
// operand for PSHUFB to permute the contents of the destination XMM register
|
||||||
// into a repeating byte pattern.
|
// into a repeating byte pattern.
|
||||||
alignas(16) constexpr std::array<std::array<char, sizeof(V128)>,
|
alignas(16) constexpr std::array<std::array<char, kVectorSize>,
|
||||||
16> pattern_generation_masks =
|
16> pattern_generation_masks =
|
||||||
MakePatternMaskBytesTable(
|
MakePatternMaskBytesTable(
|
||||||
/*index_offset=*/0,
|
/*index_offset=*/0,
|
||||||
@@ -313,7 +325,7 @@ alignas(16) constexpr std::array<std::array<char, sizeof(V128)>,
|
|||||||
// Basically, pattern_reshuffle_masks is a continuation of
|
// Basically, pattern_reshuffle_masks is a continuation of
|
||||||
// pattern_generation_masks. It follows that, pattern_reshuffle_masks is same as
|
// pattern_generation_masks. It follows that, pattern_reshuffle_masks is same as
|
||||||
// pattern_generation_masks for offsets 1, 2, 4, 8 and 16.
|
// pattern_generation_masks for offsets 1, 2, 4, 8 and 16.
|
||||||
alignas(16) constexpr std::array<std::array<char, sizeof(V128)>,
|
alignas(16) constexpr std::array<std::array<char, kVectorSize>,
|
||||||
16> pattern_reshuffle_masks =
|
16> pattern_reshuffle_masks =
|
||||||
MakePatternMaskBytesTable(
|
MakePatternMaskBytesTable(
|
||||||
/*index_offset=*/16,
|
/*index_offset=*/16,
|
||||||
@@ -329,6 +341,21 @@ static inline V128 LoadPattern(const char* src, const size_t pattern_size) {
|
|||||||
return V128_Shuffle(V128_LoadU(reinterpret_cast<const V128*>(src)),
|
return V128_Shuffle(V128_LoadU(reinterpret_cast<const V128*>(src)),
|
||||||
generation_mask);
|
generation_mask);
|
||||||
}
|
}
|
||||||
|
// vuint8m1_t cannot be used as an element of std::pair
|
||||||
|
#if SNAPPY_HAVE_RVV
|
||||||
|
#define LoadPatternAndReshuffleMask(src, pattern_size) \
|
||||||
|
V128 pattern = LoadPattern(src, pattern_size);\
|
||||||
|
V128 reshuffle_mask = V128_Load(reinterpret_cast<const V128*>(\
|
||||||
|
pattern_reshuffle_masks[pattern_size - 1].data()));
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Suppress -Wignored-attributes warning for __m128i in x86 SSE2 environment
|
||||||
|
// warning: ignoring attributes on template argument 'snappy::internal::V128' {aka '__vector(2) long long int'} [-Wignored-attributes]
|
||||||
|
// This occurs because __m128i has vector attributes (e.g., __attribute__((vector_size(16)))) that are ignored in template parameters.
|
||||||
|
#ifdef __SSE2__
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
||||||
|
#endif
|
||||||
|
|
||||||
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
|
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
|
||||||
static inline std::pair<V128 /* pattern */, V128 /* reshuffle_mask */>
|
static inline std::pair<V128 /* pattern */, V128 /* reshuffle_mask */>
|
||||||
@@ -345,7 +372,12 @@ LoadPatternAndReshuffleMask(const char* src, const size_t pattern_size) {
|
|||||||
pattern_reshuffle_masks[pattern_size - 1].data()));
|
pattern_reshuffle_masks[pattern_size - 1].data()));
|
||||||
return {pattern, reshuffle_mask};
|
return {pattern, reshuffle_mask};
|
||||||
}
|
}
|
||||||
|
// Restore original diagnostic state in x86 SSE2 environment
|
||||||
|
#ifdef __SSE2__
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
|
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
|
||||||
|
|
||||||
// Fallback for when we need to copy while extending the pattern, for example
|
// Fallback for when we need to copy while extending the pattern, for example
|
||||||
@@ -379,10 +411,14 @@ static inline bool Copy64BytesWithPatternExtension(char* dst, size_t offset) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
#if SNAPPY_HAVE_RVV
|
||||||
|
LoadPatternAndReshuffleMask(dst - offset, offset)
|
||||||
|
#else
|
||||||
auto pattern_and_reshuffle_mask =
|
auto pattern_and_reshuffle_mask =
|
||||||
LoadPatternAndReshuffleMask(dst - offset, offset);
|
LoadPatternAndReshuffleMask(dst - offset, offset);
|
||||||
V128 pattern = pattern_and_reshuffle_mask.first;
|
V128 pattern = pattern_and_reshuffle_mask.first;
|
||||||
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
|
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
|
||||||
|
#endif
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
V128_StoreU(reinterpret_cast<V128*>(dst + 16 * i), pattern);
|
V128_StoreU(reinterpret_cast<V128*>(dst + 16 * i), pattern);
|
||||||
pattern = V128_Shuffle(pattern, reshuffle_mask);
|
pattern = V128_Shuffle(pattern, reshuffle_mask);
|
||||||
@@ -490,11 +526,14 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
|
|||||||
// Typically, the op_limit is the gating factor so try to simplify the loop
|
// Typically, the op_limit is the gating factor so try to simplify the loop
|
||||||
// based on that.
|
// based on that.
|
||||||
if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) {
|
if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) {
|
||||||
|
#if SNAPPY_HAVE_RVV
|
||||||
|
LoadPatternAndReshuffleMask(src, pattern_size);
|
||||||
|
#else
|
||||||
auto pattern_and_reshuffle_mask =
|
auto pattern_and_reshuffle_mask =
|
||||||
LoadPatternAndReshuffleMask(src, pattern_size);
|
LoadPatternAndReshuffleMask(src, pattern_size);
|
||||||
V128 pattern = pattern_and_reshuffle_mask.first;
|
V128 pattern = pattern_and_reshuffle_mask.first;
|
||||||
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
|
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
|
||||||
|
#endif
|
||||||
// There is at least one, and at most four 16-byte blocks. Writing four
|
// There is at least one, and at most four 16-byte blocks. Writing four
|
||||||
// conditionals instead of a loop allows FDO to layout the code with
|
// conditionals instead of a loop allows FDO to layout the code with
|
||||||
// respect to the actual probabilities of each length.
|
// respect to the actual probabilities of each length.
|
||||||
@@ -517,11 +556,14 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
|
|||||||
}
|
}
|
||||||
char* const op_end = buf_limit - 15;
|
char* const op_end = buf_limit - 15;
|
||||||
if (SNAPPY_PREDICT_TRUE(op < op_end)) {
|
if (SNAPPY_PREDICT_TRUE(op < op_end)) {
|
||||||
|
#if SNAPPY_HAVE_RVV
|
||||||
|
LoadPatternAndReshuffleMask(src, pattern_size);
|
||||||
|
#else
|
||||||
auto pattern_and_reshuffle_mask =
|
auto pattern_and_reshuffle_mask =
|
||||||
LoadPatternAndReshuffleMask(src, pattern_size);
|
LoadPatternAndReshuffleMask(src, pattern_size);
|
||||||
V128 pattern = pattern_and_reshuffle_mask.first;
|
V128 pattern = pattern_and_reshuffle_mask.first;
|
||||||
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
|
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
|
||||||
|
#endif
|
||||||
// This code path is relatively cold however so we save code size
|
// This code path is relatively cold however so we save code size
|
||||||
// by avoiding unrolling and vectorizing.
|
// by avoiding unrolling and vectorizing.
|
||||||
//
|
//
|
||||||
@@ -1247,13 +1289,41 @@ void MemCopy64(char* dst, const void* src, size_t size) {
|
|||||||
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst) + 1, data);
|
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst) + 1, data);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
std::memmove(dst, src, kShortMemCopy);
|
#ifdef SNAPPY_HAVE_RVV
|
||||||
// Profiling shows that nearly all copies are short.
|
uint8_t* dst_u8 = (uint8_t*)dst;
|
||||||
if (SNAPPY_PREDICT_FALSE(size > kShortMemCopy)) {
|
const uint8_t* src_u8 = (const uint8_t*)src;
|
||||||
std::memmove(dst + kShortMemCopy,
|
if (src_u8 < dst_u8 && dst_u8 < src_u8 + size) { //overlap bwd copy
|
||||||
static_cast<const uint8_t*>(src) + kShortMemCopy,
|
size_t offset = size;
|
||||||
64 - kShortMemCopy);
|
while (offset > 0) {
|
||||||
|
size_t vl = VSETVL_E8M1(offset);
|
||||||
|
offset -= vl;
|
||||||
|
vuint8m1_t vec = VLE8_V_U8M1(src_u8 + offset, vl);
|
||||||
|
VSE8_V_U8M1(dst_u8 + offset, vec, vl);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
size_t vl = VSETVL_E8M1(size);
|
||||||
|
if (vl < size) { // if size >vl,use the max_vlen copy
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < size) {
|
||||||
|
vl = VSETVL_E8M1(size - offset);
|
||||||
|
vuint8m1_t vec = VLE8_V_U8M1(src_u8 + offset, vl);
|
||||||
|
VSE8_V_U8M1(dst_u8 + offset, vec, vl);
|
||||||
|
offset += vl;
|
||||||
|
}
|
||||||
|
} else { // copy the leaft
|
||||||
|
vuint8m1_t vec = VLE8_V_U8M1(src_u8, vl);
|
||||||
|
VSE8_V_U8M1(dst_u8, vec, vl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::memmove(dst, src, kShortMemCopy);
|
||||||
|
//Profiling shows that nearly all copies are short.
|
||||||
|
if (SNAPPY_PREDICT_FALSE(size > kShortMemCopy)) {
|
||||||
|
std::memmove(dst + kShortMemCopy,
|
||||||
|
static_cast<const uint8_t*>(src) + kShortMemCopy,
|
||||||
|
64 - kShortMemCopy);}
|
||||||
|
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user