Задать вопрос
Dyikot
@Dyikot

Что происходит на уровне памяти при обращение к полям класса?

Известно что процессор считывает из кэша. В кэш они попадают из ОЗУ. Процессор считывает по 64 байта.
Понятно что переменный которые используются чаще всего надо в начало. Но есть пару вопросов.

Для примера возьмем следующую структру:
struct WindowData
{
	float width, height;
        // other fields 64 bytes +
	bool isVisible;
};


1) При обращение к window.isVisible сначало другие переменные загрузятся в память и потом уже нужное поле?
2) Или оно сразу перейдет к нужному? И если нет, то так каждый раз когда нужно обратится?
3) Если нужно обратится к нему несколько раз за функцию, то лучше скопировать/сделать ссылку в локальную переменную?
  • Вопрос задан
  • 125 просмотров
Подписаться 1 Простой 2 комментария
Помогут разобраться в теме Все курсы
  • Нетология
    Разработчик на C++
    12 месяцев
    Далее
  • Академия Эдюсон
    Разработчик игр на Unreal Engine + ИИ
    9 месяцев
    Далее
  • Stepik
    Профессия: Разработчик C++ (Junior)
    2 месяца
    Далее
Решения вопроса 2
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Компилятор знает, по какому смещению относительно начала класса лежит каждое поле. Поэтому при обращении к isVisible будет одно чтение по адресу именно где оно лежит. Доступа к предыдущим полям не будет. Если они попали в одну кеш-линию, то они все загрузятся в кеш. Но при этом они могут быть в разных кеш линиях, даже если в классе идут совсем подряд, зависит от конкретного расположения в памяти всех полей, если граница проходит как раз между ними. Это может быть если класс больше 64 байт и первые 64 попали в одну линию, а следующий член класса уже в соседнюю. Или если класс меньше, но у него выравнивание, скажем. 4 байта. и он может лежать за 4 байта до конца линии и там уже второй int в кэш линию не влезет вместе с первым.

Создавать локальную переменную не надо. Чаще всего компилятор сам поймет, что тут обращение к одному и тому же адресу и туда никто ничего не писал и можно просто загрузить один раз в регистр и все.
Только если в в профилировщике видите что вот тут у вас узкое место и в ассемблере видите, что да, там есть несколько обращений, тогда имеет смысл вводить переменную. Но это весьма редкий случай. Перегружать код ради этой преждевременной оптимизации не стоит.
Ответ написан
Комментировать
Vamp
@Vamp
В целом, вы думаете в правильном направлении.

1. Зависит от того, как эти данные размещаются в памяти и сколько там полей. Может оказаться, что isVisible окажется в начале кеш лайна, если перед ним будет 64 байта других полей. Но в целом да, грузится сразу много полей за раз.

2. После загрузки данных в кеш, по смещению будет обращение сразу к нужному полю.

3. Да. Компилятор даже может локальную копию разместить в регистре и в дальнейшем не будет обращений ни к кешу, ни к ОЗУ. Но опять же, очень сложно на это повлиять из кода. К тому же такая оптимизация может производиться даже если обращаться к полю структуры, если компилятор сможет успешно применить технику оптимизации под названием escape analysis.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@res2001
Developer, ex-admin
Согласен с предыдущими отвечающими.
Обычно вы в своей программе не заморачиваетесь подобными вопросами, т.к. в общем случае на производительность это влияет мало и компилятор сам все оптимизирует лучше вас.
Есть смысл использовать локальную переменную, только когда лично вам, как программисту, это удобно (например это заметно сократит и упростит код).

Но встречаются специфические случаи, когда размещение переменных в памяти становится важно.
Например, за этим следят при проектировании lock-free структур данных.
В этих случаях применяют оптимизацию, смысл ее в том, что бы гарантировать, что рядом лежащие атомарные переменные будут находиться в разных кеш линиях, что позволит избежать лишней паразитной синхронизации (когда вам надо синхронизироваться по одному атомику, а синхронизируется еще и другой, если оба в одной кеш линии).
В этом случае либо дают каждому атомику выравнивание на размер кеш линии, либо вставляют между ними искусственное поле (padding) размером в кеш линию. Пишут, что это может дать не плохой буст в производительности lock-free структур на синтетических тестах, там где генерируется очень высокая конкуренция, что на практике обычно встречается не часто.
Да, в этом случае в структуре будет дырка не используемой памяти и размер структуры вырастет, но это то, чем приходится платить за lock-free доступ. Если посмотреть на какую-нибудь lock-free структуру, то обычно там либо используется дополнительная память, либо есть дополнительный код (которого не было бы если бы использовались блокировки), или чаще всего встречается и то и другое.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы