Наиболее эффективный метод копирования содержимого std:: deque в байт-массив


Есть ли лучший способ скопировать содержимое std::deque в байтовый массив? Похоже, что в STL должно быть что-то для этого.

// Generate byte-array to transmit
uint8_t * i2c_message = new uint8_t[_tx.size()];
if ( !i2c_message ) {
    errno = ENOMEM;
    ::perror("ERROR: FirmataI2c::endTransmission - Failed to allocate memory!");
} else {
    size_t i = 0;

    // Load byte-array
    for ( const auto & data_byte : _tx ) {
        i2c_message[i++] = data_byte;
    }

    // Transmit data
    _marshaller.sendSysex(firmata::I2C_REQUEST, _tx.size(), i2c_message);
    _stream.flush();

    delete[] i2c_message;
}
Я ищу предложения либо по пространству, либо по скорости,либо по тому и другому...

EDIT: следует отметить, что _marshaller.sendSysex() не может бросить.

ПОСЛЕДУЮЩИЕ ДЕЙСТВИЯ:

Я подумал, что стоит пересказать все, потому что комментарии довольно поучительны (за исключением войны пламени). :- P

Ответ на вопрос, как он был задан...

Использовать std::copy

Общая картина:

Вместо того, чтобы просто увеличить производительность исходного кода, стоило подумать о добавлении надежности и долговечности в базу кода.

Я проглядел RAII - получение ресурсов-это инициализация. Идя в другом направлении и принимая небольшое снижение производительности, я мог бы получить большие выгоды в отказоустойчивости (как указано @PaulMcKenzie и @WhozCraig). Фактически, я даже могу изолировать свой код от изменений в зависимости!

Окончательное Решение:

В этом случае у меня действительно есть доступ (и возможность изменять) большую базу кода - часто это не так. Я пересмотрел * выгоду, которую я получал от использования std::deque, и я поменял весь базовый контейнер на std::vector. Таким образом, экономится ударная производительность при замене контейнеров, а также получаются преимущества непрерывных данных и Рэй.

*я выбрал std::deque, потому что мне всегда нужно push_front два байта, чтобы завершить свой байтовый массив перед отправкой. Однако, поскольку это всегда два байта, я смог заполнить вектор двумя фиктивными байтами и заменить их случайным доступом-o (n) time.

1 7

1 ответ:

Примите стандартную библиотеку C++. Предполагая, что _tx на самом деле является std::deque<uint8_t>, один из способов сделать это просто:

std::vector<uint8_t> msg(_tx.cbegin(), _tx.cend());
_marshaller.sendSysex(firmata::I2C_REQUEST, msg.size(), msg.data());

Это выделяет соответствующий размер смежного буфера, копирует содержимое из исходной пары итераторов,а затем вызывает операцию отправки. Вектор будет автоматически очищен при выходе из области, и исключение будет выдано, если выделение для построения его каким-либо образом не удается.

Стандартная библиотека предоставляет множество способов разбрасывания данных, особенно даны итераторы, которые отмечают, где начинать, а где останавливаться. Можешь использовать это в своих интересах. Кроме того, разрешение RAII управлять владением и очисткой таких сущностей, а не ручное управление памятью, почти всегда является лучшим подходом, и его следует поощрять.

В общем, если вам нужна непрерывность (и, судя по внешнему виду этого вызова, именно поэтому вы это делаете), то копирование из несмежного пространства в смежное-это практически ваш единственный вариант, а для этого требуется пространство и время копирования. Не так уж много вы можете сделать, чтобы избежать этого. Я предполагаю, что заглянуть в специфику реализации std::deque и, возможно, сделать что-то вроде укладки send-calls было бы возможно, но я серьезно сомневаюсь, что будет какая-то награда, и единственная экономия, вероятно, испарится в вызовах с несколькими send.

Наконец, есть еще один вариант, который, возможно, стоит рассмотреть. Посмотрите на источник всего этого. Действительно ли a std::deque гарантирован? Например, Конечно ваш строить этот контейнер где-то в другом месте. Если вы можете сделать эту операцию сборки максимально эффективной или почти такой же, используя std::vector, то вся эта проблема исчезнет, так как вы можете просто отправить ее.

Например, если вы знали (доказуемо), что ваш std::deque никогда не будет больше некоторого размера N, вы могли бы, предварительно определив размер std::vector или аналогичное непрерывное RAII-защищенное распределение, чтобы быть 2*N по размеру, запустить как переднюю, так и заднюю итераторную пару в середине и либо предварить данные, пройдя переднюю часть итератор назад, или добавить данные, идя кормовой итератор вперед. В конце концов, ваши данные будут смежными между носом и кормой, и отправка-это все, что остается. никаких копий не потребуется, хотя дополнительное пространство все еще требуется. Все это зависит от точного определения максимального размера сообщения. Если это доступно для вас, это может быть идея, заслуживающая профилирования.