Доброго дня.
Все еще беспокоит проблема оптимального хранения данных в объекте, которые редко используются. Например, есть некий компонентный объект, у которого огромное множество инкарнаций используют только базовый набор данных, но некоторые используют специальные, причем в произвольных комбинациях. При этом, эти данные разнородные (нельзя завести под общий интерфейс и сложить отдельно) и меняются в процессе исполнения, то есть нельзя сделать шаблон с трейтами.
По мере раздумий пришел к такому мульти-варианту:
void f1()
{
mystd::multivariant<int, int, string> x; // Пустой объект, в нем ничего нет
x.get<2>() = "hello"; // Записали строку в 3-ю ячейку.
ASSERT(!x.has<1>()); // У нас нет 2-го инта.
x.get<1>() = 1; // 2-ий инт создался и в него записали 1
ASSERT(x.has<1>()); // У нас есть 2-й инт.
x.reset<1>(); // Теперь убрали.
}
Таким образом я могу "включать и выключать" любые части мультиварианта. При выключении объект удаляется.
Почему это не сделать набором std::optional, потому что они все едят память, в сумме это может набежать втрое или вдесятеро больше, чем объект в "компактном" виде. Почему не сделать это набором std::unique_ptr — потому что для каждой секции будет свое обращение к куче. А так — все лежит в последовательном буфере. Если объектов мало, они лежат прямо рядом, в статической части, если не помещаются — выделяются на куче. Размер статической части, например, может выбираться как размер наибольшего члена варианта, это уже вопрос оптимизации. Минус такого варианта в том, что операция доступа к тому или иному члену потребует времени, нужно будет посчитать размер предыдущих частей, чтобы найти смещение нужного члена. Ну и всякие "перемещения" при перевыделении памяти.
Вопрос такой: насколько это оправдано или "память давно не ресурс" и ее экономия вылезет большими потерями из-за более сложных алгоритмов доступа? Есть ли где-то такое реализованное?
Здравствуйте, Went, Вы писали:
W>Доброго дня.
И вам того же.
W>Вопрос такой: насколько это оправдано или "память давно не ресурс" и ее экономия вылезет большими потерями из-за более сложных алгоритмов доступа? Есть ли где-то такое реализованное?
Память всё ещё ресурс, но не стоит злоупотреблять этим лозунгом.
Если у вас в итоге потребляемая память несколько Мб то тут нечего экономить.
Берёте то, что вам больше подходит.
Экономить на int и так не получится без ущерба производительности, обычно размер int равен или меньше размера указателя.
Чем собственно не подходит вариант unique_ptr<string> ?
Здравствуйте, _NN_, Вы писали: _NN>Экономить на int и так не получится без ущерба производительности, обычно размер int равен или меньше размера указателя.
Ну, про int я писал как пример, а в реале — это небольшие структуры: пары векторов, контейнеры, но иногда неожиданно большие вещи. _NN>Чем собственно не подходит вариант unique_ptr<string> ?
Потому что уник на каждую структуру потребует по указателю на каждый член варианта и будет выделять в лоб место в куче под каждую мелочь. Мой же умозрительный мультивариант обойдется (в простейшем случае) всего лишь одной битовой маской присутствия членов (да, ограничение на 32 или 64 члена) + указатель на начало буфера. И добавление новых членов в этот мультивариант будет иметь нулевые издержки по памяти, но, очевидно, линейно нарастающую сложность для доступа к члену (каждый следующий член будет требовать на 1 сложение больше).
Здравствуйте, Went, Вы писали:
W>Вопрос такой: насколько это оправдано или "память давно не ресурс" и ее экономия вылезет большими потерями из-за более сложных алгоритмов доступа? Есть ли где-то такое реализованное?
Здравствуйте, PM, Вы писали: PM>Может быть, такое подойдет: https://github.com/izvolov/burst#-динамический-кортеж
Близко, но не совсем то. Через это можно было бы реализовать мой мультивариант, но тогда накладные расходы вероятно превысят пользу от всей идеи в целом.
Ключевая разница — динамический кортеж поддерживает произвольное количество произвольных данных, а, значит, должен где-то хранить порядок записей, их длины, и, очевидно, делетеры для каждой из записей. В моем же случае типы записей известны на момент компиляции, а последовательность выводится из одной единственной битовой маски. Плюс для динамического кортежа потребуется карта соответствий — на каком индексе в этом кортеже хранится тот или иной элемент данных.
Здравствуйте, Went, Вы писали:
W>Здравствуйте, _NN_, Вы писали: _NN>>Экономить на int и так не получится без ущерба производительности, обычно размер int равен или меньше размера указателя. W>Ну, про int я писал как пример, а в реале — это небольшие структуры: пары векторов, контейнеры, но иногда неожиданно большие вещи. _NN>>Чем собственно не подходит вариант unique_ptr<string> ? W>Потому что уник на каждую структуру потребует по указателю на каждый член варианта и будет выделять в лоб место в куче под каждую мелочь. Мой же умозрительный мультивариант обойдется (в простейшем случае) всего лишь одной битовой маской присутствия членов (да, ограничение на 32 или 64 члена) + указатель на начало буфера. И добавление новых членов в этот мультивариант будет иметь нулевые издержки по памяти, но, очевидно, линейно нарастающую сложность для доступа к члену (каждый следующий член будет требовать на 1 сложение больше).
Вам нужен постоянный размер или динамический ? О каком добавлении идёт речь ?
Боюсь, придётся писать такую структуру данных самому.
Чтобы не выделять место под элементы есть optional но у него занимает место индикатор живучести объекта.
Как вариант можно сделать компактный optional где индникатор не будет в самом объекте типа:
Здравствуйте, _NN_, Вы писали:
_NN>Вам нужен постоянный размер или динамический ? О каком добавлении идёт речь ?
Члены моего мультиварианта нельзя добавлять и убирать, как в векторе. Их можно включать или выключать. По сути, это optional-на-много-членов. Выгода относительно обычного optional в том, что факт существования хранится в общей битовой маске, а все члены — в едином куске памяти (в простом случае — в куче). В этом случае добавление многообразия новых возможных членов в пределах разрядности маски не будет нести никаких накладных расходов, потому что не будет требовать каждый раз создавать новый объект типа optional<>, статично живущий в теле объекта.
Здравствуйте, Went, Вы писали:
W>Доброго дня. W>Все еще беспокоит проблема оптимального хранения данных в объекте, которые редко используются.
Ну напиши свой аллокатор что ли под это. W>Вопрос такой: насколько это оправдано или "память давно не ресурс" и ее экономия вылезет большими потерями из-за более сложных алгоритмов доступа? Есть ли где-то такое реализованное?
Память ресурс для ресурсоёмких приложений или на серверной стороне. Если у тебя довольно простое приложение с небольшим количеством эелементов то можешь не экономить на "спичках". Вопрос целесообразнаости оптимизации.
можно хранить variant<Args> initial и vector<variant<Args>> additionalArgs и написать свой метод std::get который сначала лезет в initial, и если тот бросает исключение, перебирать additionalArgs. Можно протащить метод reserve для вектора наружу, тогда дополнительная память будет выделяться за одно new. Если различия в размере объектов не так велики, вполне приемлемый вариант получается.
W>Вопрос такой: насколько это оправдано или "память давно не ресурс" и ее экономия вылезет большими потерями из-за более сложных алгоритмов доступа? Есть ли где-то такое реализованное?
Мое понимание что память снова ресурс из-за того что теперь оптимизируют попадание в кэши: когда рабочие данные небольшие им проще располагаться в кэше процессора и это может сильно ускорить работу. Мне правда не вполне понятно как это можно проверить и границы применимости этой идее, обычно все что я видел это что заметное ускорение получают когда у вас много данных и алгоритм который их обрабатывает. Если ваш мультивариант может участвовать в такого рода алгоритмах то будет лучше если он будет занимать меньше памяти.
Здравствуйте, Джо, Вы писали: Джо>Мое понимание что память снова ресурс из-за того что теперь оптимизируют попадание в кэши: когда рабочие данные небольшие им проще располагаться в кэше процессора и это может сильно ускорить работу. Мне правда не вполне понятно как это можно проверить и границы применимости этой идее, обычно все что я видел это что заметное ускорение получают когда у вас много данных и алгоритм который их обрабатывает. Если ваш мультивариант может участвовать в такого рода алгоритмах то будет лучше если он будет занимать меньше памяти.
Да, отчасти по попадание в кэши я и думал, экономя. Но, с другой стороны, если данные мультиварианта не будут помещаться в in-place буфер, то окажутся размазаны по куче, что, наверное, еще сильнее усложнит им попадание в кэш.