Я пытаюсь максимально эффективно преобразовать беззнаковые целочисленные данные в их двоичное представление в памяти.
Я написал четыре шаблонные функции для преобразования целых чисел как в прямой, так и в обратный порядок байтов, две из них используют битовые операции, а две другие используют указатели для копирования данных.
Они проверены как правильные, а также очень эффективные, поскольку я определил, что функции с прямым порядком байтов будут такими же быстрыми, как std::memcpy
, но функции с прямым порядком байтов почему-то занимают немного больше времени.
Эти функции:
#include <vector>
using std::vector;
typedef vector<uint8_t> bytes;
template<class T>
inline bytes LittleEndian(const T& data) {
size_t size = sizeof(T);
bytes _bytes(size);
uint8_t mask = 255;
for (size_t i = 0, shift = 0; i < size; i++, shift += 8) {
_bytes[i] = (data >> shift) & mask;
}
return _bytes;
}
template<class T>
inline bytes BigEndian(const T& data) {
size_t size = sizeof(T);
bytes _bytes(size);
uint8_t mask = 255;
for (size_t i = size, shift = 0; i-- > 0; shift += 8) {
_bytes[i] = (data >> shift) & mask;
}
return _bytes;
}
template<class T>
inline bytes CPU_Endian(const T& data) {
size_t size = sizeof(T);
bytes _bytes(size);
uint8_t* dst = (uint8_t *)_bytes.data(), * src = (uint8_t *) & data;
for (size_t i = 0; i < size; i++) {
*dst++ = *src++;
}
return _bytes;
}
template<class T>
inline bytes Flip_CPU_Endian(const T& data) {
size_t size = sizeof(T);
bytes _bytes(size);
uint8_t* dst = (uint8_t *)_bytes.data(), * src = (uint8_t *)&data + size - 1;
for (size_t i = 0; i < size; i++) {
*dst++ = *src--;
}
return _bytes;
}
И я хочу развернуть циклы for с помощью std::index_sequence
, и поскольку они связаны, я поместил их в один вопрос. Они касаются трех вещей: повторять что-то N раз, создавать последовательность индексов, которая уменьшается, а не увеличивается, и использовать индекс для установки значений.
Я пытался сделать это сам, но это не сработало:
template<class T>
inline bytes CPU_Endian2(const T& data) {
size_t size = sizeof(T);
bytes _bytes(size);
uint8_t* dst = (uint8_t*)_bytes.data(), * src = (uint8_t*)&data;
[&]<std::size_t...N>(std::index_sequence<N...>){
((*dst++ = *src++),...);
}(std::make_index_sequence<size>{});
return _bytes;
}
Не компилируется, журнал ошибок:
Build started at 18:54...
1>------ Build started: Project: hexlify_test, Configuration: Release x64 ------
1>hexlify_test.cpp
1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(98,3): error C7515: a fold expression must contain an unexpanded parameter pack
1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(99,3): error C3878: syntax error: unexpected token '(' following 'expression'
1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(99,3): message : error recovery skipped: '( identifier :: . . . {'
1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(99,35): error C2760: syntax error: '}' was unexpected here; expected ';'
1>Done building project "hexlify_test.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
========== Build completed at 18:54 and took 01.796 seconds ==========
Как я могу преобразовать эти функции в те, которые используют std::index_sequence
вместо циклов for?
Добавление constexpr
к size_t size = sizeof(T);
не привело к компиляции.
-> constexpr size_t size = sizeof(T);
Кроме того, если вы гонитесь за производительностью, вам не следует передавать std::vector
для каждого сериализуемого объекта. Скорее всего, вы просто хотите записать данные в уже существующий буфер.
SCNR: boost.org/doc/libs/1_85_0/libs/endian/doc/html/endian.html -- Я уверен, что они уже давно об этом подумали.
((*(dst+N) = *(src+N)) , ...);
Обратите также внимание, что ваша реализация предполагает порядок байтов хоста.
Я исправил код согласно комментариям, но потом после тестирования обнаружил, что это не ускоряет код, как я предполагал, поэтому я хотел удалить вопрос, но теперь опубликован ответ...
Не забудьте измерить производительность до и после, с включенной оптимизацией.
Если размер constexpr
, std::array
предпочтительнее std::vector
. Тогда можно просто std::bit_cast
.
У вас есть 2 проблемы:
size
не является constexpr
, поэтому его нельзя использовать в качестве аргумента шаблона.
-> constexpr size_t size = sizeof(T);
Ваше выражение сгиба не использует какой-либо пакет.
Так что либо
(((*dst++ = *src++), static_cast<void>(Is)), ...);
или
((dst[Is] = src[Is]),...);
Вместе это было бы
template<class T>
bytes CPU_Endian2(const T& data) {
constexpr size_t size = sizeof(T);
bytes _bytes(size);
uint8_t* dst = (uint8_t*)_bytes.data(), * src = (uint8_t*)&data;
[&]<std::size_t...Is>(std::index_sequence<Is...>){
((dst[Is] = src[Is]), ...);
}(std::make_index_sequence<size>{});
return _bytes;
}
Вы не используете пакет параметров нигде в своем расширении, и
size
не используетсяconstexpr
. Но в любом случае это в большинстве случаев бессмысленно, компиляторы прекрасно видят константный ограниченный цикл.