КД>В моем случае счетчик хранится в блоке с самими данными.
КД>Я этот shared_ptr поэтому и не люблю. Даже кушать не могу. То есть нигде не использую.
Если создаёшь shared_ptr через make_shared, то счётчик ссылок тоже вместе с данными будет. Я бы для начала в свой велосипед как раз shared_ptr и засунул. Посмотрел бы, что да как, а потом, если надо, то дальше бы оптимизировал.
Здравствуйте, Коваленко Дмитрий, Вы писали: КД>Собственно говоря, ответ мне тут уже подсказали — make_shared.
std::make_shared<const std::string> будет оптимально (одним блоком) только для коротких строк, которые помещаются в SSO-буфер std::string'а.
Для длинных строк будет еще аллокация для буфера строки.
Если из интерфейса std::string кроме compare и c_str ничего не нужно, то можно сделать строчку оптимальнее.
К примеру, нет необходимости хранить size и capacity и счетчик всегда будет в одном блоке с символами строки.
Я решал похожую задачу с константными строками и не стал связываться с подсчетом ссылок.
Сделал пул памяти поверх кучи Windows (HeapCreate, HeapAlloc, HeapDestroy) с выделением от 64K и больше.
Нуль-терминированные строки "ложились" в блоках одна за другой.
При создании строки наружу выдавался const char*, удаление отдельной строки не предусматривалось.
Когда большой объект (вроде DOM — в моем случае), владеющий всеми этими строками, становился не нужен, то просто удалялся весь пул (HeapDestroy).
Все получается предельно компактно, шустро работает и потокобезопасно в пределах времени жизни большого объекта.
Реализация занимает 100 строчек, включая ассерты.
Здравствуйте, EreTIk, Вы писали:
КД>>>В моем случае счетчик хранится в блоке с самими данными.
A>>Если создаёшь shared_ptr через make_shared, то счётчик ссылок тоже вместе с данными будет.
ETI>С данными (байтами) самой строки?
Здравствуйте, EreTIk, Вы писали: ETI>Здравствуйте, andyp, Вы писали: ETI>>>С данными (байтами) самой строки? A>>Нет, конечно. ETI>Тогда это два выделения вместо трех, как я понял ТС хотел хранить счетчик с данными строки и получить одну аллокацию из кучи на формирование объекта строки.
Да, точно. Я не сообразил, что std::wstring, хотя и хранится в блоке со счетчиком ссылок, сами данные будет хранить в отдельном блоке памяти
То есть, все таки с shared_ptr будет два блока вместо одного.
---
Я ради эксперимента перевел на std::shared_ptr<const std::wstring>.
Пришлось исправлять сравнения (нужно разыменовывать shared_ptr) и написать замену для std::less<std::shared_ptr<const std::wstring> — опять же ради разыменования.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Еще можно было в "shared_ptr" строки засунуть — но это два раза память выделяться будет (здесь я не уверен, не пробовал, только сейчас об этом подумал). КД>Хотелось нормальный read-only класс строки, которая при присваивании будет увеличивать счетчик ссылок у разделяемых данных.
shared_ptr<const std::string> как раз должен подойти, дополнительная память будет выделяться только под счетчики ссылок, а строки будут неизменяемые.
Здравствуйте, andyp, Вы писали:
ETI>>С данными (байтами) самой строки?
A>Нет, конечно.
Тогда это два выделения вместо трех, как я понял ТС хотел хранить счетчик с данными строки и получить одну аллокацию из кучи на формирование объекта строки.
ETI>Тогда это два выделения вместо трех, как я понял ТС хотел хранить счетчик с данными строки и получить одну аллокацию из кучи на формирование объекта строки.
Я отчасти поэтому и писал про "что да как". Если с++17, то можно shared_ptr<unsigned char[]> использовать, но тогда много чего придется делать заново руками.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>В одной небольшой задачке потребовалось загрузить снаружи некий набор строк, а потом его мутузить разными способами.
КД>Я на это дело посмотрел и вспомнил про свой велосипед — класс для константных строк с использованием счетчика ссылок. КД>Прикрутил этот класс к этой программе, реализовал создание новых экземпляров через словарь строк.
КД>--- КД>В STL что-то подобное для константных строк есть? string_view, может для твоего сценария и не подойдет
Здравствуйте, Igore, Вы писали:
I>Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>>В одной небольшой задачке потребовалось загрузить снаружи некий набор строк, а потом его мутузить разными способами.
КД>>Я на это дело посмотрел и вспомнил про свой велосипед — класс для константных строк с использованием счетчика ссылок. КД>>Прикрутил этот класс к этой программе, реализовал создание новых экземпляров через словарь строк.
КД>>--- КД>>В STL что-то подобное для константных строк есть? I>string_view, может для твоего сценария и не подойдет
Не. Не то.
Тут строки создаются толпой, но потом они живут своей жизнью. Постепенно они изничтожаются.
Можно было, конечно, создать словарь std::wstring, потом его юзать указатели на его элементы. Но это не то.
Еще можно было в "shared_ptr" строки засунуть — но это два раза память выделяться будет (здесь я не уверен, не пробовал, только сейчас об этом подумал).
---
Хотелось нормальный read-only класс строки, которая при присваивании будет увеличивать счетчик ссылок у разделяемых данных.
Мой велосипед так и делает.
Но я уже устал от своих велосипедов
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>В одной небольшой задачке потребовалось загрузить снаружи некий набор строк, а потом его мутузить разными способами.
КД>VS2019.
КД>Я сначала замутил её на базе std::wstring.
КД>Программулина выжрала почти 4GB, что было на грани фола для 32-битного процесса.
А можно саму задачу? Мне кажется, у тебя ошибка в том, что ты для внутренних операций копировал строки, вместо перекидывания ссылок на них.
КД>Тут строки создаются толпой, но потом они живут своей жизнью. Постепенно они изничтожаются.
А это важно, что они уничтожаются постепенно, а не в конце все разом?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Chorkov, Вы писали:
КД>>Но я уже устал от своих велосипедов
C>А что за операции предполагается проволить со строками, кроме сравнения на равенство?
В этой конкретной задаче:
1. Сравнение. По факту, используется только оператор <. В словаре строк.
2. Получение самой строки, для формирования текстов запросов к базе.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, T4r4sB, Вы писали:
КД>>В одной небольшой задачке потребовалось загрузить снаружи некий набор строк, а потом его мутузить разными способами.
КД>>VS2019.
КД>>Я сначала замутил её на базе std::wstring.
КД>>Программулина выжрала почти 4GB, что было на грани фола для 32-битного процесса.
TB>А можно саму задачу? Мне кажется, у тебя ошибка в том, что ты для внутренних операций копировал строки, вместо перекидывания ссылок на них.
Это часть тестовой системы.
Сначала формируются тестовые данные, которые потом рассовываются по (параллельно выполняемым) подзадачам.
КД>>Тут строки создаются толпой, но потом они живут своей жизнью. Постепенно они изничтожаются.
TB>А это важно, что они уничтожаются постепенно, а не в конце все разом?
По мере того как подзадачи отрабатывают, память освобождается.
Этих подзадач ~10тыщ.
А общая толпа, в рамках которой они работают — за 700 тысяч.
Удерживать словарь до завершения работы последней подзадачи не очень разумно. Особенно на 32х битах.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Сначала формируются тестовые данные, которые потом рассовываются по (параллельно выполняемым) подзадачам.
Всё равно не вижу причин рассылать копии строк, а не ссылки на них.
КД>Удерживать словарь до завершения работы последней подзадачи не очень разумно. Особенно на 32х битах.
Почему?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, XuMuK, Вы писали:
КД>>Еще можно было в "shared_ptr" строки засунуть — но это два раза память выделяться будет (здесь я не уверен, не пробовал, только сейчас об этом подумал). КД>>Хотелось нормальный read-only класс строки, которая при присваивании будет увеличивать счетчик ссылок у разделяемых данных. XMK>shared_ptr<const std::string> как раз должен подойти, дополнительная память будет выделяться только под счетчики ссылок, а строки будут неизменяемые.
Да, я тоже про это подумал. Но — вот это вот дополнительное выделение памяти напрягает.
В моем случае счетчик хранится в блоке с самими данными.
Я этот shared_ptr поэтому и не люблю. Даже кушать не могу. То есть нигде не использую.
----
Вообще этот мой велосипед (const_string) относительно хорошо продуман, поскольку он может представлять как строки из динамической памяти, так и статические строки
----
А вместо string_view у меня юзается другой велосипед — const_str_box.
Кривоватые, конечно. Но .... делать
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, Chorkov, Вы писали:
КД>>>Но я уже устал от своих велосипедов
C>>А что за операции предполагается проволить со строками, кроме сравнения на равенство?
КД>В этой конкретной задаче:
КД>1. Сравнение. По факту, используется только оператор <. В словаре строк. КД>2. Получение самой строки, для формирования текстов запросов к базе.
Надеюсь запросы на поиск полного совпадения строк, а не подстроки в строке?
Тогда, стоит поменять структуру базы: дабавит таблицу уникальных идентификаторов, и далее пользоваться только их номерами (ссылками на эту таблицу).
На стороне C++ строк вообще не будет.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>В одной небольшой задачке потребовалось загрузить снаружи некий набор строк, а потом его мутузить разными способами. КД>Программа стала жрать 600MB максимум.
КД>В STL что-то подобное для константных строк есть?
Гм, а сколько всего строк и какой их размер?
Может не стоит связываться с СТЛ?
Для таких сценариев хорошо работает переход от отдельных строк к объекту "коллекция строк". Все строки лежат в одном блоке памяти.
Доступ к строке — по индексу.
Хочешь — возвращай указатель на 0-terminated строку, хочешь — самописный аналог string_view (обертка над парой указателей или указатель+длина).
Еще в ДОСовские времена использовали такую штуку.
Удаление из коллекции в этом случае не очень эффективно, но может оно и не нужно? На выходе разом всю память освободишь.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>VS2019.
КД>Прикрутил этот класс к этой программе, реализовал создание новых экземпляров через словарь строк.
КД>В STL что-то подобное для константных строк есть?
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>Гм, а сколько всего строк и какой их размер?
Я их не считал — много наверное.
SVZ>Может не стоит связываться с СТЛ? SVZ>Для таких сценариев хорошо работает переход от отдельных строк к объекту "коллекция строк". Все строки лежат в одном блоке памяти. SVZ>Доступ к строке — по индексу. SVZ>Хочешь — возвращай указатель на 0-terminated строку, хочешь — самописный аналог string_view (обертка над парой указателей или указатель+длина). SVZ>Еще в ДОСовские времена использовали такую штуку.
SVZ>Удаление из коллекции в этом случае не очень эффективно, но может оно и не нужно? На выходе разом всю память освободишь.
Словарь строк формируется в процессе формирования подзадач. И эти подзадачи сразу уезжают на выполнение.
Когда все подзадачи сформированы, словарь грохается.
Но строки остаются — в тех подзадачах которые выполняются или еще не были выполнены.
Строки, которые были привязаны к завершенным подзадачам, но удерживались словарем, будут удалены вместе со словарем — он обнуляет их счетчик ссылок.
Подзадачи про словарь вообще ничего не знают. Им дали наборы строк, они с ними и работают.
-----------------
В Борландовская STL была std::string со счетчиком ссылок.
А в студии — каждая std::string хранит копию данных.
В этом и печаль.
Мне так кажется, что в STL нужна (константная) строка со счетчиком ссылок. Хотя, с другой стороны, вроде как std::shared_ptr<const string> проблему решает.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, EreTIk, Вы писали:
КД>>Прикрутил этот класс к этой программе, реализовал создание новых экземпляров через словарь строк.
КД>>В STL что-то подобное для константных строк есть?
ETI>Может из ATL подойдет?
То есть в STL такого нет
Стороннее мне не надо.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
КД>Словарь строк формируется в процессе формирования подзадач. И эти подзадачи сразу уезжают на выполнение. КД>Когда все подзадачи сформированы, словарь грохается. КД>Но строки остаются — в тех подзадачах которые выполняются или еще не были выполнены.
Между подзадачами много дубликатов?
Если так, то шаред поинтер нормальное решение.
КД>В Борландовская STL была std::string со счетчиком ссылок. КД>А в студии — каждая std::string хранит копию данных.
Стандарт-с, однако.
КД>Мне так кажется, что в STL нужна (константная) строка со счетчиком ссылок.
Красиво, как в Яве, всё равно не получится. Для этого GC нужен.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, Stanislav V. Zudin, Вы писали:
КД>>Словарь строк формируется в процессе формирования подзадач. И эти подзадачи сразу уезжают на выполнение. КД>>Когда все подзадачи сформированы, словарь грохается. КД>>Но строки остаются — в тех подзадачах которые выполняются или еще не были выполнены.
SVZ>Между подзадачами много дубликатов?
Как минимум есть x3. Тестируются строки в прямом порядке, в обратном порядку и random
А так, отдельные строки могут в большем количестве подзадач использоваться — я не вникал.
SVZ>Если так, то шаред поинтер нормальное решение.
+1 один блок под счетчик...
SVZ>Красиво, как в Яве, всё равно не получится. Для этого GC нужен.
С явой не работал. Но, в свете всей это ..., понял почему в C# строки константные
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, T4r4sB, Вы писали:
SVZ>>Красиво, как в Яве, всё равно не получится. Для этого GC нужен.
TB>Чтоб было "как в GC", надо просто все строки хранить в одном месте а потом грохать разом на выходе.
Не, я немного про другое.
Атомарность присваивания ссылок в Яве позволяет очень здорово работать с иммутабельными объектами (теми же строками).
На многопоточных задачах можно не заморачиваться синхронизацией — если потребовалось модифицировать объекто, то тупо заменяем старый объект новым. GC потом приберет освободившихся.
Чтобы такое замутить на плюсах приходится городить шаред поинтер, подсчет ссылок.
А "грохать разом на выходе" я предложил, но Дмитрий не хочет
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Тут строки создаются толпой, но потом они живут своей жизнью. Постепенно они изничтожаются.
КД>Можно было, конечно, создать словарь std::wstring, потом его юзать указатели на его элементы. Но это не то.
КД>Еще можно было в "shared_ptr" строки засунуть — но это два раза память выделяться будет (здесь я не уверен, не пробовал, только сейчас об этом подумал).
КД>--- КД>Хотелось нормальный read-only класс строки, которая при присваивании будет увеличивать счетчик ссылок у разделяемых данных.
КД>Мой велосипед так и делает.
КД>Но я уже устал от своих велосипедов
Ну, можно еще старый gcc взять до С++11, где строки с cow реализованы
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>Атомарность присваивания ссылок в Яве позволяет очень здорово работать с иммутабельными объектами (теми же строками). SVZ>На многопоточных задачах можно не заморачиваться синхронизацией — если потребовалось модифицировать объекто, то тупо заменяем старый объект новым. GC потом приберет освободившихся.
SVZ>Чтобы такое замутить на плюсах приходится городить шаред поинтер, подсчет ссылок.
SVZ>А "грохать разом на выходе" я предложил, но Дмитрий не хочет
У меня "на выходе" грохается словарь.
Но строки продолжают жить, пока не завершаться задачи в которых они используются.
Эти задачи, сами по себе, тоже дополнительную память жрут. И кроме них там тоже много кто жрет памяти. Поэтому постепенно освобождение памяти более ненужных строк весьма кстати.
Собственно говоря, ответ мне тут уже подсказали — make_shared.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, andyp, Вы писали:
КД>>В моем случае счетчик хранится в блоке с самими данными.
A>Если создаёшь shared_ptr через make_shared, то счётчик ссылок тоже вместе с данными будет.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>В STL что-то подобное для константных строк есть?
Использую std::shared_ptr<const std::[w]string> + небольшой лисапед со словарём для абсолютно тех же целей.
Делалось это ещё до std::string_view. Если бы делал сейчас, то наружу торчал бы только std::string_view
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>string_view — это все таки штука для доступа/представления, а не для хранения. Насколько я понимаю.
Если твои данные гарантированно не меняются, наружу можно выдавать std::string_view. А как оно внутри хранится (в данном случае это std::shared_ptr<const std::string>) это уже не важно.
Я бы сейчас вот так сделал (псевдокод):
std::string_view ConstStrDict::get(std::string_view str)
{
auto it = dict_.find(str);
if (it == dict_.end())
it = dict_.insert(std::pair { str, std::make_shared<std::string>(str) });
return *it->second;
}
Сейчас же выдаётся наружу std::shared_ptr<const std::string>. Просто лень переписывать
Здравствуйте, ArtDenis, Вы писали:
КД>>string_view — это все таки штука для доступа/представления, а не для хранения. Насколько я понимаю.
AD>Если твои гарантированно данные не меняются, наружу можно выдавать std::string_view. А как оно внутри хранится (в данном случае это std::shared_ptr<const std::string>) это уже не важно.
, что словарь хранится только в процессе формирования задач. КД>Потом словарь грохается, но строки продолжают жить своей жизнью и постепенно изничтожаются. КД>Если в задачах удерживать словарь, то он будет удерживать все строки до завершения последней задачи. А мне такое не надо.
Да, всё зависит от задачи. Универсального решения нету.
Кстати, можно периодически уничтожать в словаре элементы, на которые нету ссылок. Тогда не будут накапливаться уже не используемые строки. Но тогда наружу надо выдавать std::shared_ptr<std::string>