Почему использование указателей (с низкой оптимизацией) делает программу быстрее? - PullRequest
0 голосов
/ 01 мая 2019

Я следовал учебному пособию по программированию на встроенном С, а затем понял, что использование указателя для указания на переменную, а затем его разыменование делает программу быстрее!

У меня есть базовые знания о сборке, но я не представлял, почему назначение адреса переменной указателю будет быстрее, мы не говорим о передаче по ссылке, по указателю или по значению!

Как я понимаю,

  • код без указателя: адрес памяти был присвоен регистру R0, как именно то, что произошло в коде с указателем.
  • p_int стал псевдонимом для регистра R0, как это может помочь сделать программу быстрее?

код без использования указателя:

int counter = 0;
int main() {
    while (counter < 6) {
        ++(counter);
    }
    return 0;
}

тогда сборка будет как в enter image description here

И наоборот, вот код с указателем:

int counter = 0;
int main() {
    int *p;
    p = &counter;
    while (*p < 6) {
        ++(*p);
    }
    return 0;
}

тогда сборка будет как в enter image description here


Обновление

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

Чтобы получить доступ к переменной в памяти, процессору нужен адрес этого переменная в одном из регистров. На самых низких уровнях кода оптимизация, компилятор загружает этот адрес из памяти кода перед каждым доступом к переменной. Скорость указателя это потому, что локальная переменная внутри функции main () выделен в регистр. Это означает, что адрес находится в зарегистрироваться (R0 в этом случае) и не требует загрузки и перезагружается в реестр каждый раз. На более высоких уровнях оптимизации компилятор генерирует более разумный код и код без указатель так же быстр, как с указателем. --MMS

Ответы [ 4 ]

3 голосов
/ 02 мая 2019

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

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

Что программисты должны делать и чему должны учить люди, пишущие курсы, - это писать код как можно более понятным и понятным. Это означает, что оба ваших примера плохие, поскольку они излишне неясны и являются примерами «преждевременной оптимизации». Лучше код будет:

  int counter;
  ...
  for(counter=0; counter < 6; counter++)
  {}

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

Сделайте это:

  • Напишите наиболее читаемый код, который вы можете.
  • В выпуске включить оптимизацию.
  • Если есть проблемы с производительностью, проведите тестирование и найдите узкие места.
  • Вручную оптимизируйте узкие места, если это необходимо. Возможно, с учетом конкретной системы.
1 голос
/ 01 мая 2019

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

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

0 голосов
/ 03 мая 2019

Оригинал Обновление дает хороший ответ.Кроме того, в оригинале «counter» является глобальной переменной, поэтому каждый доступ к микросхеме ARM требует сначала загрузки переменной в регистр.В зависимости от того, где находится переменная и уровня оптимизации, это как минимум одна инструкция LDR (может быть больше), тогда счетчик обновлений ++ требует инструкции add и обратной записи в глобальную переменную.

IFcounter объявлен как локальная переменная, тогда на самом деле использование версии указателя будет менее оптимальным.В этом случае большинство компиляторов выделяет счетчик в регистр, тогда доступ к нему будет очень быстрым.Если используется указатель, счетчик будет принудительно выделен стеку (поскольку его адрес назначен «p»), и для добавления и доступа потребуется больше инструкций.

0 голосов
/ 01 мая 2019

прямо напротив (почти идентично, но на одну инструкцию больше:):

https://godbolt.org/z/xDYecQ

enter image description here

...