2017-07-22 80 views
7

Czy istnieje lepszy sposób kopiowania zawartości std::deque do tablicy bajtów? Wygląda na to, że w STL powinno być coś takiego.Najbardziej wydajna metoda kopiowania treści std :: deque do tablicy bajtowej

// 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; 
} 

szukam sugestii dotyczących zarówno przestrzeni lub prędkości lub obu ...

EDIT: Należy zauważyć, że nie można rzucać _marshaller.sendSysex().

FOLLOW UP:

Pomyślałem, że warto byłoby bieżnikowania wszystko, ponieważ komentarze są bardzo pouczające (z wyjątkiem wojny płomienia). :-P

Odpowiedź na pytanie, zadane ...

Zastosowanie std::copy

Im większy obraz:

Zamiast po prostu zwiększenie surowe wykonanie kodu, warto było rozważyć dodanie solidności i długowieczności do bazy kodu.

Przeoczyłem RAII - Akwizycja zasobów jest inicjowana. Kierując się w przeciwnym kierunku i biorąc niewielki wpływ na osiągi, mogłem uzyskać duże zyski w zakresie odporności (jak zauważyli @PaulMcKenzie i @WhozCraig). W rzeczywistości mogę nawet izolować mój kod od zmian w zależności!

Ostateczne Rozwiązanie:

W tym przypadku, tak naprawdę mają dostęp do (i zdolność do zmiany) większą bazę Code - często nie przypadek. Ponownie oceniam * korzyść, którą zyskałem dzięki użyciu std::deque i zamieniłem cały podstawowy kontener na std::vector. W ten sposób można zaoszczędzić na wydajności związanej z zamianą kontenerów i uzyskać korzyści z sąsiadujących danych i RAII.

* Wybrałem std::deque, ponieważ zawsze mam do push_front dwa bajty, aby sfinalizować moją tablicę bajtów przed wysłaniem. Ponieważ jednak jest to zawsze dwa bajty, udało mi się umieścić wektor z dwoma bajtami i zastąpić je losowym dostępem - O (n).

+0

'std :: copy()' może? – user0042

+0

Widziałem tylko 'std :: copy' używane do kopiowania z tablicy do kontenera, a nie odwrotnie. – Zak

+0

Po pierwsze, użyłbym 'std :: vector '. po drugie, użyłbym konstruktora iteratora do zbudowania tego samego. To eliminuje większość tego kodu, w tym daje [wiarygodne RAII] (http://en.cppreference.com/w/cpp/language/raii). element 'data()' wektora lub adres-pierwszego-elementu dałby ci wskaźnik do sąsiednich danych, które wydajesz się szukać. – WhozCraig

Odpowiedz

7

Obejmij standardową bibliotekę C++. Zakładając _tx jest naprawdę std::deque<uint8_t>, jednym ze sposobów, aby to zrobić, to po prostu:

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

ten przydziela odpowiedni rozmiar bufora ciągłą, kopiuje zawartość z iteratora pary źródłowego, a następnie powołuje się na swoją operację wysyłania. Wektor zostanie automatycznie wyczyszczony w gnieździe-wyjściu, a wyjątek zostanie zgłoszony, jeśli alokacja na jego zbudowanie zawiodła.

Standardowa biblioteka zapewnia mnóstwo sposobów na przesuwanie danych, szczególnie w przypadku iteratorów określających miejsce rozpoczęcia i miejsca zatrzymania. Równie dobrze może to wykorzystać na swoją korzyść.Dodatkowo, pozwolenie RAII na zajmowanie się własnością i oczyszczanie takich jednostek, a nie ręczne zarządzanie pamięcią, jest prawie zawsze lepszym podejściem i powinno być zachęcane.

Podsumowując, jeśli potrzebujesz ciągłości (i sądząc po wyglądzie tego połączenia, właśnie dlatego to robisz), kopiowanie z nieciągłej do sąsiadującej przestrzeni jest praktycznie jedyną opcją, i zajmuje to miejsce i czas kopiowania. Niewiele można zrobić, aby tego uniknąć. Przypuszczam, że wgląd w szczegóły implementacji std::deque i możliwe, że coś takiego jak układanie wywołań będzie możliwe, ale poważnie wątpię, że będzie jakakolwiek nagroda, a jedyne oszczędności prawdopodobnie wyparują w wywołaniach wielokrotnego wysłania.

Wreszcie istnieje inna opcja, która może być warta rozważenia. Spójrz na źródło tego wszystkiego. Czy naprawdę gwarantowane jest std::deque? Na przykład z pewnością budujesz ten kontener gdzieś indziej. Jeśli możesz wykonać tę operację budowania jako wydajną lub prawie taką samą, używając std::vector, cały ten problem zniknie, ponieważ możesz to po prostu wysłać.

Na przykład, jeśli wiedział (provably) że std::deque nigdy nie będzie większy niż niektóre wielkości N, można wstępnie Rozmiar A std::vector lub podobny ciągły przydział RAII zabezpieczony, aby być 2*N wielkości, początek zarówno środkową, jak i tylną parę iteracyjną pośrodku i albo przechodź do danych z wyprzedzeniem, idąc wstecznym iteratorem do tyłu, albo dołączaj dane, idąc ruchem wstecznym do przodu. W końcu twoje dane będą ciągłe między przednią i tylną, a wysyłanie to wszystko, co pozostaje. żadne kopie nie będą wymagane, chociaż dodatkowe miejsce jest nadal wymagane. To wszystko zależy od pewności, z jaką maksymalnym rozmiarem wiadomości. Jeśli jest to dla ciebie dostępne, może to być pomysł warty profilowania.

+1

RAII to świetny punkt. Nie podobało mi się przydzielenie i to właśnie skłoniło mnie do zadania pytania. Wiem jednak, że nie jest to bardziej efektywne pod względem przestrzeni i wątpię, czy jest bardziej efektywne pod względem prędkości, więc chociaż jest to dobra rada, tak naprawdę nie odpowiada na pytanie. – Zak

+0

@Zak, jeśli potrzebujesz ciągłości (i sądząc po wyglądzie tego połączenia, właśnie dlatego to robisz), kopiowanie z nieciągłej do sąsiadującej przestrzeni jest praktycznie jedyną opcją, która wymaga przestrzeni i czas kopiowania. Niewiele można zrobić, aby tego uniknąć. Przypuszczam, że wgląd w specyfikację implementacji 'std :: deque' i możliwe, że coś takiego jak układanie wywołań będzie możliwe, ale poważnie wątpię, że byłaby jakakolwiek nagroda, a jedyne oszczędności prawdopodobnie wyparowałyby w wywołaniach wielokrotnego wysłania . – WhozCraig

+0

Wklej to do swojej odpowiedzi, a dam ci znacznik wyboru. Dziękuję także za wspaniałą, kompletną i przemyślaną rozmowę. – Zak