Подтвердить что ты не робот

С++ производительность доступа к переменным-членам по сравнению с локальными переменными

Является ли более эффективным для класса доступ к переменным-членам или локальным переменным? Например, предположим, что у вас есть метод (обратный вызов), который несет единственную ответственность за получение данных, выполнение вычислений на нем, а затем передачу его другим классам. По производительности, имеет ли смысл иметь список переменных-членов, которые этот метод заполняет при получении данных? Или просто объявляйте локальные переменные каждый раз, когда вызывается метод обратного вызова?

Предположим, что этот метод будет называться сотни раз в секунду...

В случае, если я не понимаю, вот несколько быстрых примеров:

// use local variables
class thisClass {
    public:
        void callback( msg& msg )
        {
            int varA;
            double varB;
            std::string varC;
            varA = msg.getInt();
            varB = msg.getDouble();
            varC = msg.getString();

            // do a bunch of calculations
         }

};

// use member variables
class thisClass {
    public:
        void callback( msg& msg )
        {
             m_varA = msg.getInt();
             m_varB = msg.getDouble();
             m_varC = msg.getString();

             // do a bunch of calculations
        }

    private:
        int m_varA;
        double m_varB;
        std::string m_varC;

};
4b9b3361

Ответ 1

Резюме: практически во всех сценариях это не имеет значения, но для локальных переменных есть небольшое преимущество.

Предупреждение. Вы оптимизируете микро-оптимизацию. В итоге вы потратите часы, пытаясь понять код, который должен выиграть наносекунду.

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

Предупреждение. Во-первых, второе и последнее правило оптимизации: measure!


Прежде всего, посмотрите на типичную сборку, созданную для x86 (ваша платформа может меняться):

// stack variable: load into eax
mov eax, [esp+10]

// member variable: load into eax
mov ecx, [adress of object]
mov eax, [ecx+4]

Как только адрес объекта загружен, введите регистр, инструкции идентичны. Загрузка адреса объекта обычно может быть сопряжена с более ранней инструкцией и не ударяет по времени выполнения.

Но это означает, что регистр ecx недоступен для других оптимизаций. Тем не менее, современные процессоры делают некоторые интенсивные обманки, чтобы сделать это менее проблематичным.

Кроме того, при доступе к многим объектам это может стоить дополнительно. Тем не менее, это меньше, чем одно среднее значение цикла, и часто существует множество опций для инструкций по сопряжению.

Локальность памяти: вот шанс для стека выиграть большое время. Верх стека практически всегда находится в кеше L1, поэтому загрузка занимает один цикл. Скорее всего, объект будет перенаправлен в кеш-память L2 (правило 10, 10 циклов) или основная память (100 циклов).

Однако вы платите это только за первый доступ. если все, что у вас есть, - это единственный доступ, то 10 или 100 циклов остаются незаметными. если у вас тысячи доступов, данные объекта также будут в кеше L1.

Таким образом, выигрыш настолько мал, что практически не имеет смысла копировать переменные-члены в локальные сети для достижения лучшей производительности.

Ответ 2

Я бы предпочел локальные переменные на общих принципах, потому что они сводят к минимуму изменчивое состояние зла в вашей программе. Что касается производительности, ваш профилировщик расскажет вам все, что вам нужно знать. Местные жители должны быть быстрее для ints и, возможно, других встроенных функций, потому что они могут быть помещены в регистры.

Ответ 3

Глупый вопрос.
Все зависит от компилятора и от того, что он делает для оптимизации.

Даже если это сработало, что вы получили? Способ обфускации вашего кода?

Переменный доступ обычно выполняется с помощью указателя и смещения.

  • Указатель на объект + смещение
  • Указатель на Stack Frame + offset

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

Ответ 4

Это должна быть ваша проблема с компиляторами. Вместо этого оптимизируйте поддержку: если информация используется только локально, сохраните ее в локальных (автоматических) переменных. Я ненавижу классы чтения, замусоренные переменными-членами, которые на самом деле ничего не говорят мне о самом классе, но только некоторые подробности о том, как сработает множество методов: (

На самом деле, я был бы удивлен, если локальные переменные не будут ускорены в любом случае - они должны быть в кеше, поскольку они близки к остальным функциям данных (кадр вызова), а указатель объектов может быть где-то полностью else - но я просто угадываю здесь.

Ответ 5

В моем аргументе он не должен влиять на производительность, потому что:

  • В первом примере переменные доступны через поиск в стеке, например. [ESP] +4, что означает текущий конец стека плюс четыре байта.
  • Во втором примере доступ к переменным осуществляется с помощью поиска относительно этого (помните, что varB равен this- > varB). Это аналогичная машинная инструкция.

Поэтому нет большой разницы.

Однако вам следует избегать копирования строки;)

Ответ 6

Объем данных, с которыми вы будете взаимодействовать, будет иметь большее влияние на скорость выполнения, чем то, как вы представляете данные при реализации алгоритма.

Процессору действительно неважно, находятся ли данные в стеке или в куче (кроме вероятности того, что верхняя часть стека будет в кэше процессора, как упомянуто в п. Петрчен), но для максимальной скорости данные будут иметь чтобы вписаться в кеш процессора (кеш L1, если у вас более одного уровня кеша, что почти у всех современных процессоров). Любая загрузка из кеша L2 - или $DEITY запрещает, основная память - замедляет выполнение. Поэтому, если вы обрабатываете строку размером в несколько сотен КБ и получаете шансы на каждый вызов, разница даже не будет измерима.

Имейте в виду, что в большинстве случаев 10% -ное ускорение в программе довольно неопределимо для конечного пользователя (если вам не удастся сократить время выполнения вашей ночной партии с 25 часов до менее чем за 24 часа), так что это не если вы не уверены и имеете выход профилировщика для резервного копирования, что этот конкретный фрагмент кода находится в пределах 10% -20% "горячей зоны", который имеет большое влияние на время выполнения вашей программы.

Другие соображения должны быть более важными, такими как ремонтопригодность или другие внешние факторы. Например, если вышеприведенный код содержит многопотоковый код, использование локальных переменных может облегчить реализацию.

Ответ 7

Это зависит, но я ожидаю, что не будет никакой разницы.

Важно следующее: использование переменных-членов в качестве временных файлов сделает ваш код не реентерабельным - например, он будет терпеть неудачу, если два потока попытаются вызвать callback() на одном и том же объекте. Использование статических локалей (или статических переменных-членов) еще хуже, потому что тогда ваш код будет терпеть неудачу, если два потока попытаются вызвать callback() на любом объекте thisClass - или потомке.

Ответ 8

Несколько пунктов, которые не были указаны явно другими:

  • Вы потенциально вызываете операторы присваивания в свой код. например varC = msg.getString();

  • У вас есть несколько потраченных впустую циклов каждый раз, когда устанавливается функциональный кадр. Вы создаете переменные, вызываемые конструктором по умолчанию, а затем вызываете оператор присваивания, чтобы получить значение RHS в локалях.

  • Объявить locals как const-ref и, конечно же, инициализировать их.

  • Элементы-члены могут находиться в куче (если ваш объект был выделен там) и, следовательно, страдают от нелокальности.

  • Даже несколько сохраненных циклов хороши - зачем тратить время на вычисления, если бы вы могли его избежать.

Ответ 9

Если вы сомневаетесь, сравните и убедитесь сами. И убедитесь, что это имеет значение в первую очередь - сотни раз в секунду не является большой нагрузкой для современного процессора.

Тем не менее, я не думаю, что будет какая-то разница. Оба будут постоянными смещениями от указателя, locals будут из указателя стека, и члены будут от указателя "this".

Ответ 10

Использование переменных-членов должно быть незначительно быстрее, поскольку они должны быть выделены только один раз (когда объект построен), а не каждый раз, когда вызывается обратный вызов. Но по сравнению с остальной частью работы, которую вы, вероятно, делаете, я ожидаю, что это будет очень крошечный процент. Benckmark и посмотреть, что быстрее.

Ответ 11

Также есть третий вариант: статические локали. Они не перераспределяются каждый раз при вызове функции (фактически, они сохраняются во всех вызовах), но они не загрязняют класс избыточными переменными-членами.