Объединение векторов без копирования - есть ли готовое?
От: Shmj Ниоткуда  
Дата: 17.08.24 13:28
Оценка:
Вопрос такой.

Если нужно рассматривать кусочек вектора как отдельный массив без копирования — есть span.

А если противоположная задача — объединить n векторов без копирования. Вот есть n векторов. Нужно их рассматривать как один — как бы получить указатель на общий массив данных, но при этом без копирования и выделения памяти — чтобы в памяти они так и остались разрозненными кусками.

Есть ли что готовое? Что бы вы применили?
Отредактировано 17.08.2024 13:29 Shmj . Предыдущая версия .
Re: Объединение векторов без копирования - есть ли готовое?
От: Нomunculus Россия  
Дата: 17.08.24 13:29
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вопрос такой.


S>Вот есть n векторов. Нужно их рассматривать как один — как бы получить указатель на общий массив данных, но при этом без копирования и выделения памяти — чтобы в памяти они так и остались разрозненными кусками.


S>Есть ли что готовое? Что бы вы применили?


Обернул бы их в класс, у которого бы сделал свой итератор и []
Re: Объединение векторов без копирования - есть ли готовое?
От: T4r4sB Россия  
Дата: 17.08.24 13:29
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вопрос такой.


S>Вот есть n векторов. Нужно их рассматривать как один — как бы получить указатель на общий массив данных, но при этом без копирования и выделения памяти — чтобы в памяти они так и остались разрозненными кусками.


S>Есть ли что готовое? Что бы вы применили?


Ну тебе нужен специальный хитрый класс, хранящий вектор ссылок на спаны и который за O(n) пересчитывает индекс
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: Объединение векторов без копирования - есть ли готовое?
От: vopl Россия  
Дата: 17.08.24 13:57
Оценка: 6 (2)
Здравствуйте, Shmj, Вы писали:

S>Вопрос такой.


S>Если нужно рассматривать кусочек вектора как отдельный массив без копирования — есть span.


S>А если противоположная задача — объединить n векторов без копирования. Вот есть n векторов. Нужно их рассматривать как один — как бы получить указатель на общий массив данных, но при этом без копирования и выделения памяти — чтобы в памяти они так и остались разрозненными кусками.


S>Есть ли что готовое? Что бы вы применили?


https://en.cppreference.com/w/cpp/ranges/concat_view
Re[2]: Объединение векторов без копирования - есть ли готовое?
От: Shmj Ниоткуда  
Дата: 17.08.24 14:51
Оценка:
Здравствуйте, vopl, Вы писали:

V>https://en.cppreference.com/w/cpp/ranges/concat_view


Да, похоже что оно. Вот, кстати, GPT совместно со мной что наваял:

#include <iostream>
#include <vector>
#include <stdexcept>

class MultiArray
{
public:
    // Constructor that takes a vector of pointers to vectors
    MultiArray(const std::vector<std::vector<uint8_t>*> &ranges)
    {
        std::size_t cumulativeSize = 0;
        for (auto* range : ranges)
        {
            _blocks.emplace_back(range);
            cumulativeSize += range->size();
            _cumulativeSizes.push_back(cumulativeSize);
        }
        _totalSize = cumulativeSize;

        // Initialize cache
        _cachedBlockIndex = 0;
    }
    
    // Returns total size of the combined arrays
    std::size_t size() const
    {
        return _totalSize;
    }
    
    // Custom iterator that simulates pointer behavior
    class Iterator
    {
    public:
        Iterator(const MultiArray* multiArray, std::size_t index)
        : _multiArray(multiArray), _index(index) {}
        
        // Dereference operator
        uint8_t operator*() const
        {
            return (*_multiArray)[_index];
        }
        
        // Pointer arithmetic: Increment
        Iterator& operator++()
        {
            ++_index;
            return *this;
        }
        
        // Pointer arithmetic: Decrement
        Iterator& operator--()
        {
            --_index;
            return *this;
        }
        
        // Pointer arithmetic: Add integer
        Iterator operator+(std::size_t offset) const
        {
            return Iterator(_multiArray, _index + offset);
        }
        
        // Pointer arithmetic: Subtract integer
        Iterator operator-(std::size_t offset) const
        {
            return Iterator(_multiArray, _index - offset);
        }
        
        // Pointer comparison: Equality
        bool operator==(const Iterator& other) const
        {
            return _index == other._index && _multiArray == other._multiArray;
        }
        
        // Pointer comparison: Inequality
        bool operator!=(const Iterator& other) const
        {
            return !(*this == other);
        }
        
        // Get the underlying index (optional, for debugging)
        std::size_t getIndex() const
        {
            return _index;
        }
        
    private:
        const MultiArray* _multiArray;
        std::size_t _index;
    };
    
    // Access an element in the combined arrays
    uint8_t operator[](std::size_t index) const
    {
        if (index >= _totalSize)
            throw std::out_of_range("Index out of range");

        // Check cache
        if (index >= _cachedGlobalIndex && index < _cachedGlobalIndex + _blocks[_cachedBlockIndex]->size())
        {
            // Index is within the cached block
            std::size_t localIndex = index - _cachedGlobalIndex;
            return (*_blocks[_cachedBlockIndex])[localIndex];
        }

        // If not in cache, perform binary search to find the correct block
        auto it = std::lower_bound(_cumulativeSizes.begin(), _cumulativeSizes.end(), index + 1);
        std::size_t blockIndex = std::distance(_cumulativeSizes.begin(), it);

        // Update cache
        _cachedBlockIndex = blockIndex;
        _cachedGlobalIndex = (blockIndex > 0) ? _cumulativeSizes[blockIndex - 1] : 0;

        // Calculate local index within the found block
        std::size_t localIndex = index - _cachedGlobalIndex;

        return (*_blocks[_cachedBlockIndex])[localIndex];
    }
    
    // Return an iterator (acting as a pointer) to the first element
    Iterator data() const
    {
        return Iterator(this, 0);
    }
    
private:
    // Pointers to the vectors, without ownership
    std::vector<std::vector<uint8_t>*> _blocks;
    std::vector<std::size_t> _cumulativeSizes;  // Cumulative sizes of blocks for binary search
    std::size_t _totalSize = 0;

    // Cache to store the last accessed block and index
    mutable std::size_t _cachedGlobalIndex = 0;
    mutable std::size_t _cachedBlockIndex = 0;
};

int main()
{
    std::vector<uint8_t> array1 = {1, 2, 3};
    std::vector<uint8_t> array2 = {4, 5};
    std::vector<uint8_t> array3 = {6, 7, 8, 9};
    
    // Create a list of vector pointers
    std::vector<std::vector<uint8_t>*> arrays = {&array1, &array2, &array3};
    
    // Create MultiArray with the list of pointers to existing vectors
    MultiArray multiArray(arrays);
    
    // Get the total size
    std::size_t length = multiArray.size();
    
    // Get a "pointer" to the first element
    auto ptr = multiArray.data();
    
    // Access elements using pointer arithmetic
    for (std::size_t i = 0; i < length; ++i)
    {
        std::cout << static_cast<int>(*(ptr + i)) << " ";
    }
    std::cout << std::endl;
    
    // Access elements with increment
    auto iter = multiArray.data();
    
    for (int i = 0; i < multiArray.size(); i++)
    {
        std::cout << static_cast<int>(*iter) << " ";
        ++iter;
    }
    std::cout << std::endl;
    
    return 0;
}


Но вряд ли это можно назвать оптимальным решением
Re: Объединение векторов без копирования - есть ли готовое?
От: Pzz Россия https://github.com/alexpevzner
Дата: 17.08.24 15:02
Оценка: +1
Здравствуйте, Shmj, Вы писали:

S>Есть ли что готовое? Что бы вы применили?


Для начала бы убедился в том, что мне это действительно надо.

Копирование само по себе — процесс достаточно быстрый, если речь не идет о каких-то совсем неприличных объемах данных. Вполне возможно даже, что выделение нового буфера из кучи займет больше времени чем, собственно, копирование.

С другой стороны, логическое объединение нескольких блоков в один с общим индексом. Оно, конечно, будет O(1). Но только всякие ветвления, то-сё. Коэффициент может получиться большим.

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

Ну а так, понятно что делать. Нарисовать класс-обертку, который оборачивает эти самые вектора.
Re[3]: Объединение векторов без копирования - есть ли готовое?
От: pik Италия  
Дата: 17.08.24 15:03
Оценка:
Здравствуйте, Shmj, Вы писали:


S>Да, похоже что оно. Вот, кстати, GPT совместно со мной что наваял:


так ты ему скажи чтобы юзал C++26 он тебе выдаст что надо
Re[2]: Объединение векторов без копирования - есть ли готовое?
От: Shmj Ниоткуда  
Дата: 17.08.24 15:11
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Копирование само по себе — процесс достаточно быстрый, если речь не идет о каких-то совсем неприличных объемах данных. Вполне возможно даже, что выделение нового буфера из кучи займет больше времени чем, собственно, копирование.


Конечно больше — но чтобы куда-то скопировать — нужно сначала выделить эту память. А это дорого.

Получается у меня данных в пакете примерно 20 Мб, но при этом много пользователей — около 1000 одновременных может быть в сек. Выделять на каждый чих каждому + 20 Мб. — это уже 20 гиг расход на пустом месте.

Pzz>Ну а так, понятно что делать. Нарисовать класс-обертку, который оборачивает эти самые вектора.


Ну выше что-то подобное привели из стандартной либы + мы c GPT наваяли вариантик.

Но беда в том что это не подошло — оно возвращает итератор и это бы подходило, если бы данные не передавались в либу на голом C, которая требует именно настоящий непрерывный блок памяти, а не итератор.
Re[3]: Объединение векторов без копирования - есть ли готовое?
От: Pzz Россия https://github.com/alexpevzner
Дата: 17.08.24 15:35
Оценка:
Здравствуйте, Shmj, Вы писали:

Pzz>>Копирование само по себе — процесс достаточно быстрый, если речь не идет о каких-то совсем неприличных объемах данных. Вполне возможно даже, что выделение нового буфера из кучи займет больше времени чем, собственно, копирование.


S>Конечно больше — но чтобы куда-то скопировать — нужно сначала выделить эту память. А это дорого.


S>Получается у меня данных в пакете примерно 20 Мб, но при этом много пользователей — около 1000 одновременных может быть в сек. Выделять на каждый чих каждому + 20 Мб. — это уже 20 гиг расход на пустом месте.


Ты задачу-то опиши.

S>Но беда в том что это не подошло — оно возвращает итератор и это бы подходило, если бы данные не передавались в либу на голом C, которая требует именно настоящий непрерывный блок памяти, а не итератор.


А что за либа? Долго ли думает?

Есть куча архитектурных решений, из которых можно было бы выбирать. Например, можно зарезервировать один линейный буфер, и когда пользователь уже вывалил все свои данные, копировать их на минуточку в этот буфер, вызывать сишную либу, забирать результат а буфер предоставить для следующего запроса. Или, например, сделать не один буфер, а по числу ядер CPU, если либа шибко думательная.

Ты задачу-то опиши, а то ты пришел уже не с задачей, а с готовым решением, в котором ты не уверен.
Re[4]: Объединение векторов без копирования - есть ли готовое?
От: Shmj Ниоткуда  
Дата: 18.08.24 15:17
Оценка: -1
Здравствуйте, Pzz, Вы писали:

Pzz>Ты задачу-то опиши.


Это уже будет не C++ -тематика а архитектура ПО и мало кому интересно здесь.
Re: Объединение векторов без копирования - есть ли готовое?
От: landerhigh Пират  
Дата: 19.08.24 09:30
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Есть ли что готовое? Что бы вы применили?


В boost::asio scatter-gather IO. Подобный подход использовался со времен царя Гороха.
www.blinnov.com
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.