Skip to content

Commit 86ddd63

Browse files
authored
Merge pull request #17 from KredeGC/performance
Various fixes
2 parents 0eef0f0 + 25151ae commit 86ddd63

File tree

8 files changed

+161
-115
lines changed

8 files changed

+161
-115
lines changed

include/bitstream/stream/bit_reader.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,9 @@ namespace bitstream
212212

213213
BS_ASSERT(m_Policy.extend(num_bits));
214214

215-
// Fast path
216-
if (num_bits == 32U && m_ScratchBits == 0U)
215+
// This is actually slower
216+
// Possibly due to unlikely branching
217+
/*if (num_bits == 32U && m_ScratchBits == 0U)
217218
{
218219
const uint32_t* ptr = m_Policy.get_buffer() + m_WordIndex;
219220
@@ -222,7 +223,7 @@ namespace bitstream
222223
m_WordIndex++;
223224
224225
return true;
225-
}
226+
}*/
226227

227228
if (m_ScratchBits < num_bits)
228229
{

include/bitstream/stream/bit_writer.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,9 @@ namespace bitstream
248248

249249
BS_ASSERT(m_Policy.extend(num_bits));
250250

251-
// Fast path
252-
if (num_bits == 32U && m_ScratchBits == 0U)
251+
// This is actually slower
252+
// Possibly due to unlikely branching
253+
/*if (num_bits == 32U && m_ScratchBits == 0U)
253254
{
254255
uint32_t* ptr = m_Policy.get_buffer() + m_WordIndex;
255256
@@ -258,7 +259,7 @@ namespace bitstream
258259
m_WordIndex++;
259260
260261
return true;
261-
}
262+
}*/
262263

263264
uint32_t offset = 64U - num_bits - m_ScratchBits;
264265
uint64_t ls_value = static_cast<uint64_t>(value) << offset;
@@ -388,8 +389,8 @@ namespace bitstream
388389
Policy m_Policy;
389390

390391
uint64_t m_Scratch;
391-
uint32_t m_ScratchBits;
392-
uint32_t m_WordIndex;
392+
int m_ScratchBits;
393+
size_t m_WordIndex;
393394
};
394395

395396
using fixed_bit_writer = bit_writer<fixed_policy>;

include/bitstream/stream/stream_traits.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ namespace bitstream
5151

5252
uint32_t get_total_bits() const noexcept { return m_TotalBits; }
5353

54-
bool extend(uint32_t num_bits)
54+
bool extend(uint32_t num_bits) noexcept
5555
{
5656
if (!can_serialize_bits(num_bits))
5757
return false;

include/bitstream/traits/integral_traits.h

Lines changed: 102 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,101 @@ namespace bitstream
1919
template<typename T, T = (std::numeric_limits<T>::min)(), T = (std::numeric_limits<T>::max)()>
2020
struct bounded_int;
2121

22+
#pragma region const integral types
23+
/**
24+
* @brief A trait used to serialize integer values with compiletime bounds
25+
* @tparam T A type matching an integer value
26+
* @tparam Min The lower bound. Inclusive
27+
* @tparam Max The upper bound. Inclusive
28+
*/
29+
template<typename T, T Min, T Max>
30+
struct serialize_traits<bounded_int<T, Min, Max>, typename std::enable_if_t<std::is_integral_v<T> && !std::is_const_v<T>>>
31+
{
32+
static_assert(sizeof(T) <= 8, "Integers larger than 8 bytes are currently not supported. You will have to write this functionality yourself");
33+
34+
/**
35+
* @brief Writes an integer into the @p writer
36+
* @param writer The stream to write to
37+
* @param value The value to serialize
38+
* @return Success
39+
*/
40+
template<typename Stream>
41+
typename utility::is_writing_t<Stream>
42+
static serialize(Stream& writer, in<T> value) noexcept
43+
{
44+
static_assert(Min < Max);
45+
46+
BS_ASSERT(value >= Min && value <= Max);
47+
48+
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
49+
50+
static_assert(num_bits <= sizeof(T) * 8);
51+
52+
if constexpr (sizeof(T) > 4 && num_bits > 32)
53+
{
54+
// If the given range is bigger than a word (32 bits)
55+
uint32_t unsigned_value = static_cast<uint32_t>(value - Min);
56+
BS_ASSERT(writer.serialize_bits(unsigned_value, 32));
57+
58+
unsigned_value = static_cast<uint32_t>((value - Min) >> 32);
59+
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));
60+
}
61+
else
62+
{
63+
// If the given range is smaller than or equal to a word (32 bits)
64+
uint32_t unsigned_value = static_cast<uint32_t>(value - Min);
65+
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));
66+
}
67+
68+
return true;
69+
}
70+
71+
/**
72+
* @brief Reads an integer from the @p writer into @p value
73+
* @param reader The stream to read from
74+
* @param value The value to serialize
75+
* @return Success
76+
*/
77+
template<typename Stream>
78+
typename utility::is_reading_t<Stream>
79+
static serialize(Stream& reader, T& value) noexcept
80+
{
81+
static_assert(Min < Max);
82+
83+
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
84+
85+
static_assert(num_bits <= sizeof(T) * 8);
86+
87+
if constexpr (sizeof(T) > 4 && num_bits > 32)
88+
{
89+
// If the given range is bigger than a word (32 bits)
90+
value = 0;
91+
uint32_t unsigned_value;
92+
93+
BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
94+
value |= static_cast<T>(unsigned_value);
95+
96+
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
97+
value |= static_cast<T>(unsigned_value) << 32;
98+
99+
value += Min;
100+
}
101+
else
102+
{
103+
// If the given range is smaller than or equal to a word (32 bits)
104+
uint32_t unsigned_value;
105+
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));
106+
107+
value = static_cast<T>(unsigned_value) + Min;
108+
}
109+
110+
BS_ASSERT(value >= Min && value <= Max);
111+
112+
return true;
113+
}
114+
};
115+
#pragma endregion
116+
22117
#pragma region integral types
23118
/**
24119
* @brief A trait used to serialize integer values with runtime bounds
@@ -39,7 +134,7 @@ namespace bitstream
39134
*/
40135
template<typename Stream>
41136
typename utility::is_writing_t<Stream>
42-
static serialize(Stream& writer, in<T> value, T min = (std::numeric_limits<T>::min)(), T max = (std::numeric_limits<T>::max)()) noexcept
137+
static serialize(Stream& writer, in<T> value, T min, T max) noexcept
43138
{
44139
BS_ASSERT(min < max);
45140

@@ -81,7 +176,7 @@ namespace bitstream
81176
*/
82177
template<typename Stream>
83178
typename utility::is_reading_t<Stream>
84-
static serialize(Stream& reader, T& value, T min = (std::numeric_limits<T>::min)(), T max = (std::numeric_limits<T>::max)()) noexcept
179+
static serialize(Stream& reader, T& value, T min, T max) noexcept
85180
{
86181
BS_ASSERT(min < max);
87182

@@ -121,100 +216,17 @@ namespace bitstream
121216

122217
return true;
123218
}
124-
};
125-
#pragma endregion
126-
127-
#pragma region const integral types
128-
/**
129-
* @brief A trait used to serialize integer values with compiletime bounds
130-
* @tparam T A type matching an integer value
131-
* @tparam Min The lower bound. Inclusive
132-
* @tparam Max The upper bound. Inclusive
133-
*/
134-
template<typename T, T Min, T Max>
135-
struct serialize_traits<bounded_int<T, Min, Max>, typename std::enable_if_t<std::is_integral_v<T> && !std::is_const_v<T>>>
136-
{
137-
static_assert(sizeof(T) <= 8, "Integers larger than 8 bytes are currently not supported. You will have to write this functionality yourself");
138219

139220
/**
140-
* @brief Writes an integer into the @p writer
141-
* @param writer The stream to write to
221+
* @brief Writes or reads an integer into the @p stream
222+
* @param stream The stream to serialize to/from
142223
* @param value The value to serialize
143224
* @return Success
144225
*/
145-
template<typename Stream>
146-
typename utility::is_writing_t<Stream>
147-
static serialize(Stream& writer, in<T> value) noexcept
226+
template<typename Stream, typename U>
227+
static bool serialize(Stream& stream, U&& value) noexcept
148228
{
149-
static_assert(Min < Max);
150-
151-
BS_ASSERT(value >= Min && value <= Max);
152-
153-
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
154-
155-
static_assert(num_bits <= sizeof(T) * 8);
156-
157-
if constexpr (sizeof(T) > 4 && num_bits > 32)
158-
{
159-
// If the given range is bigger than a word (32 bits)
160-
uint32_t unsigned_value = static_cast<uint32_t>(value - Min);
161-
BS_ASSERT(writer.serialize_bits(unsigned_value, 32));
162-
163-
unsigned_value = static_cast<uint32_t>((value - Min) >> 32);
164-
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));
165-
}
166-
else
167-
{
168-
// If the given range is smaller than or equal to a word (32 bits)
169-
uint32_t unsigned_value = static_cast<uint32_t>(value - Min);
170-
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));
171-
}
172-
173-
return true;
174-
}
175-
176-
/**
177-
* @brief Reads an integer from the @p writer into @p value
178-
* @param reader The stream to read from
179-
* @param value The value to serialize
180-
* @return Success
181-
*/
182-
template<typename Stream>
183-
typename utility::is_reading_t<Stream>
184-
static serialize(Stream& reader, T& value) noexcept
185-
{
186-
static_assert(Min < Max);
187-
188-
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
189-
190-
static_assert(num_bits <= sizeof(T) * 8);
191-
192-
if constexpr (sizeof(T) > 4 && num_bits > 32)
193-
{
194-
// If the given range is bigger than a word (32 bits)
195-
value = 0;
196-
uint32_t unsigned_value;
197-
198-
BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
199-
value |= static_cast<T>(unsigned_value);
200-
201-
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
202-
value |= static_cast<T>(unsigned_value) << 32;
203-
204-
value += Min;
205-
}
206-
else
207-
{
208-
// If the given range is smaller than or equal to a word (32 bits)
209-
uint32_t unsigned_value;
210-
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));
211-
212-
value = static_cast<T>(unsigned_value) + Min;
213-
}
214-
215-
BS_ASSERT(value >= Min && value <= Max);
216-
217-
return true;
229+
return serialize_traits<bounded_int<T>>::serialize(stream, std::forward<U>(value));
218230
}
219231
};
220232
#pragma endregion

include/bitstream/utility/assert.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
#define BS_BREAKPOINT() throw
1111
#endif
1212

13-
#define BS_ASSERT(x) if (!(x)) { BS_BREAKPOINT(); return false; }
13+
#define BS_ASSERT(...) if (!(__VA_ARGS__)) { BS_BREAKPOINT(); return false; }
1414
#else // BS_DEBUG_BREAK
15-
#define BS_ASSERT(x) if (!(x)) { return false; }
15+
#define BS_ASSERT(...) if (!(__VA_ARGS__)) { return false; }
1616

1717
#define BS_BREAKPOINT() throw
1818
#endif // BS_DEBUG_BREAK

src/test/fast_serialization_test.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ namespace bitstream::test::performance
2323

2424
auto end = std::chrono::steady_clock::now();
2525

26-
auto start_time = std::chrono::time_point_cast<std::chrono::microseconds>(start).time_since_epoch();
27-
auto end_time = std::chrono::time_point_cast<std::chrono::microseconds>(end).time_since_epoch();
26+
auto start_time = std::chrono::time_point_cast<std::chrono::nanoseconds>(start).time_since_epoch();
27+
auto end_time = std::chrono::time_point_cast<std::chrono::nanoseconds>(end).time_since_epoch();
2828

29-
auto time = (end - start).count();
29+
auto time = (end_time - start_time).count();
3030

31-
std::cout << " ->time spent: " << time << "us\n";
31+
std::cout << " ->time spent: " << time << "ns\n";
3232

3333
BS_TEST_ASSERT(status);
3434
}
@@ -47,7 +47,11 @@ namespace bitstream::test::performance
4747
// Should just use straight memcpy
4848
profile_time([&]
4949
{
50-
return writer.serialize_bytes(reinterpret_cast<uint8_t*>(numbers), 1024U * 8U * sizeof(uint32_t));
50+
BS_ASSERT(writer.serialize_bytes(reinterpret_cast<uint8_t*>(numbers), 1024U * 8U * sizeof(uint32_t)));
51+
52+
writer.flush();
53+
54+
return true;
5155
});
5256
}
5357

@@ -66,7 +70,11 @@ namespace bitstream::test::performance
6670
// Has to use serialize_bits individually
6771
profile_time([&]
6872
{
69-
return writer.serialize_bytes(reinterpret_cast<uint8_t*>(numbers), 1024U * 8U * sizeof(uint32_t));
73+
BS_ASSERT(writer.serialize_bytes(reinterpret_cast<uint8_t*>(numbers), 1024U * 8U * sizeof(uint32_t)));
74+
75+
writer.flush();
76+
77+
return true;
7078
});
7179
}
7280

@@ -80,10 +88,11 @@ namespace bitstream::test::performance
8088
{
8189
for (uint32_t i = 0U; i < 1024U; i++)
8290
{
83-
if (!writer.serialize_bits(24U, 32U))
84-
return false;
91+
BS_ASSERT(writer.serialize_bits(24U, 32U));
8592
}
8693

94+
writer.flush();
95+
8796
return true;
8897
});
8998
}
@@ -98,10 +107,11 @@ namespace bitstream::test::performance
98107
{
99108
for (uint32_t i = 0U; i < 1024U; i++)
100109
{
101-
if (!writer.serialize_bits(24U, 31U))
102-
return false;
110+
BS_ASSERT(writer.serialize_bits(24U, 31U));
103111
}
104112

113+
writer.flush();
114+
105115
return true;
106116
});
107117
}

0 commit comments

Comments
 (0)