"зарегистрировать" ключевое слово в C? - PullRequest
250 голосов
/ 23 февраля 2009

Что делает ключевое слово register на языке Си? Я читал, что он используется для оптимизации, но не четко определен ни в одном стандарте. Это все еще актуально, и если да, то когда бы вы его использовали?

Ответы [ 17 ]

320 голосов
/ 23 февраля 2009

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

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

64 голосов
/ 23 февраля 2009

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

Таким образом, используя register, вы ничего не выиграете (в любом случае компилятор сам решит, куда поместить переменную) и потеряет оператор & - нет причин использовать его.

32 голосов
/ 23 февраля 2009

Указывает компилятору использовать регистр ЦП вместо ОЗУ для хранения переменной Регистры находятся в центральном процессоре и имеют гораздо более быстрый доступ, чем оперативная память. Но это всего лишь предложение для компилятора, и оно может не выполняться.

20 голосов
/ 17 декабря 2012

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


В последней версии стандарта C ++ 11, N3485 , об этом говорится в 7.1.1 / 3:

Спецификатор

register является подсказкой для реализации, что объявленная переменная будет активно использоваться. [ note: Подсказка может быть проигнорирована, и в большинстве реализаций она будет игнорироваться, если будет взят адрес переменной. Это использование устарело ... - конец примечания ]

В C ++ (но не в C) стандарт не устанавливает, что вы не можете взять адрес переменной, объявленной register; однако из-за того, что переменная, хранящаяся в регистре ЦП в течение всего времени ее существования, не имеет связанной с ней ячейки памяти, попытка получить ее адрес будет недействительной, и компилятор будет игнорировать ключевое слово register, чтобы разрешить получение адреса.

16 голосов
/ 23 февраля 2009

Это не имеет значения по крайней мере 15 лет, так как оптимизаторы принимают лучшие решения по этому поводу, чем вы можете. Даже когда это было уместно, в архитектуре ЦП с большим количеством регистров было гораздо больше смысла, чем в SPARC или M68000, чем в Intel с небольшим количеством регистров, большинство из которых зарезервированы компилятором для собственных целей.

12 голосов
/ 17 декабря 2009

На самом деле, регистр сообщает компилятору, что переменная не имеет псевдонима все остальное в программе (даже не символы).

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

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

12 голосов
/ 30 марта 2015

Я читал, что он используется для оптимизации, но не определен четко ни в одном стандарте.

На самом деле является , четко определенным стандартом C. Цитируя N1570 черновик раздел 6.7.1, пункт 6 (другие версии имеют такую ​​же формулировку):

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

Унарный оператор & нельзя применять к объекту, определенному с помощью register, а register нельзя использовать во внешнем объявлении.

Существует несколько других (довольно неясных) правил, специфичных для register -квалифицированных объектов:

  • Определение объекта массива с помощью register имеет неопределенное поведение.
    Исправление: Допустимо определять объект массива с помощью register, но с таким объектом ничего полезного сделать нельзя (для индексации в массиве требуется адрес его начального элемента).
  • Спецификатор _Alignas (новый в C11) нельзя применять к такому объекту.
  • Если имя параметра, переданное макросу va_start, квалифицировано register, поведение не определено.

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

Как следует из названия, оригинальное значение register должно было требовать сохранения объекта в регистре процессора. Но с улучшениями в оптимизации компиляторов это стало менее полезным. Современные версии стандарта C не ссылаются на регистры ЦП, потому что они больше (не должны) предполагать, что есть такая вещь (есть архитектуры, которые не используют регистры). Распространено мнение, что применение register к объявлению объекта с большей вероятностью ухудшит сгенерированный код, поскольку оно мешает распределению регистров собственного компилятора. Еще может быть несколько случаев, когда это полезно (скажем, если вы действительно знаете, как часто к переменной обращаются, и ваши знания лучше, чем то, что может вычислить современный оптимизирующий компилятор).

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

6 голосов
/ 09 мая 2017

Storytime!

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

Но C - это только абстракция. И, в конечном итоге, из you извлекается язык ассемблера. Сборка - это язык, который читает процессор, и если вы используете его, вы делаете вещи с точки зрения процессора. Что делает процессор? По сути, он читает из памяти, выполняет математику и пишет в память. Процессор не просто выполняет математические операции с числами в памяти. Во-первых, вам нужно переместить число из памяти в память внутри ЦП, называемое регистр . Когда вы закончите делать с этим номером все, что вам нужно, вы можете переместить его обратно в обычную системную память. Зачем вообще использовать системную память? Количество регистров ограничено. Вы получаете только около ста байтов в современных процессорах, а старые популярные процессоры были еще более фантастически ограничены (6502 имели 3 8-битных регистра для бесплатного использования). Итак, ваша средняя математическая операция выглядит так:

load first number from memory
load second number from memory
add the two
store answer into memory

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

Когда вы объявляете переменную register, вы говорите компилятору: «Йо, я намерен использовать эту переменную много и / или быть недолгим. На вашем месте я бы попытался сохранить ее в регистр. " Когда стандарт C говорит, что компиляторы на самом деле ничего не должны делать, это потому, что стандарт C не знает, для какого компьютера вы компилируете, и это может быть похоже на 6502 выше, где все 3 регистра необходимы просто для работы и нет запасного регистра, чтобы сохранить ваш номер. Однако, когда он говорит, что вы не можете взять адрес, это потому, что регистры не имеют адресов. Это руки процессора. Поскольку компилятору не нужно давать вам адрес, и поскольку у него вообще не может быть адреса, для компилятора теперь доступно несколько оптимизаций. Это может, скажем, сохранить номер в реестре всегда. Ему не нужно беспокоиться о том, где он хранится в памяти компьютера (кроме необходимости вернуть его обратно). Он может даже поместить его в другую переменную, передать другому процессору, изменить местоположение и т. Д.

tl; dr: Краткосрочные переменные, которые много математики. Не объявляйте слишком много сразу.

4 голосов
/ 23 февраля 2009

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

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

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

4 голосов
/ 29 апреля 2018

Я проверил ключевое слово register в QNX 6.5.0, используя следующий код:

#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/neutrino.h>
#include <sys/syspage.h>

int main(int argc, char *argv[]) {
    uint64_t cps, cycle1, cycle2, ncycles;
    double sec;
    register int a=0, b = 1, c = 3, i;

    cycle1 = ClockCycles();

    for(i = 0; i < 100000000; i++)
        a = ((a + b + c) * c) / 2;

    cycle2 = ClockCycles();
    ncycles = cycle2 - cycle1;
    printf("%lld cycles elapsed\n", ncycles);

    cps = SYSPAGE_ENTRY(qtime) -> cycles_per_sec;
    printf("This system has %lld cycles per second\n", cps);
    sec = (double)ncycles/cps;
    printf("The cycles in seconds is %f\n", sec);

    return EXIT_SUCCESS;
}

Я получил следующие результаты:

-> 807679611 прошедших циклов

-> Эта система имеет 3300830000 циклов в секунду

-> Количество циклов в секундах составляет ~ 0,244600

.

А теперь без регистра int:

int a=0, b = 1, c = 3, i;

Я получил:

-> 1421694077 прошедших циклов

-> Эта система имеет 3300830000 циклов в секунду

-> Количество циклов в секундах составляет ~ 0,430700

...