Стоимость доступа к динамически создаваемым объектам с динамически распределенными элементами - PullRequest
2 голосов
/ 26 мая 2010

Я создаю приложение, в котором будут динамически размещенные объекты типа A, каждый из которых имеет динамически распределенный член (v), аналогичный приведенному ниже классу

class A {
int a;
int b;
int* v;
};

где:

  • Память для v будет выделена в конструкторе.
  • v будет выделено один раз при создании объекта типа A. Его размер не нужно изменять.
  • Размер v будет отличаться во всех случаях A.

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

  • Может ли наличие динамически распределенного v означать, что экземпляр A и его член v не расположены вместе в памяти?
  • Какие инструменты и методы можно использовать для проверки, является ли эта фрагментация узким местом в производительности?
  • Если такая фрагментация является проблемой производительности, существуют ли какие-либо методы, которые могли бы позволить A и v выделяться в непрерывной области памяти?
  • Или есть какие-то методы, облегчающие доступ к памяти, такие как схема предварительной выборки? например, получить объект типа A, работающий с другими переменными-членами во время предварительной выборки v.
  • Если бы размер v или приемлемый максимальный размер мог быть известен во время компиляции, замена v массивом фиксированного размера, например int v [max_length], привела бы к повышению производительности?

Целевые платформы - это стандартные настольные машины с процессорами x86 / AMD64, ОС Windows или Linux, скомпилированные с использованием компиляторов GCC или MSVC.

Ответы [ 3 ]

2 голосов
/ 26 мая 2010

Если у вас есть веская причина заботиться о производительности ...

Может быть, динамически выделенный v может означать, что экземпляр A и его член v не расположены вместе в памяти?

Если им обоим присвоено «новое», то вполне вероятно, что они будут рядом друг с другом. Тем не менее, текущее состояние памяти может существенно повлиять на этот результат, это существенно зависит от того, что вы делали с памятью. Если вы просто распределите тысячу таких вещей один за другим, то более поздние почти наверняка будут «почти смежными».

Если экземпляр A находится в стеке, весьма маловероятно, что его 'v' будет рядом.

Если такая фрагментация является проблемой производительности, существуют ли какие-либо методы, которые могли бы разрешить A и v выделяться в непрерывной области памяти?

Выделите место для обоих, затем поместите новые их в это пространство. Это грязно, но обычно должно работать:

char* p = reinterpret_cast<char*>(malloc(sizeof(A) + sizeof(A::v)));
char* v = p + sizeof(A);
A* a = new (p) A(v);

// time passes

a->~A();
free(a);

Или есть какие-то методы, облегчающие доступ к памяти, такие как схема предварительной выборки?

Предварительная выборка зависит от компилятора и платформы, но многие компиляторы имеют встроенные функции для этого. Имейте в виду, что это не очень поможет, если вы попытаетесь получить доступ к этим данным сразу, для того, чтобы предварительная выборка имела какое-либо значение, вам часто приходится делать это за сотни циклов, прежде чем вы захотите получить данные. Тем не менее, это может быть огромный прирост скорости. Внутреннее будет выглядеть примерно так: __pf(my_a->v);

Если размер v или допустимый максимальный размер могут быть известны во время компиляции замена v массивом фиксированного размера, например int v [max_length], приведет к улучшению производительность

Может быть. Если буфер фиксированного размера обычно близок к нужному размеру, это может привести к значительному увеличению скорости. Таким образом, всегда будет быстрее получить доступ к одному экземпляру, но если буфер является чрезмерно гигантским и в значительной степени неиспользуемым, вы потеряете возможность для добавления большего количества объектов в кэш. То есть Лучше иметь в кеше меньше объектов, чем неиспользуемых данных, заполняющих кеш.

Особенности зависят от ваших целей дизайна и производительности. Интересная дискуссия по этому вопросу с конкретной проблемой «реального мира» на конкретном оборудовании с определенным компилятором, см. Подводные камни объектно-ориентированного программирования (это ссылка на Документы Google для PDF, Сам PDF можно найти здесь ).

1 голос
/ 26 мая 2010

Может ли наличие динамически распределенного v означать, что экземпляр A и его член v не расположены вместе в памяти?

Да, это, скорее всего, будет.

Какие инструменты и методы можно использовать для проверки, является ли эта фрагментация узким местом в производительности?

кашгринд, акула. ​​

Если такая фрагментация является проблемой производительности, существуют ли какие-либо методы, которые могли бы позволить A и v выделяться в непрерывной области памяти?

Да, вы могли бы распределить их вместе, но вы, вероятно, должны увидеть, если это проблема в первую очередь. Вы можете использовать распределение арены, например, или написать свои собственные распределители.

Или существуют какие-либо методы, облегчающие доступ к памяти, такие как схема предварительной выборки? например, получить объект типа A, работающий с другими переменными-членами во время предварительной выборки v.

Да, вы могли бы сделать это. Лучше всего расположить области памяти, используемые вместе, рядом друг с другом.

Если бы размер v или приемлемый максимальный размер мог быть известен во время компиляции, то замена v массивом фиксированного размера, например int v [max_length], привела бы к повышению производительности?

Может или не может. По крайней мере, это сделало бы v локальным с членами структуры.

  1. Введите код.
  2. Профиль
  3. Оптимизировать.
0 голосов
/ 26 мая 2010

Если вам нужно передать большое количество таких данных через ЦП и выполнить очень мало вычислений для каждого, как вы говорите, почему мы делаем все это распределение памяти?

Не могли бы вы просто иметь одну копию структуры и один (большой) буфер v, считывать в него свои данные (в двоичном формате, для скорости), выполнять очень маленькие вычисления и переходить к следующей .

Программа должна тратить почти 100% времени на ввод / вывод. Если вы приостановите его несколько раз во время работы, вы должны видеть это почти каждый раз в процессе вызова системной подпрограммы, такой как FileRead. Некоторые профилировщики могут предоставлять вам эту информацию, за исключением случаев, когда у них наблюдается аллергия на время ввода-вывода.

...