Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Сентябрь 21, 2015, 10:23



Название: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 21, 2015, 10:23
Добрый день

Есть структура
Код
C++ (Qt)
struct CData {
int mValue;
....
std::vector <float> mTail;
};
И все в ней хорошо, если бы не одно "но" - по памяти она может оказаться весьма расходной. Когда mTail содержит приличное число эл-тов - все норм, расходы на вектор (и его пул) оправданы. Но часто тех эл-тов с гулькин "нос", всего 1-2. Часто число эл-тов фиксировано - но становится известным уже в runtime.

Какой выход (учитывая что структуры могут интенсивно удаляться/создаваться)?

Спасибо


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 21, 2015, 11:42
union?


Название: Re: Хранение "хвостиков"
Отправлено: qate от Сентябрь 21, 2015, 13:43
преждевременная оптимизация - зло )
приведи реальные замеры, когда расходы хранения мешают быстродействию, расходу памяти
и почему не QList<float> ?


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 21, 2015, 15:22
преждевременная оптимизация - зло )
Ну это давно уже "гимн ленивых" :)

приведи реальные замеры, когда расходы хранения мешают быстродействию, расходу памяти
Кстати про быстродействие я ничего не говорил - сами догадались :). Ну предположим в структуре всего 2 члена - тогда на ее заполнении время будет тратиться на конструктор вектора, push_back и new->malloc

По памяти: 1 или 2 флота займут 4 или 8 байт. Пустой std::vector - уже 12 байт (больше в 64 бит) + выделит минимум 16 байт, да расходы на сам блок - вот Вам и перерасход в 3-4 раза. А Qt контейнеры еще жирнее

и почему не QList<float> ?
Потому что при размере float = 4 QList тоже сводится к вектору


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 21, 2015, 17:21
В принципе, std::vector можно инициализировать константным количеством элементов, и тогда push_back не будет занимать значимое время. Но если кол-во элементов известно заранее, может, есть смысл использовать что-то вроде QPair? Специфики задачи не знаю, но лично я иногда так и делаю.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 21, 2015, 17:40
Специфики задачи не знаю,
Ну пошли отмазки  :) Никогда в С не делали такого?
Код
C++ (Qt)
struct CData {
int mValue;
....
int tailCount;
float mTail[1];
};
Но это некультурно, как то же самое цивильно?


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 21, 2015, 17:55
Почему я про специфику говорю - я не знаю, насколько имеет смысл иметь структуру с переменным кол-вом флоатов, как часто сия конструкция используется и т.д. Если речь только о том, что неохота 1 или 2 флота в вектор паковать, можно сделать, например, базовую структуру вообще без вектора и потом 3 раза от нее наследоваться, расширив либо вектором, либо одним флотом, либо двумя (в итоге получим 4 типа данных, один из которых будет базовым для остальных). Но опять же - я не знаю, как предполагается использовать такие данные.

А что до "некультурного" кода... Ну да, нехорошо как-то, такое все таки имхо через Юнион лучше делать...


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 21, 2015, 18:52
А что до "некультурного" кода... Ну да, нехорошо как-то, такое все таки имхо через Юнион лучше делать...
Какой-то странный union
Код
C++ (Qt)
union  {
float raz[1];
float dva[2];
float tri[3];
};
К чему это? Все равно размер по максимуму, эффект тот же что и
Код
C++ (Qt)
float mData[3];
 
Ну это проходит только когда максимум известен и достаточно мал. А может быть что все имеют по 1-2, но находится один урод у которого 100. Да и вообще как-то не похоже на решение взрослого дяди  :)

..можно сделать, например, базовую структуру вообще без вектора и потом 3 раза от нее наследоваться, расширив либо вектором, либо одним флотом, либо двумя (в итоге получим 4 типа данных, один из которых будет базовым для остальных).
И потом у каждого виртуальный доступ к mTail - но классы-то разные, в контейнер их уже не положить, придется через указатели на базовый, не "плодим ли сущности"?

Вот если нет интенсивного удаления - тогда можно просто сложить флоты в др контейнер и хранить индекс данных в нем
Код
C++ (Qt)
struct CData {
int mValue;
....
int tailCount;  // если у каждого свой счетчик
int tailIndex;
};
Правда для доступа к флотам придется все время иметь под рукой 2-й контейнер (или разориться еще на указатель на него) - но в принципе терпимо. Но что делать если удалять надо, и часто?


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 22, 2015, 00:40
Ну дык идея то хорошая как раз, завести пул объектов, при удалении помечать объект как убитый, но в памяти он останется, так что при повторном использовании сразу доступен :)

Для максимального эффекта можно и несколько пулов завести.


Название: Re: Хранение "хвостиков"
Отправлено: qate от Сентябрь 22, 2015, 08:45
А Qt контейнеры еще жирнее

замеры есть ?
не забываем о Copy-On-Write, Implicit Sharing и Growth Strategies


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 09:45
Ну дык идея то хорошая как раз, завести пул объектов, при удалении помечать объект как убитый, но в памяти он останется, так что при повторном использовании сразу доступен :)

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

замеры есть ?
не забываем о Copy-On-Write, Implicit Sharing и Growth Strategies
Я вижу что Вы много читали  :) Но чем мощнее контейнер - тем больше накладные расходы. Это само по себе нормально, но речь как раз о ситуации(ях) когда эти расходы становятся неприятно велики (относительно полезной работы)


Название: Re: Хранение "хвостиков"
Отправлено: qate от Сентябрь 22, 2015, 10:28
Это само по себе нормально, но речь как раз о ситуации(ях) когда эти расходы становятся неприятно велики (относительно полезной работы)

ну хоть один пример будет это подтверждающее ?
чтобы я кроме опыта чтения приобрел и практическое подтверждение )


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 22, 2015, 10:58
Как-то не выглядит "дешевым решением", если городить его для конкретного класса. Вот в общем виде - да, но что-то затрудняюсь выбрать подходящий стандартный контейнер.

В этом случае темплейт в руки :)


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 11:31
ну хоть один пример будет это подтверждающее ?
чтобы я кроме опыта чтения приобрел и практическое подтверждение )
Странно что у Вас проблемы с таким примером. Ну ладно, см. аттач. Рез-ты
Цитировать
(член структуры) time = 0.001   RAM = 203.1 Mb
(std:vector)         tine = 16.785  RAM = 1.38 Gb
(QList)                time = 17.968  RAM = 1.79 Gb
Да, и на MSVC в дебаге лучше его не запускать - не дождетесь.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 11:33
В этом случае темплейт в руки :)
Каким образом? Ведь число хранимых эл-тов известно в runtime (а не на компиляции), о чем уже говорилось выше


Название: Re: Хранение "хвостиков"
Отправлено: qate от Сентябрь 22, 2015, 11:44
Ну ладно, см. аттач. Рез-ты

test1 =  0.121
test2 =  13.464
test3 =  8.072




Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 13:10
Ну ладно, см. аттач. Рез-ты

test1 =  0.121
test2 =  13.464
test3 =  8.072
И что Вы хотели этим сказать?  :)


Название: Re: Хранение "хвостиков"
Отправлено: m_ax от Сентябрь 22, 2015, 13:36
Лёгкое изменение теста (немного нагрузки и плюшек буста), то результаты уже не так разнятся:

Код
Bash
elapsed time: 400 msec
elapsed time: 1405 msec
elapsed time: 5797 msec
 

Сам тест:
Код
C++ (Qt)
template <class R>
struct getter : public boost::static_visitor<R>
{
   getter(size_t i) : index(i) {}
 
   template <class V>
   R operator()(V & v) const
   {
       return v[index];
   }
 
   R operator()(R v) const
   {
       if (index) throw std::out_of_range("out of range");
       return v;
   }
 
private:
   size_t index;
};
 
template <class R, class V>
R& get(size_t i, V & v)
{
   return boost::apply_visitor(getter<R&>(i), v);
}
 
 
 
const int TEST_COUNT = 50 * 1024 * 1024;
 
struct CData {
   float val;
};
 
struct CData2 {
   boost::variant<std::vector<float>, float> vec;
// или boost::variant<std::vector<float>, std::array<float, MAGIC_BUF>> vec;
// Или написать свою обёртку над таким вариантом, которая внешне была аналогична обычному вектору, например.
};
 
struct CData3 {
   QList<float> vec;
};
 
 
template <class F>
void run(F f)
{
   auto start = std::chrono::high_resolution_clock::now();
   f();
   auto stop = std::chrono::high_resolution_clock::now();
 
   auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(stop-start).count();
 
   std::cout << "elapsed time: " << elapsed << " msec" << std::endl;
}
 
int main()
{
   std::mt19937 gen;
   std::uniform_real_distribution<float> dist(0.0f, 1.0f);
 
   run([&]()
   {
 
       CData * data = new CData[TEST_COUNT];
           for (int i = 0; i < TEST_COUNT; ++i)
               data[i].val = dist(gen);
           delete [] data;
   });
 
   run([&]
   {
       CData2 * data2 = new CData2[TEST_COUNT];
           for (int i = 0; i < TEST_COUNT; ++i) {
               data2[i].vec = dist(gen);
           }
 
           delete [] data2;
   });
 
   run([&]{
       CData3 * data3 = new CData3[TEST_COUNT];
           for (int i = 0; i < TEST_COUNT; ++i)
               data3[i].vec.push_back(dist(gen));
           delete [] data3;
   });
 
   return 0;
}
 


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 22, 2015, 13:56
В этом случае темплейт в руки :)
Каким образом? Ведь число хранимых эл-тов известно в runtime (а не на компиляции), о чем уже говорилось выше

Можно сделать темплейт и определить через него пулы на 1, 2 и 3 элемента (например).
А объект с данными будет содержать указатель на конкретный элемент в нужном пуле.
Либо сделать один пул, который является вектором флотов и имеет на каждый флот флаг "занят"-"свободен", например, через битовую маску. Тогда нужный объект должен будет найти в пуле первые N свободных подряд идущих элементов и заюзать их. Но тут поиск нужен и т.д., имхо несколько пулов лучше.


Название: Re: Хранение "хвостиков"
Отправлено: xokc от Сентябрь 22, 2015, 14:02
Немного оффтопну. Вот никого не "воротит" от такого?
Код
C++ (Qt)
auto start = std::chrono::high_resolution_clock::now();
f();
auto stop = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(stop-start).count();
std::cout << "elapsed time: " << elapsed << " msec" << std::endl;
 
И это ещё спасибо "auto", без него бы оно ещё страшнее стало.
Сравните с
Код
C++ (Qt)
QElapsedTimer t;
t.start();
f();
std::cout << "elapsed time: " << t.elapsed() << " msec" << std::endl;
 


Название: Re: Хранение "хвостиков"
Отправлено: qate от Сентябрь 22, 2015, 14:09
И что Вы хотели этим сказать?  :)

что QList быстрее


Название: Re: Хранение "хвостиков"
Отправлено: qate от Сентябрь 22, 2015, 14:12
мне также не ясно - зачем тащить std и буст в qt проект, если в qt есть все из них (имею ввиду то что уже есть)
мы же не заменяем qstring на std::string


Название: Re: Хранение "хвостиков"
Отправлено: kamre от Сентябрь 22, 2015, 14:15
Вроде бы какие-то реализации для std::string для коротких строк используют inplace буфер, а для больших указатель на память, выделяемую в куче. Вот здесь что-то очень похожее можно использовать.

Вот из MSVC 2012:
Код:
	enum
{ // length of internal buffer, [1, 16]
_BUF_SIZE = 16 / sizeof (value_type) < 1 ? 1
: 16 / sizeof (value_type)};
enum
{ // roundup mask for allocated buffers, [0, 15]
_ALLOC_MASK = sizeof (value_type) <= 1 ? 15
: sizeof (value_type) <= 2 ? 7
: sizeof (value_type) <= 4 ? 3
: sizeof (value_type) <= 8 ? 1 : 0};

value_type *_Myptr()
{ // determine current pointer to buffer for mutable string
return (this->_BUF_SIZE <= this->_Myres
? _STD addressof(*this->_Bx._Ptr)
: this->_Bx._Buf);
}

const value_type *_Myptr() const
{ // determine current pointer to buffer for nonmutable string
return (this->_BUF_SIZE <= this->_Myres
? _STD addressof(*this->_Bx._Ptr)
: this->_Bx._Buf);
}

union _Bxty
{ // storage for small buffer or pointer to larger one
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

size_type _Mysize; // current length of string
size_type _Myres; // current storage reserved for string


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 22, 2015, 14:18
Ворвусь в тему. QVarLengthArray спасет отца. Краткость - сестра.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 14:35
Немного оффтопну. Вот никого не "воротит" от такого?
Та ужасно. И эти имена из одной-двух букв (хрен за что зацепиться моему слабеющему сознанию). Но начнешь критиковать - обзовут "старым ретроградом", "толстым троллем" и.т.п. Так что лучше молчать, мол "молодежный стиль" и все такое

Можно сделать темплейт и определить через него пулы на 1, 2 и 3 элемента (например).
А объект с данными будет содержать указатель на конкретный элемент в нужном пуле.
Либо сделать один пул, который является вектором флотов и имеет на каждый флот флаг "занят"-"свободен", например, через битовую маску. Тогда нужный объект должен будет найти в пуле первые N свободных подряд идущих элементов и заюзать их. Но тут поиск нужен и т.д., имхо несколько пулов лучше.
Это ничего нового не вносит. Понятно что проблема актуальна/возникает когда хвостиков "много" (мульены), а тогда темплейты создают неприятные проблемы с контейнером.

Код
C++ (Qt)
struct CData2 {
   boost::variant<std::vector<float>, float> vec;
// или boost::variant<std::vector<float>, std::array<float, MAGIC_BUF>> vec;
// Или написать свою обёртку над таким вариантом, которая внешне была аналогична обычному вектору, например.
};
 
Субъетивно, но существенно - эдак все время может быть потрачено на изучение буста (да чего там, и задача может быть забыта). Когда же это будет на бусте (если будет) - не исключено что рез-т окажется не так уж хорош как ожидалось.

Ну вот допустим я захотел хранить 0, 1, 2, 3, 4 флота напрямую (до 16 байт), а для большего числа эл-тов уже вектор. Длинноватая сопля получается в определении такого boost::variant и длинновато его инициализировать зная текущее число эл-тов.

А главное - проблем с памятью это не решит. Какой бы ни был вумный boost::variant, а данные он будет хранить в куче (иначе это не variant).  Ведь можно и просто так
Код
C++ (Qt)
int count;
float * data;   // и просто new[] и delete[]
Не вижу чем (в данном случае) это хуже boost::variant, скорее лучше

На мой взгляд самое естественное - хранить данные в др контейнере, (правда неясно в каком и как). Странно что никто не предложил ничего из этой оперы


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 14:48
Ворвусь в тему. QVarLengthArray спасет отца. Краткость - сестра.
Логика "пользователя Qt": какой класс из букваря похож? Ага, вот этот - ну все, берем  :)


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 22, 2015, 15:00
Ну, контейнер (пул) объективно решит проблему частых аллокаций-деаллокаций. Производительность это подымет. Тогда надо так и делать:

int count;
float * data;

где data будет указывать на count элементов в пуле. Пулы, кстати, можно создавать тоже в рантайме :) В зависимости от кол-ва элементов "хвоста". Это, конечно, не упростит имплементацию, но фурычить будет шустрее.


Название: Re: Хранение "хвостиков"
Отправлено: m_ax от Сентябрь 22, 2015, 15:32
Цитировать
На мой взгляд самое естественное - хранить данные в др контейнере, (правда неясно в каком и как).

std::valarray. С ним получаются самые лучшие результаты:
Код
Bash
elapsed time: 393 msec
elapsed time: 800 msec std::valarray
elapsed time: 5659 msec QList
 


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 15:41
Цитировать
На мой взгляд самое естественное - хранить данные в др контейнере, (правда неясно в каком и как).

std::valarray. С ним получаются самые лучшие результаты:
Так а что valarray, куда его лепить-то?


Название: Re: Хранение "хвостиков"
Отправлено: m_ax от Сентябрь 22, 2015, 15:49
Цитировать
Так а что valarray, куда его лепить-то?

Ай, нет.. прошу прощения, опечатался там в коде, поэтому такие времена получились.. 


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 22, 2015, 17:01
Логика "пользователя Qt": какой класс из букваря похож? Ага, вот этот - ну все, берем  :)
Бля, какой же ты упоротый. Для идиота - это класс, которому можно преаллоцировать какое-то количество элементов на стеке (ваш юзкейз "2 флоата"), либо, при превышении этого лимита, он будет аллоцировать необходимое кол-во памяти в куче. Но почитать "букварь" нам же слабо. Мудила.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 22, 2015, 17:22
Бля, какой же ты упоротый. Для идиота - это класс, которому можно преаллоцировать какое-то количество элементов на стеке (ваш юзкейз "2 флоата"), либо, при превышении этого лимита, он будет аллоцировать необходимое кол-во памяти в куче. Но почитать "букварь" нам же слабо. Мудила.
А может не надо горячиться и примитивно хамить? :) Ведь QVarLengthArray валиден только в текущей области видимости и членом структуры быть не может.


Название: Re: Хранение "хвостиков"
Отправлено: Old от Сентябрь 22, 2015, 17:50
Ведь QVarLengthArray валиден только в текущей области видимости и членом структуры быть не может.
Ну приехали.  ::)


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 22, 2015, 19:17
Ведь QVarLengthArray валиден только в текущей области видимости и членом структуры быть не может.
Может.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 23, 2015, 08:24
Ведь QVarLengthArray валиден только в текущей области видимости и членом структуры быть не может.
Тут я конечно фигню спорол, помнил что "выделяет на стеке" - ну и значит что-то типа "alloca" :). На самом деле все опять сводится к этому
Код
C++ (Qt)
template <size_t num>
struct CData {
int mValue;
...
float data[num];
};
QVarLengthArray может хранить др данные (не только float) и добавлять/удалять их - но меня интересует только хранение. В том-то и проблема что воспользоваться такой конструкцией я не могу, т.к. num должно быть известно на компиляции.


Название: Re: Хранение "хвостиков"
Отправлено: Old от Сентябрь 23, 2015, 08:38
В том-то и проблема что воспользоваться такой конструкцией я не могу, т.к. num должно быть известно на компиляции.
Этот num в QVarLengthArray определяет границу.
Если данных будет меньше num, то они будут храниться в float data[ num ], а при заполнении этого массива будет алоцирован буфер в куче.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 23, 2015, 09:25
Пример
Код
C++ (Qt)
struct CData {
int mValue;
...
CVarLengthArray <float, ???> data;  // а здесь что?
};
Т.е. привлечение CVarLengthArray ничего не решает, только тратится еще больше памяти на его служебные поля

Вообще какое-то падение скорости можно пережить, в конце-концов есть нагрузка. Но по расходу памяти полный провал - 200Mb vs 1.37Gb  :'(


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 23, 2015, 10:14
Расход памяти по сравнению чего с чем?


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 23, 2015, 10:16
Расход памяти по сравнению чего с чем?
См пост #13


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 23, 2015, 10:21
Собственно нужен контейнер (или кастомный аллокатор) который бы не удалял эл-ты, а оставлял "дырки", которые потом могут заполняться. Наверняка в дусте есть такой...


Название: Re: Хранение "хвостиков"
Отправлено: m_ax от Сентябрь 23, 2015, 12:26
Цитировать
Наверняка в дусте есть такой...
Наверное, boost::small_vector


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 23, 2015, 18:36
Код
C++ (Qt)
CVarLengthArray <float, ???> data;  // а здесь что?
 

Ну вот допустим я захотел хранить 0, 1, 2, 3, 4 флота напрямую (до 16 байт), а для большего числа эл-тов уже вектор.

А уже далее ничто не мешает сделать
Код:
struct S
{
    size_t size;
    union {
        float data1[4];
        float *data2 {nullptr};
    }
};


Название: Re: Хранение "хвостиков"
Отправлено: Old от Сентябрь 23, 2015, 18:44
Но по расходу памяти полный провал - 200Mb vs 1.37Gb  :'(
Вы так им пользовалтсь? :)
Код
C++ (Qt)
QVarLengthArray<float> data;


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 23, 2015, 19:05
Old
Ну вы что, там же огромный оверхед - там есть int alloc и T* хранится не в юнионе.


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 23, 2015, 19:26
Получается, чтобы использовать пул, нужно в структуре хранить адрес свободного элемента (ну или индекс), это как минимум 4 байта для 32 битной системы или 8 байт для 64 битной. Т.е. по размеру эквивалент 1 или 2 флоата. Таким образом, пул для 1-флоатного варианта не имеет смысла на 32 битах. Чтобы уменьшить расход памяти, вариант с 1 флоатом можно тупо паковать в струкуру напрямую. С 2 флоатами - зависит от разрядности. В принципе, я бы тоже паковал в структуру. Для остальных можно делать либо пул, либо вектор.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 24, 2015, 09:40
Ну вот допустим я захотел хранить 0, 1, 2, 3, 4 флота напрямую (до 16 байт), а для большего числа эл-тов уже вектор.

А уже далее ничто не мешает сделать
Код:
struct S
{
    size_t size;
    union {
        float data1[4];
        float *data2 {nullptr};
    }
};
Да, и здесь это будет проще и лучше чем boost::variant или QVarLengthArray. Но все же возможен вариант с куда меньшим оверхедом
Код
C++ (Qt)
struct CData {
size_t size;
size_t index;  // индекс в др контейнере float
};
Правда так CData теряет "самодостаточность", чтобы получить данные нужно знать др контейнер - но такого ограничения и не налагалось. Проблема в том что это годится только если нет интенсивного удаления эл-тов CData 

Наверное, boost::small_vector
Это тот же самый QVarLengthArray. Похоже нужен cached_node_allocator, но разобраться там не так уж просто...


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 25, 2015, 07:55
Код
C++ (Qt)
struct CData {
size_t size;
size_t index;  // индекс в др контейнере float
};
Ну это почти эквивалентно вектору с кастомным аллокатором, к-ый уже предлагали (http://www.prog.org.ru/index.php?topic=29333.msg215281#msg215281).


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 25, 2015, 09:51
Подумал ещё. Мой вариант занимает 2*8 байт + данные или 8 байт + данные, если данных мало.
Ваш вариант занимает 2*8 байт + данные.
Где ж тут "куда меньший оверхед"? Разве что на динамическую аллокацию, ну так это ж еще пул писать надо, фрагментацию учитывать.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 25, 2015, 10:54
Ну это почти эквивалентно вектору с кастомным аллокатором, к-ый уже предлагали (http://www.prog.org.ru/index.php?topic=29333.msg215281#msg215281).
Если Вы имеете ввиду пост #26 то неясно что там предлагалось - упоминалось о "пулах", и что с того?

Подумал ещё. Мой вариант занимает 2*8 байт + данные или 8 байт + данные, если данных мало.
Ваш вариант занимает 2*8 байт + данные.
Если float data1[4], то никак не меньше 16 да плюс счетчик. Да и вообще я могу отпихнуться unsigned int (вместо size_t) и иметь всегда 8

Где ж тут "куда меньший оверхед"? Разве что на динамическую аллокацию, ну так это ж еще пул писать надо, фрагментацию учитывать.
В этом все дело. Чем поддерживать указатель на float? new/delete лучше вектора но при малых данных оверхед приличный. Напр 5 флотов - сам блок уже 32 байта плюс "обвязки" для кучи. А вот если мы храним данные в отдельном контейнере - они занимают ровно sizeof


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 25, 2015, 11:16
Я предлагал пулы в виде списка структур для 2, 3 и 4 флотов. При этом данные хранятся в отдельном контейнере, а объект имеет указатель на элемент пула. Убился объект - освободился элемент в пуле (но физически он остался в памяти). Добавился объект - нашли первый свободный элемент пула и связали с объектом. Нету свободных - добавили.
Заимплементировать такой пул несложно.

Для 1 флота пул юзать не имеет смылса, т.к. флот займет столько же, сколько и указатель. А для бОльшего кол-ва данных - std::vector пойдет.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 25, 2015, 11:31
Я предлагал пулы в виде списка структур для 2, 3 и 4 флотов. При этом данные хранятся в отдельном контейнере, а объект имеет указатель на элемент пула. Убился объект - освободился элемент в пуле (но физически он остался в памяти). Добавился объект - нашли первый свободный элемент пула и связали с объектом. Нету свободных - добавили.
Заимплементировать такой пул несложно.
Позвольте, но ведь это (позорный?) "велосипед" :)  Обдумывал в этом направлении, соображения:

- почему не для 5 тоже (или произвольного числа)?
- почему бОльшая дырка не может заполняться?
- в какой-то момент оказалось "почти все в дырках", как "освободиться"?
и.т.д

По сути это "своя маленькая куча" а раз так - это должна быть хорошо известная задача. Рыпнулся в буст, но..


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 25, 2015, 12:27
Количество данных, которое имеет смысл держать в пуле, зависит просто от размера указателя на элемент в пуле. Допустим, в 32-битном приложении флоат займет 32 бита, и поинтер на элемент - тоже 32 бита. Т.е. 1 флот можно хранить просто в структуре данных. От 2 до N флотов уже займут больше, чем 1 поинтер - значит, имеет смысл, наверное, "пулить" (но мы всегда будем терять 32 бита из-за поинтера, вопрос в том, какой будет оверхед у вектора флотов std::vector<float>).

Насчет дырок - тут я не понял. Обычно пул - это просто 2 списка преаллоцированных элементов, один из них - "свободный", другой - "занятый". При освобожднии занятого он переходит в список "свободных", и наоборот. Поэтому "дырок" быть не должно в принципе.

Да, это по сути велосипед, наверно. Но я не знаю, что еще предложить :)



Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 25, 2015, 12:34
Насчет дырок - тут я не понял. Обычно пул - это просто 2 списка преаллоцированных элементов, один из них - "свободный", другой - "занятый". При освобожднии занятого он переходит в список "свободных", и наоборот. Поэтому "дырок" быть не должно в принципе.
Я имел ввиду расклад "мульен "свободных" (дырок) и только 10 "занятых" - а память-то распределена для всех

Да, это по сути велосипед, наверно. Но я не знаю, что еще предложить :)
Да, и, как оно часто бывает, полезешь велосипедить, а оно оказывается не так уж мало/просто как казалось.

Да, и заметьте, как красноречиво молчание "бустовских эстетов" :) А ведь задача явно из базовых..


Название: Re: Хранение "хвостиков"
Отправлено: Racheengel от Сентябрь 25, 2015, 12:39
Фрагментация памяти - это зло... Сколько мы с ней боролись, ужас :(
Буст не спасает, когда нужен хардкор системного уровня, увы. Тут впору до ассемблера опускаться иногда.
"Эстетики" в таком случае мало, к сожалению, когда должно "быстро летать", а не "красиво выглядеть"...


Название: Re: Хранение "хвостиков"
Отправлено: Old от Сентябрь 25, 2015, 12:47
Буст не спасает, когда нужен хардкор системного уровня, увы. Тут впору до ассемблера опускаться иногда.
К сожалению, не все это понимают и поэтому регулярно пытаются там найти решения на все случаи жизни. :)


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 26, 2015, 10:55
В этом все дело. Чем поддерживать указатель на float? new/delete лучше вектора но при малых данных оверхед приличный. Напр 5 флотов - сам блок уже 32 байта плюс "обвязки" для кучи. А вот если мы храним данные в отдельном контейнере - они занимают ровно sizeof

Шта? Типа float *data - это УЖАС оверхед, а size_t index - нет? Шта?
Еще раз, sizeof(T*) == sizeof(size_t). Делая индекс вы ни на чем не экономите.
В "другом контейнере" лежит 5 флотов (5*4) + size (8) + index (8).
В куче лежит 5 флотов (5*4) + size (8) + указатель в кучу (8).
У меня оверхед по памяти в исходном варианте потому что флоатов в структуре 4, ну так это дань скорости, сделайте 2 флоата (1 дабл) и не будет оверхеда. Гибкость же.
Ваш вариант только отличается тем, что (по сути) вместо malloc используется кастомный аллокатор на пуле, но это вообще отдельная тема, когда их стоит/не стоит применять.


Название: Re: Хранение "хвостиков"
Отправлено: Old от Сентябрь 26, 2015, 11:23
Если использовать пулы, то можно сэкономить на хранении размера данных, оставив только указатель.
Т.е. структура будет хранить только указатель на float.

Код
C++ (Qt)
struct Data {
....
float *m_data;
};
 

Код
C++ (Qt)
Data data;
data.m_data = FloatAllocator::alloc( 1 );    // Быстро возвращается указатель на буфер в пуле однофлоатов (данные хранятся компактно)
data.m_data = FloatAllocator::alloc( 2 );    // Быстро возвращается указатель на буфер в пуле двуфлоатов (данные хранятся компактно)
data.m_data = FloatAllocator::alloc( 4 );    // Быстро возвращается указатель на буфер в пуле четырехфлоатов (данные хранятся компактно)
data.m_data = FloatAllocator::alloc( 10500 );    // Алоцируется буфер в куче для указанного количества
 
size_t size = FloatAllocator::size( data.m_data );    // Возвращает доступное количество флоатов
 
FloatAllocator::free( data.m_data );    // Для пула просто пометка что буфер освободился, для не преаллоцированых буферов - освобождение
 


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 26, 2015, 11:37
Шта? Типа float *data - это УЖАС оверхед, а size_t index - нет? Шта?
Еще раз, sizeof(T*) == sizeof(size_t). Делая индекс вы ни на чем не экономите.
В "другом контейнере" лежит 5 флотов (5*4) + size (8) + index (8).
В куче лежит 5 флотов (5*4) + size (8) + указатель в кучу (8).
У меня оверхед по памяти в исходном варианте потому что флоатов в структуре 4, ну так это дань скорости, сделайте 2 флоата (1 дабл) и не будет оверхеда. Гибкость же.
Вот как раз не та же. 4 флота хороши напр для 3D модели, которая чаще всего - треугольники, реже квадранглы, а остальные случаи (1, 2 и > 4) редки. Но в общем случае известно лишь что число флотов ("хвостиков") может быть мало, скажем, меньше 10. Нацеливаясь на какой-то конкретный случай Вы заметно проигрываете в остальных. Да и с uniоn неприятная возня, нужны геттеры/сеттеры. Не проблема конечно, но код явно не украшает.

Ваш вариант только отличается тем, что (по сути) вместо malloc используется кастомный аллокатор на пуле, но это вообще отдельная тема, когда их стоит/не стоит применять.
При условии что CData могут интенсивно удаляться - точно стоит (иначе достаточно вектора). Вопрос где взять такой аллокатор. В букваре ж нема... или я ошибаюсь?  :)

[OFF]
Шта?
Возможно Вам это кажется остроумным, но читать/понимать трудновато  :)[/OFF]


Название: Re: Хранение "хвостиков"
Отправлено: Old от Сентябрь 26, 2015, 11:52
Вопрос где взять такой аллокатор. В букваре ж нема... или я ошибаюсь?  :)
Одним алокатором тут не обойтись. :)
Есть boost::object_pool, можно взять его или написать свой пул (я бы писал свой). И на пулах пишите свой менеджер памяти, я чуть выше написал его возможности.

Код
C++ (Qt)
template<typename T, size_t maxPrealloc>
class Allocator
{
public:
   T    *alloc( size_t n );
   void  free( T * );
   size_t  size( T * );
};
 

Буферы для количества элементов менее maxPrealloc будет браться из преалоцированных пулов, все остальные выделяться в куче.
Напомню, здесь еще и на размере данных в CData экономиться.


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 26, 2015, 16:02
При условии что CData могут интенсивно удаляться - точно стоит (иначе достаточно вектора). Вопрос где взять такой аллокатор. В букваре ж нема... или я ошибаюсь?  :)

В общем, как обычно, задача сформулирована через жопу. Сначала вы пишете, что вектор ест слишком много памяти. Мы это обсуждаем 5 страниц. Потом оказывается, что "много памяти" - это один (!) лишний указатель, а на самом деле нам важна не память, а скорость аллокации. Собственно, см. мой эмоциональный пост.


Название: Re: Хранение "хвостиков"
Отправлено: Igors от Сентябрь 26, 2015, 16:27
Сначала вы пишете, что вектор ест слишком много памяти. Мы это обсуждаем 5 страниц. Потом оказывается, что "много памяти" - это один (!) лишний указатель, а на самом деле нам важна не память, а скорость аллокации.
Возможно Вас сбило с толку "(иначе достаточно вектора)" - но это имеется ввиду хранить все хвосты в отдельном векторе (а не совать вектор в каждый CData), т.е. никакого противоречия здесь нет.

В общем, как обычно, задача сформулирована через жопу.
...
Собственно, см. мой эмоциональный пост.
Откуда такая нетерпеливость? Зачем раздражаться и срываться на поросячий визг если Вы не увидели решения моментально? :) Я его тоже пока не вижу, вот и хорошо, есть о чем подумать.


Название: Re: Хранение "хвостиков"
Отправлено: Авварон от Сентябрь 26, 2015, 16:49
Потому что вы пишете хрен пойми  чем. Мне каждую фразу приходится перечитывать по нескольку раз.
Формулируйте задачу четко. Например: struct Data{ vector<float>;} не подходит 1. потому что жрет много памяти (float[N]+3*sizeof(void *)) 2. Потому что медленно аллоцируется. А дальше - "по 1 я хочу float[N]", "по 2 я хочу 0 malloc'ов". А не как обычно - тут "много" памяти, тут "медленно". А то получается, что вам "медленно", но по памяти есть запас, или наоборот, хоть и "медленно", но надо еще сильнее оптимизировать память. Просто вытягивать из вас 5 страниц нахуй никому не вперлось. И ладно если бы 1 раз такое, каждая ваша тема - только через 20 страниц высняется "ТЗ".


Название: Re: Хранение "хвостиков"
Отправлено: Old от Сентябрь 26, 2015, 17:37
Зачем раздражаться и срываться на поросячий визг если Вы не увидели решения моментально? :) Я его тоже пока не вижу, вот и хорошо, есть о чем подумать.
Печально, а у меня уже есть рабочий прототип.

Код
C++ (Qt)
int main()
{
Allocator<float, 4> memman;
 
float *a1 = memman.alloc( 1 );
BOOST_ASSERT( a1 );
float *a2 = memman.alloc( 2 );
BOOST_ASSERT( a2 );
float *a3 = memman.alloc( 3 );
BOOST_ASSERT( a3 );
float *a4 = memman.alloc( 4 );
BOOST_ASSERT( a4 );
float *a5 = memman.alloc( 5 );
BOOST_ASSERT( a5 );
float *a100 = memman.alloc( 100 );
BOOST_ASSERT( a100 );
 
size_t s100 = memman.size( a100 );
BOOST_ASSERT( s100 == 100 );
size_t s5 = memman.size( a5 );
BOOST_ASSERT( s5 == 5 );
size_t s4 = memman.size( a4 );
BOOST_ASSERT( s4 == 4 );
size_t s3 = memman.size( a3 );
BOOST_ASSERT( s3 == 3 );
size_t s2 = memman.size( a2 );
BOOST_ASSERT( s2 == 2 );
size_t s1 = memman.size( a1 );
BOOST_ASSERT( s1 == 1 );
 
memman.free( a2 );
memman.free( a1 );
memman.free( a4 );
memman.free( a3 );
memman.free( a5 );
memman.free( a100 );
 
return 0;
}
 

Цитировать
Allocate pool 1
Allocate pool 2
Allocate pool 3
Allocate pool 4
Allocate heap 5
Allocate heap 100
Size heap 100
Size heap 5
Size pool 4
Size pool 3
Size pool 2
Size pool 1
Free pool 2
Free pool 1
Free pool 4
Free pool 3
Free heap 5
Free heap 100