Skip to content

Commit

Permalink
Add some lightweight encodings
Browse files Browse the repository at this point in the history
  • Loading branch information
willdealtry committed Nov 5, 2024
1 parent b0e80e3 commit a328636
Show file tree
Hide file tree
Showing 25 changed files with 1,438 additions and 16 deletions.
10 changes: 5 additions & 5 deletions cpp/arcticdb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ set(arcticdb_srcs
version/symbol_list.cpp
version/version_map_batch_methods.cpp
storage/s3/ec2_utils.cpp
util/buffer_holder.cpp)
util/buffer_holder.cpp codec/frequency_encoding.hpp codec/constant_encoding.hpp codec/statistics.hpp codec/fastlanes_common.hpp codec/delta.hpp codec/ffor.hpp codec/bitpack_fused.hpp)

add_library(arcticdb_core_object OBJECT ${arcticdb_srcs})

Expand Down Expand Up @@ -653,7 +653,7 @@ set (arcticdb_core_libraries
arcticdb_proto
xxHash::xxHash
prometheus-cpp::push
prometheus-cpp::pull
#prometheus-cpp::pull
unordered_dense::unordered_dense
${standard_libraries}
fmt::fmt
Expand Down Expand Up @@ -872,6 +872,7 @@ else()
${CMAKE_COMMAND} -E copy $<TARGET_FILE:arcticdb_ext> ${CMAKE_INSTALL_PREFIX})
endif()


## Unit Tests ##
if(${TEST})
unset(Python_USE_STATIC_LIBS)
Expand Down Expand Up @@ -956,12 +957,11 @@ if(${TEST})
version/test/version_map_model.hpp
python/python_handlers.cpp
storage/test/common.hpp
version/test/test_sort_index.cpp)
version/test/test_sort_index.cpp codec/test/test_frequency_encoding.cpp codec/test/test_constant_encoding.cpp codec/test/encoding_test_common.hpp codec/test/test_stats.cpp codec/test/test_ffor.cpp codec/test/test_fused_bitpack.cpp)

set(EXECUTABLE_PERMS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # 755

add_executable(test_unit_arcticdb ${unit_test_srcs})

install(TARGETS test_unit_arcticdb RUNTIME
DESTINATION .
PERMISSIONS ${EXECUTABLE_PERMS}
Expand Down Expand Up @@ -1063,7 +1063,7 @@ if(${TEST})
util/test/rapidcheck_string_pool.cpp
util/test/rapidcheck_main.cpp
util/test/rapidcheck_lru_cache.cpp
version/test/rapidcheck_version_map.cpp)
version/test/rapidcheck_version_map.cpp codec/test/rapidcheck_frequency_encoding.cpp)

add_executable(arcticdb_rapidcheck_tests ${rapidcheck_srcs})
install(TARGETS arcticdb_rapidcheck_tests RUNTIME
Expand Down
150 changes: 150 additions & 0 deletions cpp/arcticdb/codec/bitpack_fused.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include <cstdint>
#include <cstddef>


#include <arcticdb/codec/fastlanes_common.hpp>
#include <arcticdb/log/log.hpp>

namespace arcticdb {


template<typename T, size_t bit_width>
constexpr T construct_mask() {
if constexpr (bit_width == type_bits<T>())
return T(-1);
else
return (T(1) << bit_width) - 1;
}

template<typename T, size_t width>
struct BitPackHelper {
static constexpr size_t bit_width = width;
static constexpr size_t num_bits = Helper<T>::num_bits;
static constexpr size_t num_lanes = Helper<T>::num_lanes;
static_assert(bit_width <= num_bits);

static constexpr T mask = construct_mask<T, bit_width>();

static constexpr size_t remaining_bits(size_t row) {
return ((row + 1) * bit_width) % num_bits;
};

static constexpr size_t current_bits(size_t row) {
return bit_width - remaining_bits(row);
}

static constexpr size_t current_word (size_t row) {
return (row * bit_width) / num_bits;
}

static constexpr size_t next_word (size_t row) {
return ((row + 1) * bit_width) / num_bits;
}

static constexpr bool at_end(size_t row) {
return next_word(row) > current_word(row);
}

static constexpr size_t shift(size_t row) {
return (row * bit_width) % num_bits;
}
};

static_assert(BitPackHelper<uint8_t, 3>::mask == 7);
static_assert(BitPackHelper<uint8_t, 3>::at_end(2));
static_assert(!BitPackHelper<uint8_t, 3>::at_end(3));

template<typename T, typename p, size_t bit_width, typename Kernel>
void bitpack_lane(
const size_t lane,
const T* __restrict in,
T* __restrict out,
Kernel& kernel) {
static constexpr auto num_bits = p::num_bits;
static constexpr auto num_lanes = p::num_lanes;
static constexpr auto mask = p::mask;

T tmp = 0;
loop<T, num_bits>([lane, in, out, &tmp, &kernel](auto r) {
constexpr size_t row = r;
size_t idx = index(row, lane);
T src = kernel(in[idx]);
src &= mask;

if constexpr(row == 0) {
tmp = src;
} else {
tmp |= src << ((row * bit_width) & (num_bits - 1));
}

if constexpr(p::at_end(row)) {
constexpr auto current_word = p::current_word(row);
constexpr auto remaining_bits = p::remaining_bits(row);
out[num_lanes * current_word + lane] = tmp;
//log::version().info("Writing to index {}", num_bits * current_word + lane);
tmp = src >> (bit_width - remaining_bits);
}
});
}

template<typename T, size_t bit_width>
struct BitPackFused : public BitPackHelper<T, bit_width> {
using Parent = BitPackHelper<T, bit_width>;
static constexpr auto num_lanes = Parent::num_lanes;

template <typename Kernel>
static void go(const T *__restrict in, T *__restrict out, Kernel &&kernel) {
for(auto lane = 0UL; lane < num_lanes; ++lane) {
bitpack_lane<T, Parent, bit_width, Kernel>(lane, in, out, kernel);
};
}
};

template <typename T, typename Parent, size_t bit_width, typename Kernel>
void bitunpack_lane(
size_t lane,
const T *__restrict in,
T *__restrict out,
Kernel &kernel) {
static constexpr auto num_bits = Parent::num_bits;
static constexpr auto num_lanes = Parent::num_lanes;
static constexpr auto mask = Parent::mask;
using p = Parent;

T src = in[lane];
T tmp;
loop<T, num_bits>([lane, in, out, &tmp, &kernel, &src](auto row) {
constexpr auto shift = p::shift(row);
if constexpr (p::at_end(row)) {
constexpr auto current_bits = p::current_bits(row);
constexpr auto current_bits_mask = construct_mask<T, current_bits>();
tmp = (src >> shift) & current_bits_mask;
if constexpr (p::next_word(row) < bit_width) {
constexpr auto next_word = p::next_word(row);
constexpr auto remaining_bits_mask = construct_mask<T, p::remaining_bits(row)>();
src = in[num_lanes * next_word + lane];
tmp |= (src & remaining_bits_mask) << current_bits;
}
} else {
tmp = (src >> shift) & mask;
}

size_t idx = index(row, lane);
out[idx] = kernel(tmp);
});
}

template<typename T, size_t bit_width>
struct BitUnpackFused : public BitPackHelper<T, bit_width> {
using Parent = BitPackHelper<T, bit_width>;
static constexpr auto num_lanes = Parent::num_lanes;

template<typename Kernel>
static void go(const T *__restrict in, T *__restrict out, Kernel &&kernel) {
for(auto lane = 0UL; lane < num_lanes; ++lane) {
bitunpack_lane<T, Parent, bit_width, Kernel>(lane, in, out, kernel);
}
}
};

} // namespace arcticdb
7 changes: 7 additions & 0 deletions cpp/arcticdb/codec/codec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,13 @@ void add_bitmagic_compressed_size(
}
}

bm::serializer<bm::bvector<> >::buffer encode_bitmap(const util::BitSet& sparse_map) {
bm::serializer<bm::bvector<> > bvs;
bm::serializer<bm::bvector<> >::buffer buffer;
bvs.serialize(sparse_map, buffer);
return buffer;
}

/// @brief Write the sparse map to the out buffer
/// Bitmagic achieves the theoretical best compression for booleans. Adding additional encoding (lz4, zstd, etc...)
/// will not improve anything and in fact it might worsen the encoding.
Expand Down
2 changes: 2 additions & 0 deletions cpp/arcticdb/codec/codec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ HashedValue get_segment_hash(Segment& seg);

SegmentDescriptorImpl read_segment_descriptor(const uint8_t*& data);

bm::serializer<bm::bvector<> >::buffer encode_bitmap(const util::BitSet& sparse_map);

} // namespace arcticdb

#define ARCTICDB_SEGMENT_ENCODER_H_
Expand Down
57 changes: 57 additions & 0 deletions cpp/arcticdb/codec/constant_encoding.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once

#include <arcticdb/log/log.hpp>
#include <arcticdb/util/preconditions.hpp>

#include <cstdint>

namespace arcticdb {
template<typename T>
struct ConstantEncoding {

#pragma pack(push, 1)
struct Data {
uint64_t size_;
T value_;
};
#pragma pack(pop)

std::optional<size_t> max_required_bytes(const T* data_in, size_t num_rows) {
if (num_rows == 0)
return 0;

const auto *pos = data_in;
const auto *end = pos + num_rows;
T first = *pos;
++pos;
do {
if (*pos != first)
return std::nullopt;

++pos;
} while (pos != end);

return sizeof(Data);
}

size_t encode(const T *data_in, size_t num_rows, uint8_t *data_out) {
if (num_rows == 0)
return 0;

auto *state = reinterpret_cast<Data*>(data_out);
state->size_ = num_rows;
state->value_ = *data_in;
return sizeof(Data);
}

size_t decode(const uint8_t *data_in, size_t bytes, T *data_out) {
util::check(bytes == sizeof(Data), "Not enough bytes in constant encoding");

const auto *state = reinterpret_cast<const Data*>(data_in);
auto *target = data_out;
auto *target_end = target + state->size_;
std::fill(target, target_end, state->value_);
return state->size_;
}
};
}
37 changes: 37 additions & 0 deletions cpp/arcticdb/codec/delta.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* Copyright 2023 Man Group Operations Limited
*
* Use of this software is governed by the Business Source License 1.1 included in the file licenses/BSL.txt.
*
* As of the Change Date specified in that file, in accordance with the Business Source License, use of this software will be governed by the Apache License, version 2.0.
*/

#include <cstdint>
#include <cstddef>
#include <limits>

#include <arcticdb/codec/fastlanes_common.hpp>

namespace arcticdb {

template <typename T>
void rsum(const uint8_t *__restrict a_in_p, uint8_t* __restrict a_out_p, const uint8_t* __restrict a_base_p) {
auto out = reinterpret_cast<uint8_t *>(a_out_p);
const auto in = reinterpret_cast<const uint8_t *>(a_in_p);
const auto base = reinterpret_cast<const uint8_t *>(a_base_p);

for (auto lane = 0U; lane < Helper<T>::num_lanes; ++lane) {
uint8_t register_0;
uint8_t tmp;
tmp = base[lane];
loop<T, Helper<T>::num_bits>([lane, base, in, &tmp, &out, &register_0](auto j) {
register_0 = in[index(j, lane)];
tmp = tmp + register_0;
out[index(j, lane)] = tmp;
});
}
}




} // nam
68 changes: 68 additions & 0 deletions cpp/arcticdb/codec/fastlanes_common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Copyright 2024 Man Group Operations Limited
*
* Use of this software is governed by the Business Source License 1.1 included in the file licenses/BSL.txt.
*
* As of the Change Date specified in that file, in accordance with the Business Source License, use of this software will be governed by the Apache License, version 2.0.
*/

#pragma once

#include <utility>
#include <limits>

namespace arcticdb {
namespace detail {

constexpr std::size_t FastLanesWidth = 1024;

template<class T, T... inds, class F>
constexpr void loop(std::integer_sequence<T, inds...>, F &&f) {
(f(std::integral_constant<T, inds>{}), ...);
}
}

template<class T, T count, class F>
constexpr void loop(F &&f) {
detail::loop(std::make_integer_sequence<T, count>{}, std::forward<F>(f));
}

template<typename T>
constexpr size_t type_bits() {
return sizeof(T) * std::numeric_limits<uint8_t>::digits;
}

template<typename T>
struct Helper {
static constexpr size_t num_bits = type_bits<T>();
static constexpr size_t register_width = detail::FastLanesWidth;
static constexpr size_t num_lanes = register_width / num_bits;
};

static_assert(Helper<uint64_t>::num_lanes == 16);
static_assert(Helper<uint8_t>::num_lanes == 128);
static_assert(Helper<uint16_t>::num_bits == 16);

constexpr std::array<size_t, 8> FL_ORDER = { 0, 4, 2, 6, 1, 5, 3, 7 };

constexpr size_t transposed_index(size_t index) {
auto lane = index % 16;
auto order = (index / 16) % 8;
auto row = index / 128;

return (lane * 64) + (FL_ORDER[order] * 8) + row;
}

constexpr size_t index(size_t row, size_t lane) {
const auto o = row / 8;
const auto s = row % 8;
return (FL_ORDER[o] * 16) + (s * 128) + lane;
}

static_assert(transposed_index(1) == 64);
static_assert(transposed_index(57) == 624);
static_assert(transposed_index(1022) == 959);

static_assert(index(1, 0) == 128);
static_assert(transposed_index(57) == 624);
static_assert(transposed_index(1022) == 959);
} // namespace arcticdb
Loading

0 comments on commit a328636

Please sign in to comment.