blob: 1f7ae1b1561c57420667e241150ccdec35cf3bf1 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef ENCODING_ALP_SIMDE_H
#define ENCODING_ALP_SIMDE_H
#include <stdint.h>
#include "alp_traits.h"
#include "simde/x86/avx2.h"
#include "simde/x86/avx512/cvt.h"
#include "simde/x86/avx512/cvtt.h"
#include "simde/x86/sse4.1.h"
namespace storage {
namespace alp {
template <typename T>
struct AlpSimdKernel;
template <>
struct AlpSimdKernel<float> {
static AlpStatus EncodeValues(const float* values, uint32_t count,
uint8_t factor, uint8_t exponent,
int32_t* encoded, uint8_t* bitmap,
float* exceptions,
uint32_t* exception_count) {
typedef AlpTypeTraits<float> Traits;
const float scale = ALP_FLOAT_EXP[exponent] * ALP_FLOAT_FRAC[factor];
const float factor_value = static_cast<float>(ALP_FACT[factor]);
const float frac_value = ALP_FLOAT_FRAC[exponent];
const simde__m256 vscale = simde_mm256_set1_ps(scale);
const simde__m256 vmagic = simde_mm256_set1_ps(ALP_FLOAT_MAGIC);
const simde__m256 vfactor = simde_mm256_set1_ps(factor_value);
const simde__m256 vfrac = simde_mm256_set1_ps(frac_value);
const simde__m256 vlower =
simde_mm256_set1_ps(ALP_FLOAT_ENCODING_LOWER_LIMIT);
const simde__m256 vupper =
simde_mm256_set1_ps(ALP_FLOAT_ENCODING_UPPER_LIMIT);
const simde__m256i vnegzero =
simde_mm256_set1_epi32(static_cast<int32_t>(0x80000000u));
const simde__m256i vall = simde_mm256_set1_epi32(-1);
uint32_t ex_count = 0;
uint32_t i = 0;
for (; i + 7 < count; i += 8) {
const simde__m256 v = simde_mm256_loadu_ps(values + i);
const simde__m256 scaled = simde_mm256_mul_ps(v, vscale);
const simde__m256 rounded =
simde_mm256_sub_ps(simde_mm256_add_ps(scaled, vmagic), vmagic);
const simde__m256i vi = simde_mm256_cvttps_epi32(rounded);
const simde__m256 back = simde_mm256_mul_ps(
simde_mm256_mul_ps(simde_mm256_cvtepi32_ps(vi), vfactor),
vfrac);
const simde__m256 in_range = simde_mm256_and_ps(
simde_mm256_cmp_ps(scaled, vlower, SIMDE_CMP_GE_OQ),
simde_mm256_cmp_ps(scaled, vupper, SIMDE_CMP_LE_OQ));
const simde__m256 rounded_in_range = simde_mm256_and_ps(
simde_mm256_cmp_ps(rounded, vlower, SIMDE_CMP_GE_OQ),
simde_mm256_cmp_ps(rounded, vupper, SIMDE_CMP_LE_OQ));
const simde__m256 ordered =
simde_mm256_cmp_ps(v, v, SIMDE_CMP_ORD_Q);
const simde__m256 equal =
simde_mm256_cmp_ps(back, v, SIMDE_CMP_EQ_OQ);
const simde__m256i is_negzero =
simde_mm256_cmpeq_epi32(simde_mm256_castps_si256(v), vnegzero);
const simde__m256 not_negzero = simde_mm256_castsi256_ps(
simde_mm256_xor_si256(is_negzero, vall));
const simde__m256 ok = simde_mm256_and_ps(
simde_mm256_and_ps(in_range, rounded_in_range),
simde_mm256_and_ps(ordered,
simde_mm256_and_ps(equal, not_negzero)));
const int mask = simde_mm256_movemask_ps(ok);
simde_mm256_storeu_si256(
reinterpret_cast<simde__m256i*>(encoded + i), vi);
for (int lane = 0; lane < 8; ++lane) {
if ((mask & (1 << lane)) == 0) {
const uint32_t index = i + static_cast<uint32_t>(lane);
bitmap[index >> 3] |=
static_cast<uint8_t>(1u << (index & 7u));
exceptions[ex_count++] = values[index];
encoded[index] = 0;
}
}
}
for (; i < count; ++i) {
int32_t value = 0;
if (!Traits::EncodeValue(values[i], factor, exponent, &value) ||
!Traits::BitwiseEqual(
Traits::DecodeValue(value, factor, exponent), values[i])) {
bitmap[i >> 3] |= static_cast<uint8_t>(1u << (i & 7u));
exceptions[ex_count++] = values[i];
} else {
encoded[i] = value;
}
}
*exception_count = ex_count;
return ALP_OK;
}
static AlpStatus DecodeValues(const int32_t* encoded, uint32_t count,
uint8_t factor, uint8_t exponent,
const uint8_t* bitmap,
const float* exceptions,
uint32_t exception_count, float* out) {
typedef AlpTypeTraits<float> Traits;
const simde__m256 vfactor =
simde_mm256_set1_ps(static_cast<float>(ALP_FACT[factor]));
const simde__m256 vfrac = simde_mm256_set1_ps(ALP_FLOAT_FRAC[exponent]);
uint32_t i = 0;
for (; i + 7 < count; i += 8) {
const simde__m256i vi = simde_mm256_loadu_si256(
reinterpret_cast<const simde__m256i*>(encoded + i));
const simde__m256 value = simde_mm256_mul_ps(
simde_mm256_mul_ps(simde_mm256_cvtepi32_ps(vi), vfactor),
vfrac);
simde_mm256_storeu_ps(out + i, value);
}
for (; i < count; ++i) {
out[i] = Traits::DecodeValue(encoded[i], factor, exponent);
}
uint32_t exception_index = 0;
for (uint32_t index = 0; index < count; ++index) {
if (bitmap != NULL &&
(bitmap[index >> 3] &
static_cast<uint8_t>(1u << (index & 7u))) != 0) {
if (exception_index >= exception_count) {
return ALP_MALFORMED;
}
out[index] = exceptions[exception_index++];
}
}
return exception_index == exception_count ? ALP_OK : ALP_MALFORMED;
}
};
template <>
struct AlpSimdKernel<double> {
static AlpStatus EncodeValues(const double* values, uint32_t count,
uint8_t factor, uint8_t exponent,
int64_t* encoded, uint8_t* bitmap,
double* exceptions,
uint32_t* exception_count) {
typedef AlpTypeTraits<double> Traits;
const double scale = ALP_DOUBLE_EXP[exponent] * ALP_DOUBLE_FRAC[factor];
const double factor_value = static_cast<double>(ALP_FACT[factor]);
const double frac_value = ALP_DOUBLE_FRAC[exponent];
const simde__m128d vscale = simde_mm_set1_pd(scale);
const simde__m128d vmagic = simde_mm_set1_pd(ALP_DOUBLE_MAGIC);
const simde__m128d vfactor = simde_mm_set1_pd(factor_value);
const simde__m128d vfrac = simde_mm_set1_pd(frac_value);
const simde__m128d vlower =
simde_mm_set1_pd(ALP_DOUBLE_ENCODING_LOWER_LIMIT);
const simde__m128d vupper =
simde_mm_set1_pd(ALP_DOUBLE_ENCODING_UPPER_LIMIT);
const simde__m128i vnegzero =
simde_mm_set1_epi64x(static_cast<int64_t>(0x8000000000000000ULL));
const simde__m128i vall = simde_mm_set1_epi32(-1);
uint32_t ex_count = 0;
uint32_t i = 0;
for (; i + 1 < count; i += 2) {
const simde__m128d v = simde_mm_loadu_pd(values + i);
const simde__m128d scaled = simde_mm_mul_pd(v, vscale);
const simde__m128d rounded =
simde_mm_sub_pd(simde_mm_add_pd(scaled, vmagic), vmagic);
const simde__m128i vi = simde_mm_cvttpd_epi64(rounded);
const simde__m128d back = simde_mm_mul_pd(
simde_mm_mul_pd(simde_mm_cvtepi64_pd(vi), vfactor), vfrac);
const simde__m128d in_range = simde_mm_and_pd(
simde_mm_cmp_pd(scaled, vlower, SIMDE_CMP_GE_OQ),
simde_mm_cmp_pd(scaled, vupper, SIMDE_CMP_LE_OQ));
const simde__m128d rounded_in_range = simde_mm_and_pd(
simde_mm_cmp_pd(rounded, vlower, SIMDE_CMP_GE_OQ),
simde_mm_cmp_pd(rounded, vupper, SIMDE_CMP_LE_OQ));
const simde__m128d ordered = simde_mm_cmp_pd(v, v, SIMDE_CMP_ORD_Q);
const simde__m128d equal =
simde_mm_cmp_pd(back, v, SIMDE_CMP_EQ_OQ);
const simde__m128i is_negzero =
simde_mm_cmpeq_epi64(simde_mm_castpd_si128(v), vnegzero);
const simde__m128d not_negzero =
simde_mm_castsi128_pd(simde_mm_xor_si128(is_negzero, vall));
const simde__m128d ok = simde_mm_and_pd(
simde_mm_and_pd(in_range, rounded_in_range),
simde_mm_and_pd(ordered, simde_mm_and_pd(equal, not_negzero)));
const int mask = simde_mm_movemask_pd(ok);
simde_mm_storeu_si128(reinterpret_cast<simde__m128i*>(encoded + i),
vi);
for (int lane = 0; lane < 2; ++lane) {
if ((mask & (1 << lane)) == 0) {
const uint32_t index = i + static_cast<uint32_t>(lane);
bitmap[index >> 3] |=
static_cast<uint8_t>(1u << (index & 7u));
exceptions[ex_count++] = values[index];
encoded[index] = 0;
}
}
}
for (; i < count; ++i) {
int64_t value = 0;
if (!Traits::EncodeValue(values[i], factor, exponent, &value) ||
!Traits::BitwiseEqual(
Traits::DecodeValue(value, factor, exponent), values[i])) {
bitmap[i >> 3] |= static_cast<uint8_t>(1u << (i & 7u));
exceptions[ex_count++] = values[i];
} else {
encoded[i] = value;
}
}
*exception_count = ex_count;
return ALP_OK;
}
static AlpStatus DecodeValues(const int64_t* encoded, uint32_t count,
uint8_t factor, uint8_t exponent,
const uint8_t* bitmap,
const double* exceptions,
uint32_t exception_count, double* out) {
typedef AlpTypeTraits<double> Traits;
const simde__m128d vfactor =
simde_mm_set1_pd(static_cast<double>(ALP_FACT[factor]));
const simde__m128d vfrac = simde_mm_set1_pd(ALP_DOUBLE_FRAC[exponent]);
uint32_t i = 0;
for (; i + 1 < count; i += 2) {
const simde__m128i vi = simde_mm_loadu_si128(
reinterpret_cast<const simde__m128i*>(encoded + i));
const simde__m128d value = simde_mm_mul_pd(
simde_mm_mul_pd(simde_mm_cvtepi64_pd(vi), vfactor), vfrac);
simde_mm_storeu_pd(out + i, value);
}
for (; i < count; ++i) {
out[i] = Traits::DecodeValue(encoded[i], factor, exponent);
}
uint32_t exception_index = 0;
for (uint32_t index = 0; index < count; ++index) {
if (bitmap != NULL &&
(bitmap[index >> 3] &
static_cast<uint8_t>(1u << (index & 7u))) != 0) {
if (exception_index >= exception_count) {
return ALP_MALFORMED;
}
out[index] = exceptions[exception_index++];
}
}
return exception_index == exception_count ? ALP_OK : ALP_MALFORMED;
}
};
template <typename T>
inline AlpStatus AlpSimdEncodeValues(
const T* values, uint32_t count, uint8_t factor, uint8_t exponent,
typename AlpTypeTraits<T>::Encoded* encoded, uint8_t* bitmap, T* exceptions,
uint32_t* exception_count) {
return AlpSimdKernel<T>::EncodeValues(values, count, factor, exponent,
encoded, bitmap, exceptions,
exception_count);
}
template <typename T>
inline AlpStatus AlpSimdDecodeValues(
const typename AlpTypeTraits<T>::Encoded* encoded, uint32_t count,
uint8_t factor, uint8_t exponent, const uint8_t* bitmap,
const T* exceptions, uint32_t exception_count, T* out) {
return AlpSimdKernel<T>::DecodeValues(encoded, count, factor, exponent,
bitmap, exceptions, exception_count,
out);
}
template <typename Unsigned>
struct AlpSimdUnpackKernel;
template <>
struct AlpSimdUnpackKernel<uint32_t> {
static bool Unpack(const uint8_t* body, uint32_t count, uint8_t bit_width,
uint32_t* out) {
if (bit_width == 0) {
for (uint32_t i = 0; i < count; ++i) {
out[i] = 0;
}
return true;
}
const uint64_t mask64 =
bit_width >= 32 ? static_cast<uint64_t>(0xFFFFFFFFu)
: ((static_cast<uint64_t>(1) << bit_width) - 1);
const simde__m256i mask =
simde_mm256_set1_epi64x(static_cast<int64_t>(mask64));
const simde__m256i perm =
simde_mm256_setr_epi32(0, 2, 4, 6, 0, 0, 0, 0);
for (uint32_t i = 0; i + 3 < count; i += 4) {
int32_t byte_offsets[4];
int64_t bit_offsets[4];
for (int lane = 0; lane < 4; ++lane) {
const uint64_t bit_pos =
static_cast<uint64_t>(i + lane) * bit_width;
byte_offsets[lane] = static_cast<int32_t>(bit_pos >> 3);
bit_offsets[lane] = static_cast<int64_t>(bit_pos & 7u);
}
const simde__m128i indices = simde_mm_loadu_si128(
reinterpret_cast<const simde__m128i*>(byte_offsets));
const simde__m256i windows = simde_mm256_i32gather_epi64(
reinterpret_cast<const int64_t*>(body), indices, 1);
const simde__m256i shifts = simde_mm256_loadu_si256(
reinterpret_cast<const simde__m256i*>(bit_offsets));
const simde__m256i shifted =
simde_mm256_srlv_epi64(windows, shifts);
const simde__m256i masked = simde_mm256_and_si256(shifted, mask);
const simde__m256i compact =
simde_mm256_permutevar8x32_epi32(masked, perm);
simde_mm_storeu_si128(reinterpret_cast<simde__m128i*>(out + i),
simde_mm256_castsi256_si128(compact));
}
return true;
}
};
template <>
struct AlpSimdUnpackKernel<uint64_t> {
static bool Unpack(const uint8_t* body, uint32_t count, uint8_t bit_width,
uint64_t* out) {
if (bit_width == 0) {
for (uint32_t i = 0; i < count; ++i) {
out[i] = 0;
}
return true;
}
const uint64_t mask64 =
bit_width >= 64 ? ~static_cast<uint64_t>(0)
: ((static_cast<uint64_t>(1) << bit_width) - 1);
const simde__m256i mask =
simde_mm256_set1_epi64x(static_cast<int64_t>(mask64));
for (uint32_t i = 0; i + 3 < count; i += 4) {
int32_t byte_offsets[4];
int64_t bit_offsets[4];
for (int lane = 0; lane < 4; ++lane) {
const uint64_t bit_pos =
static_cast<uint64_t>(i + lane) * bit_width;
byte_offsets[lane] = static_cast<int32_t>(bit_pos >> 3);
bit_offsets[lane] = static_cast<int64_t>(bit_pos & 7u);
}
const simde__m128i indices = simde_mm_loadu_si128(
reinterpret_cast<const simde__m128i*>(byte_offsets));
const simde__m256i windows = simde_mm256_i32gather_epi64(
reinterpret_cast<const int64_t*>(body), indices, 1);
const simde__m256i shifts = simde_mm256_loadu_si256(
reinterpret_cast<const simde__m256i*>(bit_offsets));
const simde__m256i shifted =
simde_mm256_srlv_epi64(windows, shifts);
const simde__m256i masked = simde_mm256_and_si256(shifted, mask);
simde_mm256_storeu_si256(reinterpret_cast<simde__m256i*>(out + i),
masked);
}
return true;
}
};
template <typename Unsigned>
inline bool AlpSimdUnpackBits(const uint8_t* body, uint32_t count,
uint8_t bit_width, Unsigned* out) {
return AlpSimdUnpackKernel<Unsigned>::Unpack(body, count, bit_width, out);
}
} // namespace alp
} // namespace storage
#endif // ENCODING_ALP_SIMDE_H