Skip to content

Commit

Permalink
[struct_pack] add document, bench, example for fast_varint (#503)
Browse files Browse the repository at this point in the history
* improve varint performance

* add bench for fast_var_int

* add document & example for fast_varint
  • Loading branch information
poor-circle authored Nov 24, 2023
1 parent c8dbcf6 commit dc10d03
Show file tree
Hide file tree
Showing 19 changed files with 233 additions and 94 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ More examples [here](https://github.com/alibaba/yalantinglibs/tree/main/src/coro

Based on compile-time reflection, very easy to use, high performance serialization library, struct_pack is a header only library, it is used by coro_rpc now.

Only one line code to finish serialization and deserialization, 2-50x faster than protobuf.
Only one line code to finish serialization and deserialization, 2-20x faster than protobuf.

### quick example
```cpp
Expand Down
37 changes: 25 additions & 12 deletions include/ylt/struct_pack/calculate_size.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,21 @@ constexpr bool STRUCT_PACK_INLINE has_64bits_varint() {
}
}

template <uint64_t parent_tag, typename Arg>
template <uint64_t parent_tag, typename Arg, typename... Args>
constexpr std::size_t STRUCT_PACK_INLINE get_int_len() {
if (has_64bits_varint<parent_tag, Arg, Args...>()) {
return 8;
}
else {
return 4;
}
}

template <uint64_t parent_tag, typename Arg, typename unsigned_t,
typename signed_t>
constexpr void STRUCT_PACK_INLINE get_fast_varint_width_impl(
const Arg &item, int &non_zero_cnt32, int &non_zero_cnt64,
uint64_t &unsigned_max, int64_t &signed_max) {
unsigned_t &unsigned_max, signed_t &signed_max) {
if constexpr (varint_t<Arg, parent_tag>) {
if (get_varint_value(item)) {
if constexpr (sizeof(Arg) == 4) {
Expand All @@ -280,27 +291,29 @@ constexpr void STRUCT_PACK_INLINE get_fast_varint_width_impl(
}
if constexpr (std::is_unsigned_v<std::remove_reference_t<
decltype(get_varint_value(item))>>) {
unsigned_max = std::max<uint64_t>(unsigned_max, get_varint_value(item));
unsigned_max =
std::max<unsigned_t>(unsigned_max, get_varint_value(item));
}
else {
signed_max =
std::max<int64_t>(signed_max, get_varint_value(item) > 0
? get_varint_value(item)
: -(get_varint_value(item) + 1));
std::max<signed_t>(signed_max, get_varint_value(item) > 0
? get_varint_value(item)
: -(get_varint_value(item) + 1));
}
}
}
}

template <uint64_t parent_tag, typename... Args>
template <uint64_t parent_tag, typename... Args, typename unsigned_t,
typename signed_t>
constexpr int STRUCT_PACK_INLINE
get_fast_varint_width_from_max(uint64_t unsigned_max, int64_t signed_max) {
get_fast_varint_width_from_max(unsigned_t unsigned_max, signed_t signed_max) {
int width_unsigned = 0, width_signed = 0;
if constexpr (has_unsigned_varint<parent_tag, Args...>()) {
if SP_LIKELY (unsigned_max <= UINT8_MAX) {
width_unsigned = 0;
}
else if (unsigned_max <= UINT16_MAX) {
else if SP_LIKELY (unsigned_max <= UINT16_MAX) {
width_unsigned = 1;
}
else if (unsigned_max <= UINT32_MAX) {
Expand All @@ -314,7 +327,7 @@ get_fast_varint_width_from_max(uint64_t unsigned_max, int64_t signed_max) {
if SP_LIKELY (signed_max <= INT8_MAX) {
width_signed = 0;
}
else if (signed_max <= INT16_MAX) {
else if SP_LIKELY (signed_max <= INT16_MAX) {
width_signed = 1;
}
else if (signed_max <= INT32_MAX) {
Expand Down Expand Up @@ -342,8 +355,8 @@ get_fast_varint_width_from_max(uint64_t unsigned_max, int64_t signed_max) {

template <uint64_t parent_tag, typename... Args>
constexpr int STRUCT_PACK_INLINE get_fast_varint_width(const Args &...items) {
uint64_t unsigned_max = 0;
int64_t signed_max = 0;
typename uint_t<get_int_len<parent_tag, Args...>()>::type unsigned_max = 0;
typename int_t<get_int_len<parent_tag, Args...>()>::type signed_max = 0;
int non_zero_cnt32 = 0, non_zero_cnt64 = 0;
(get_fast_varint_width_impl<parent_tag>(items, non_zero_cnt32, non_zero_cnt64,
unsigned_max, signed_max),
Expand Down
80 changes: 43 additions & 37 deletions include/ylt/struct_pack/packer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ class packer {
}
if constexpr (hash_head % 2) { // has more metainfo
auto metainfo = info.metainfo();
std::size_t sz = info.size();
write_wrapper<sizeof(char)>(writer_, (char *)&metainfo);
if constexpr (serialize_static_config<serialize_type>::has_compatible) {
std::size_t sz = info.size();
switch (metainfo & 0b11) {
case 1:
low_bytes_write_wrapper<2>(writer_, sz);
Expand Down Expand Up @@ -148,23 +148,24 @@ class packer {
}
}

template <uint64_t parent_tag, std::size_t sz, typename Arg>
template <uint64_t parent_tag, std::size_t sz, typename Arg,
typename unsigned_t, typename signed_t>
static constexpr void STRUCT_PACK_INLINE
get_fast_varint_width_impl(char (&vec)[sz], unsigned int &i, const Arg &item,
uint64_t &unsigned_max, int64_t &signed_max) {
unsigned_t &unsigned_max, signed_t &signed_max) {
if constexpr (varint_t<Arg, parent_tag>) {
if (get_varint_value(item) != 0) {
vec[i / 8] |= (0b1 << (i % 8));
if constexpr (std::is_unsigned_v<std::remove_reference_t<
decltype(get_varint_value(item))>>) {
unsigned_max =
std::max<uint64_t>(unsigned_max, get_varint_value(item));
std::max<unsigned_t>(unsigned_max, get_varint_value(item));
}
else {
signed_max = std::max<int64_t>(signed_max,
get_varint_value(item) > 0
? get_varint_value(item)
: -(get_varint_value(item) + 1));
signed_max = std::max<signed_t>(signed_max,
get_varint_value(item) > 0
? get_varint_value(item)
: -(get_varint_value(item) + 1));
}
}
++i;
Expand All @@ -174,8 +175,8 @@ class packer {
template <uint64_t parent_tag, std::size_t sz, typename... Args>
static constexpr int STRUCT_PACK_INLINE
get_fast_varint_width(char (&vec)[sz], const Args &...items) {
uint64_t unsigned_max = 0;
int64_t signed_max = 0;
typename uint_t<get_int_len<parent_tag, Args...>()>::type unsigned_max = 0;
typename int_t<get_int_len<parent_tag, Args...>()>::type signed_max = 0;
unsigned int i = 0;
(get_fast_varint_width_impl<parent_tag>(vec, i, items, unsigned_max,
signed_max),
Expand Down Expand Up @@ -535,35 +536,40 @@ STRUCT_PACK_MAY_INLINE void serialize_to(Writer &writer,
#endif
static_assert(sizeof...(args) > 0);
detail::packer<Writer, detail::get_args_type<Args...>> o(writer, info);
switch ((info.metainfo() & 0b11000) >> 3) {
case 0:
o.template serialize<conf, 1>(args...);
break;
if constexpr (!check_if_has_container<detail::get_args_type<Args...>>()) {
o.template serialize<conf, 1>(args...);
}
else {
switch ((info.metainfo() & 0b11000) >> 3) {
case 0:
o.template serialize<conf, 1>(args...);
break;
#ifdef STRUCT_PACK_OPTIMIZE
case 1:
o.template serialize<conf, 2>(args...);
break;
case 2:
o.template serialize<conf, 4>(args...);
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
o.template serialize<conf, 8>(args...);
}
else {
unreachable();
}
break;
case 1:
o.template serialize<conf, 2>(args...);
break;
case 2:
o.template serialize<conf, 4>(args...);
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
o.template serialize<conf, 8>(args...);
}
else {
unreachable();
}
break;
#else
case 1:
case 2:
case 3:
o.template serialize<conf, 2>(args...);
break;
case 1:
case 2:
case 3:
o.template serialize<conf, 2>(args...);
break;
#endif
default:
detail::unreachable();
break;
};
default:
detail::unreachable();
break;
};
}
}
} // namespace struct_pack::detail
8 changes: 6 additions & 2 deletions include/ylt/struct_pack/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ void string_set_length_hacker(std::string &, std::size_t);
template <typename ch>
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
#if defined(__SANITIZE_ADDRESS__)
raw_str.resize(sz);
#elif defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
defined(_MSVC_STL_VERSION)
if (sz > str.capacity()) {
str.reserve(sz);
Expand Down Expand Up @@ -193,7 +195,9 @@ template class vector_thief<decltype(&std::vector<char>::_Mypair),

template <typename ch>
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
#if defined(__GLIBCXX__) || \
#if defined(__SANITIZE_ADDRESS__)
raw_vec.resize(sz);
#elif defined(__GLIBCXX__) || \
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
defined(_MSVC_STL_VERSION)
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
Expand Down
23 changes: 23 additions & 0 deletions include/ylt/struct_pack/varint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ struct int_t<8> {
using type = int64_t;
};

template <std::size_t bytes_width>
struct uint_t;

template <>
struct uint_t<1> {
using type = uint8_t;
};

template <>
struct uint_t<2> {
using type = uint16_t;
};

template <>
struct uint_t<4> {
using type = uint32_t;
};

template <>
struct uint_t<8> {
using type = uint64_t;
};

template <typename T>
class varint {
public:
Expand Down
14 changes: 13 additions & 1 deletion src/struct_pack/benchmark/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,23 @@ enum class LibType {
PROTOBUF,
FLATBUFFER,
};
enum class SampleType { RECT, RECTS, PERSON, PERSONS, MONSTER, MONSTERS };
enum class SampleType {
RECT,
RECTS,
VAR_RECT,
VAR_RECTS,
PERSON,
PERSONS,
MONSTER,
MONSTERS
};

inline const std::unordered_map<SampleType, std::string> g_sample_name_map = {
{SampleType::RECT, "1 rect"},
{SampleType::RECTS, std::to_string(OBJECT_COUNT) + " rects"},
{SampleType::VAR_RECT, "1 rect(with fast varint edcode)"},
{SampleType::VAR_RECTS,
std::to_string(OBJECT_COUNT) + " rects(with fast varint edcode)"},
{SampleType::PERSON, "1 person"},
{SampleType::PERSONS, std::to_string(OBJECT_COUNT) + " persons"},
{SampleType::MONSTER, "1 monster"},
Expand Down
23 changes: 23 additions & 0 deletions src/struct_pack/benchmark/data_def.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
#pragma once

#include "ylt/struct_pack.hpp"
#include "ylt/struct_pack/reflection.hpp"
#if __has_include(<msgpack.hpp>)
#define HAVE_MSGPACK 1
Expand Down Expand Up @@ -60,6 +61,28 @@ inline constexpr struct_pack::sp_config set_sp_config(
return struct_pack::DISABLE_ALL_META_INFO;
}

template <typename T>
struct rect2 {
T x;
T y;
T width;
T height;
#ifdef HAVE_MSGPACK
MSGPACK_DEFINE(x, y, width, height);
#endif
};

inline constexpr struct_pack::sp_config set_sp_config(rect2<int32_t> *) {
return struct_pack::sp_config{struct_pack::sp_config::DISABLE_ALL_META_INFO |
struct_pack::sp_config::USE_FAST_VARINT |
struct_pack::sp_config::ENCODING_WITH_VARINT};
}

inline constexpr struct_pack::sp_config set_sp_config(
std::vector<rect2<int32_t>> *) {
return struct_pack::sp_config{struct_pack::DISABLE_ALL_META_INFO};
}

enum Color : uint8_t { Red, Green, Blue };

struct Vec3 {
Expand Down
4 changes: 2 additions & 2 deletions src/struct_pack/benchmark/flatbuffer_sample.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ inline void create<fb::rect32s>(flatbuffers::FlatBufferBuilder &builder,
size_t object_count) {
static std::vector<fb::rect32> rects_vector;
rects_vector.clear();
rects_vector.assign(object_count, fb::rect32{1, 11, 1111, 1111111});
rects_vector.assign(object_count, fb::rect32{1, 0, 11, 1});
auto rects = builder.CreateVectorOfStructs(rects_vector);
auto orc = fb::Createrect32s(builder, rects);
builder.Finish(orc);
Expand All @@ -44,7 +44,7 @@ inline void create<fb::rect32s>(flatbuffers::FlatBufferBuilder &builder,
template <>
inline void create<fb::rect32>(flatbuffers::FlatBufferBuilder &builder,
size_t object_count) {
const auto orc = fb::rect32{1, 11, 1111, 1111111};
const auto orc = fb::rect32{1, 0, 11, 1};
auto offset = builder.CreateStruct(orc);
builder.Finish(offset);
}
Expand Down
6 changes: 3 additions & 3 deletions src/struct_pack/benchmark/protobuf_sample.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ inline auto create_rects(size_t object_count) {
for (int i = 0; i < object_count; i++) {
mygame::rect32 *rc = rcs.add_rect32_list();
rc->set_x(1);
rc->set_y(11);
rc->set_width(1111);
rc->set_height(111111);
rc->set_y(0);
rc->set_width(11);
rc->set_height(1);
}
return rcs;
}
Expand Down
17 changes: 4 additions & 13 deletions src/struct_pack/benchmark/sample.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,16 @@ struct base_sample {

void print_buffer_size(LibType type) {
auto lib_name = get_lib_name(type);
for (auto sample_type : g_sample_type_vec) {
for (auto iter : buf_size_map_) {
std::string prefix = lib_name;
prefix.append(" serialize ").append(get_sample_name(sample_type));
prefix.append(" serialize ").append(get_sample_name(iter.first));

auto space_str = get_space_str(prefix.size(), 36);
std::cout << prefix << space_str
<< " buffer size = " << buffer_size(sample_type) << "\n";
std::cout << prefix << space_str << " buffer size = " << iter.second
<< "\n";
}
}

virtual std::size_t buffer_size(SampleType type) const {
auto it = buf_size_map_.find(type);
if (it == buf_size_map_.end()) {
throw std::runtime_error("unknown sample type");
}

return it->second;
}

auto &get_ser_time_elapsed_map() { return ser_time_elapsed_map_; }
auto &get_deser_time_elapsed_map() { return deser_time_elapsed_map_; }

Expand Down
Loading

0 comments on commit dc10d03

Please sign in to comment.