Сколько времени стоит сэкономить на стоимости процессора? Скажем, у меня есть вычисленное значение x, которое я буду использовать 2 раза, 5 раз или 20 раз. В какой момент становится более оптимальным сохранять вычисленное значение вместо его пересчета каждый раз, когда я его использую?
пример:
int a=0,b=-5;
for(int i=0;i<k;++i)
a+=abs(b);
или же
int a=0,b=-5;
int x=abs(b);
for(int i=0;i<k;++i)
a+=x;
При каком значении k второй сценарий дает лучшие результаты? Кроме того, сколько зависит это ОЗУ?
Почти невозможно дать ответ, кроме измерение в реальном сценарии. Когда вы кешируете данные в коде, они могут быть сохранены в регистре (в коде, который вы предоставляете, скорее всего, будет), или они могут быть сброшены в кэш L1 или кэш L2 … в зависимости от цикла делать (сколько данных он использует?). Если значение кэшируется в регистре, то стоимость равна 0, чем дальше оно передается, тем выше стоимость, которую потребуется для получения значения.
В общем, пишите код, который легко читать и поддерживать, затем измерьте производительность приложения и, если это не очень хорошо, профилируйте. Найдите горячие точки, выясните, почему они являются горячими точками, а затем работайте дальше. Я сомневаюсь, что кеширование против вычисления abs(x)
для чего-то, как указано выше, это будет горячая точка в реальном приложении. Так что не парься.
Поскольку значение abs(b)
не изменяется внутри цикла for, компилятор, скорее всего, оптимизирует оба фрагмента до одного и того же результата, т.е. оценивая значение abs(b)
только раз.
Я хотел бы предложить (это без тестирования, обратите внимание), что пример с
int x=abs(b)
вне цикла будет быстрее просто потому, что вы избегаете выделять кадр стека каждую итерацию для вызова abs ().
При этом, если компилятор достаточно умен, он может выяснить, что вы делаете, и выдать одинаковые (или похожие) инструкции для обоих.
Как правило, хранение этого значения вне цикла не требует больших затрат, если вообще что-то происходит, поскольку компилятор, скорее всего, в любом случае собирается сохранить результат abs (x) в регистре. Фактически, когда компилятор оптимизирует этот код (при условии, что у вас включена оптимизация), первое, что он сделает, это вытянет этот abs (x) из цикла.
Кроме того, вы можете помочь компилятору сгенерировать хороший код, указав в объявлении «x» подсказку «register». Это попросит компилятор сохранить х в значение регистра, если это возможно.
Если вы хотите посмотреть, что на самом деле делает компилятор с вашим кодом, нужно сказать ему, чтобы он компилировался, но не собирался (в gcc, опция -S) и просматривал полученный код сборки. Во многих случаях компилятор генерирует лучший код, чем вы можете оптимизировать вручную. Тем не менее, также нет причин НЕ делать эти простые оптимизации самостоятельно.
Приложение:
Компиляция приведенного выше кода с включенной оптимизацией в GCC приведет к коду, эквивалентному следующему:
a = abs(b) * k;
Попробуйте и посмотрите.
Во многих случаях это дает лучшую производительность от k = 2. Пример, который вы дали Не один. Большинство компиляторов пытаются выполнить этот вид подъема, когда включены даже низкие уровни оптимизации. В худшем случае это значение хранится в локальном стеке, и поэтому, скорее всего, будет оставаться достаточно кешированным, что сведет на нет проблемы с памятью.
Но потенциально это будет проходить в реестре.
Оригинал должен выполнить дополнительную ветвь, повторить вычисления и вернуть значение. Abs является одним из примеров функции, которую компилятор может распознать как constexpr и hoist.
При разработке своих собственных классов это одна из причин, по которой вы должны стараться помечать элементы и ссылки как возможные, когда это возможно.