Skip to content

Commit 7ef02f1

Browse files
committed
Improve decode() performance by counting using pointers, not indexes.
This makes a huge impact on MSVC/x64, which apparently had a hard time optimizing the alphabet_indexes[current_index] access syntax (which is really &alphabet_indexes[0] + current_index) to something more efficient. Other compilers get a minor improvement in benchmark numbers. As follows (MinSizeRel build, benchmarked on my laptop): Decoding to string (buffers of size 32768): VS2017/Win32: previously 128, now 97 (default C++ standard) VS2017/x64: previously 340, now 88 (default C++ standard) Clang 6/x64: previously 74, now 80 (C++17) GCC 7.3/x64: previously 79, now 78 Decoding to vector<uint8_t>: VS2017/Win32: previously 99, now 94 VS2017/x64: previously 328, now 86 Clang 6/x64: previously 72, now 78 GCC 7.3/x64: previously 78, now 76 Clang is doing slightly worse after this patch, but not nearly enough to veto this patch, given the massive VS2017 improvement. As a bonus, here are current Release build numbers: VS2017/Win32: enc 101, dec string 106 (gaspardpetit benchmark) VS2017/x64: enc 99, dec string 102 (gaspardpetit benchmark) Clang 6/x64: enc 93, dec string 73, dec vector<uint8_t> 73 (C++17) GCC 7.3/x64: enc 32, dec string 62, dec vector<uint8_t> 62 (C++17) GCC Release comes out as a definitive winner here, beating the Apache base64 implemention and the polfosol snippet by a few microseconds in both the encoding benchmark, and Apache again by a few microseconds in decoding as well, as measured by gaspardpetit/base64 when built in the same Release build. It's still about half as slow as the polfosol decoding snippet, but then again, polfosol does not do any handling of special cases whereas cppcodec handles zero termination characters, throws on invalid (out of alphabet) characters and on incorrect padding. I had an earlier patch to reduce the number of checks but it actually made things worse (probably due to increased code size) so we'll leave it at this for now.
1 parent 0b716ed commit 7ef02f1

File tree

1 file changed

+29
-22
lines changed

1 file changed

+29
-22
lines changed

cppcodec/detail/stream_codec.hpp

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -328,71 +328,78 @@ inline void stream_codec<Codec, CodecVariant>::decode(
328328
const char* src_end = src + src_size;
329329

330330
alphabet_index_t alphabet_indexes[Codec::encoded_block_size()] = {};
331-
alphabet_index_t current_alphabet_index = alphabet_index_info<CodecVariant>::eof_idx;
332-
size_t current_block_index = 0;
331+
alphabet_indexes[0] = alphabet_index_info<CodecVariant>::eof_idx;
332+
333+
alphabet_index_t* const alphabet_index_start = &alphabet_indexes[0];
334+
alphabet_index_t* const alphabet_index_end = &alphabet_indexes[Codec::encoded_block_size()];
335+
alphabet_index_t* alphabet_index_ptr = &alphabet_indexes[0];
333336

334337
while (src < src_end) {
335338
if (CodecVariant::should_ignore(*src)) {
336339
++src;
337340
continue;
338341
}
339-
current_alphabet_index = alphabet_index_lookup::for_symbol(*src);
340-
if (alphabet_index_info<CodecVariant>::is_stop_character(current_alphabet_index)) {
342+
*alphabet_index_ptr = alphabet_index_lookup::for_symbol(*src);
343+
if (alphabet_index_info<CodecVariant>::is_stop_character(*alphabet_index_ptr)) {
341344
break;
342345
}
343346
++src;
344-
alphabet_indexes[current_block_index++] = current_alphabet_index;
347+
++alphabet_index_ptr;
345348

346-
if (current_block_index == Codec::encoded_block_size()) {
349+
if (alphabet_index_ptr == alphabet_index_end) {
347350
Codec::decode_block(binary_result, state, alphabet_indexes);
348-
current_block_index = 0;
351+
alphabet_index_ptr = alphabet_index_start;
349352
}
350353
}
351354

352-
if (alphabet_index_info<CodecVariant>::is_invalid(current_alphabet_index)) {
355+
if (alphabet_index_info<CodecVariant>::is_invalid(*alphabet_index_ptr)) {
353356
throw symbol_error(*src);
354357
}
355358
++src;
356359

357-
size_t last_block_index = current_block_index;
358-
if (alphabet_index_info<CodecVariant>::is_padding(current_alphabet_index)) {
359-
if (current_block_index == 0) {
360+
alphabet_index_t* last_index_ptr = alphabet_index_ptr;
361+
if (alphabet_index_info<CodecVariant>::is_padding(*last_index_ptr)) {
362+
if (last_index_ptr == alphabet_index_start) {
360363
// Don't accept padding at the start of a block.
361364
// The encoder should have omitted that padding altogether.
362365
throw padding_error();
363366
}
364367
// We're in here because we just read a (first) padding character. Try to read more.
365-
++last_block_index;
368+
// Count with last_index_ptr, but store in alphabet_index_ptr so we don't
369+
// overflow the array in case the input data is too long.
370+
++last_index_ptr;
366371
while (src < src_end) {
367-
alphabet_index_t last_alphabet_index = alphabet_index_lookup::for_symbol(*(src++));
372+
*alphabet_index_ptr = alphabet_index_lookup::for_symbol(*(src++));
368373

369-
if (alphabet_index_info<CodecVariant>::is_eof(last_alphabet_index)) {
374+
if (alphabet_index_info<CodecVariant>::is_eof(*alphabet_index_ptr)) {
375+
*alphabet_index_ptr = alphabet_index_info<CodecVariant>::padding_idx;
370376
break;
371377
}
372-
if (!alphabet_index_info<CodecVariant>::is_padding(last_alphabet_index)) {
378+
if (!alphabet_index_info<CodecVariant>::is_padding(*alphabet_index_ptr)) {
373379
throw padding_error();
374380
}
375381

376-
++last_block_index;
377-
if (last_block_index > Codec::encoded_block_size()) {
382+
++last_index_ptr;
383+
if (last_index_ptr > alphabet_index_end) {
378384
throw padding_error();
379385
}
380386
}
381387
}
382388

383-
if (last_block_index) {
389+
if (last_index_ptr != alphabet_index_start) {
384390
if ((CodecVariant::requires_padding()
385-
|| alphabet_index_info<CodecVariant>::is_padding(current_alphabet_index)
386-
) && last_block_index != Codec::encoded_block_size())
391+
|| alphabet_index_info<CodecVariant>::is_padding(*alphabet_index_ptr)
392+
) && last_index_ptr != alphabet_index_end)
387393
{
388394
// If the input is not a multiple of the block size then the input is incorrect.
389395
throw padding_error();
390396
}
391-
if (current_block_index >= Codec::encoded_block_size()) {
397+
if (alphabet_index_ptr >= alphabet_index_end) {
392398
abort();
393399
return;
394400
}
395-
Codec::decode_tail(binary_result, state, alphabet_indexes, current_block_index);
401+
Codec::decode_tail(binary_result, state, alphabet_indexes,
402+
static_cast<size_t>(alphabet_index_ptr - alphabet_index_start));
396403
}
397404
}
398405

0 commit comments

Comments
 (0)