ключевое слово "register" в C?

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

17 ответов


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

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


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

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


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


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


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

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

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


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


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

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

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


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

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

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

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

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

  • определение объекта массива с register имеет значение undefined поведение.
    устранение: это законно, чтобы определить объект массива с register, но вы не можете сделать ничего полезного с таким объектом (индексирование в массив требует адрес его начального элемента).
  • на _Alignas спецификатор (новый в C11) не может быть применен к такому объекту.
  • если имя параметра передано в va_start макрос register-квалифицировано, поведение не определено.

может быть несколько другие; скачать проект стандарта и поиск "Регистрация", Если вы заинтересованы.

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

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


Storytime!

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

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

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

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

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

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


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

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

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


просто небольшая демонстрация (без какой-либо реальной цели) для сравнения: при удалении register ключевые слова перед каждой переменной этот фрагмент кода занимает 3,41 секунды на моем i7 (GCC), С register тот же код завершается через 0,7 секунды.

#include <stdio.h>

int main(int argc, char** argv) {

     register int numIterations = 20000;    

     register int i=0;
     unsigned long val=0;

    for (i; i<numIterations+1; i++)
    {
        register int j=0;
        for (j;j<i;j++) 
        {
            val=j+i;
        }
    }
    printf("%d", val);
    return 0;
}

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

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


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


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

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

Так много людей ошибочно рекомендуют не использовать ключевое слово register.

давайте посмотрим почему!

ключевое слово register имеет связанный побочный эффект: вы не можете ссылаться (получить адрес) переменной типа регистра.

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

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

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

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

c_register_side_effect_performance_boost


компилятор Microsoft Visual C++ игнорирует register ключевое слово, когда включена глобальная оптимизация распределения регистров (флаг компилятора /OE).

посмотреть регистрация сайта на MSDN.


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


я протестировал ключевое слово 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


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

еще одна вещь: если вы объявляете переменную как регистр, вы не можете получить ее адрес, поскольку она не хранится в памяти. он получает свое распределение в CPU реестр.