Я читаю Вот тот:
make_shared (на практике) более эффективен, потому что он выделяет
контрольный блок управления вместе с реальным объектом в одном
динамическое распределение. Напротив, конструктор для shared_ptr, который
принимает указатель обнаженного объекта должен выделить другую динамическую переменную
для подсчета ссылок
Означает ли это, что вектор std :: shared_ptr, созданный с использованием std :: make_shared, будет
«дружественный к кешу», поскольку данные (блок управления и данные реального указателя) находятся в одном блоке?
Мой вариант использования — это вектор из 100 000 общих указателей, на который указывает объект — 14 байт.
Может быть, но не рассчитывайте на это.
Для удобства кэширования вы хотите использовать как можно меньше памяти, и вы хотите, чтобы операции, которые расположены близко друг к другу по адресу, также были близки по времени (то есть достаточно близко, чтобы вторая операция использовала память, которая все еще находится на некотором уровне кэша от эффектов первой операции: чем ниже уровень кэша, тем лучше).
Если вы используете make_shared
, тогда вполне может быть небольшая экономия в общем использовании памяти, которая по крайней мере как правило, чтобы выиграть для кеша, независимо от того, какой у вас порядок использования памяти.
Если вы используете make_shared
, тогда управляющий блок и упомянутый объект (referand) будут смежны в памяти.
если ты не использование make_shared
и ваши объекты имеют другой размер по сравнению с вашими блоками управления, тогда с обычными распределителями памяти есть разумный шанс, что объекты будут сгруппированы вместе в одном месте, а блоки управления сгруппированы вместе в другом месте. Если они являются тот же самый размер (когда-то округленный распределителем памяти некоторым специфическим для реализации способом), тогда с обычными распределителями памяти есть разумный шанс, что они просто будут чередоваться в памяти для длительных запусков, если только shared_ptr
делает что-то, чтобы повлиять на это.
Ваша схема доступа к памяти будет определять, какая из этих схем лучше подходит для кэша, и, конечно, фактическая схема, которую вы получаете вmake_shared
case может быть чем-то другим, в зависимости от деталей реализации.
Тот факт, что у вас есть vector
в основном не зависит от всего этого, так как shared_ptr
объекты отделены от блоков управления и референдов.
Невозможно создать вектор общих указателей, созданных с помощью make_shared
, Попробуйте, вы не можете сделать это. Лучшее, что вы можете сделать, это скопировать конструкцию или скопировать присвоить указатели в векторе из общих указателей, созданных с помощью make_shared
, Но тогда они будут где-то еще в памяти.
Тем не менее, блоки управления будут по-прежнему рядом с объектом. Когда вы звоните make_shared
вы фактически делаете три вещи: объект, блок управления совместно используемым указателем для отслеживания ссылок на объект и совместно используемый указатель. make_shared
Функция заставляет блок управления и сам объект размещаться в одном смежном блоке памяти.
Является ли это кеш дружественным или нет — интересный вопрос. В основном, это зависит от того, как вы используете объект.
Если вы часто работаете только с общими указателями, а не с объектами, на которые они указывают (например, дублируя вектор и, таким образом, увеличивая счетчик ссылок для каждого общего указателя), то отдельные выделения, вероятно, будут более дружественными к кэшу, а не объединенными тот make_share
дает тебе.
Если вы часто работаете с самими объектами каждый раз, когда работаете с общими указателями, то make_shared
должен быть более дружественным к кешу в типичных условиях.
Как упоминалось выше, создание объекта с помощью make_shared делает «блок управления» смежным с объектом, на который делается ссылка.
В вашем случае, однако, я считаю, что это плохой выбор.
Когда вы выделяете память даже в большом блоке, вы не гарантируете получить непрерывное «физическое пространство» в отличие от разреженных, фрагментированных распределений страниц. По этой причине итерация по вашему списку приведет к чтению через большие промежутки памяти только для того, чтобы получить управляющие структуры (которые затем указывают на данные).
«Но мои строки кэша имеют длину 64 байта!» ты говоришь. Если это правда, вы мог подумать, «это будет означать, что объект загружается в кэш вместе со структурой управления», но это не обязательно так. Это зависит от многих вещей, таких как выравнивание данных, размер строки кэша, ассоциативность кэша и фактическая пропускная способность памяти, которую вы используете.
Проблема, с которой вы сталкиваетесь, заключается в том, что сначала необходимо извлечь структуру элемента управления, чтобы выяснить, где находятся данные, когда вместо этого они могут уже находиться в кэше, поэтому часть ваших данных (структура элемента управления) может быть, по крайней мере, практически гарантированно будет в кеше, если вы разместите их все вместе вместо make_shared.
Если вы хотите, чтобы ваши данные были кешированы, вы должны убедиться, что все ссылки на них помещаются в кеш самого высокого уровня. Продолжение его использования поможет убедиться, что он остается в кэше. Алгоритмы кэширования достаточно сложны, чтобы обрабатывать выборки данных, если ваш код не слишком загружен. Это еще одна часть того, как сделать ваши данные «дружественными к кешу»: используйте как можно меньше веток при работе с ними.
Также, при работе с ним, попробуйте разбить его на части, которые помещаются в кэш. По возможности работайте только с 32 тыс. Одновременно — это консервативный показатель для современных процессоров. Если вы точно знаете, на каком процессоре будет работать ваш код, вы можете оптимизировать его менее консервативно, если вам нужно.
РЕДАКТИРОВАТЬ: я забыл упомянуть соответствующие детали. Наиболее часто выделяемый размер страницы — 4 КБ. Кэши часто являются «ассоциативными», особенно в младших процессорах. Двухсторонняя ассоциация означает, что каждое местоположение в памяти может быть сопоставлено только каждой другой записи в кэше; Четырехсторонняя ассоциация означает, что она может вписаться в любое из 4 возможных отображений, 8-сторонняя означает любое из 8 возможных отображений и т. Д. Чем выше ассоциативность, тем лучше для вас. Самый быстрый кэш (L1) на процессоре имеет тенденцию быть наименее ассоциативным, поскольку он требует меньше логики управления; хорошо иметь смежные блоки данных для ссылки (например, смежные структуры управления). Полностью ассоциативный кеш желателен.