2009-02-23 4 views
216

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

+33

* Что делает ключевое слово register в C? * Игнорируется :) – bestsss

+15

@bestsss Не полностью игнорируется. Попробуйте получить адрес переменной register. – qrdl

+2

Этот код, который вы читаете, является * старым * https://www.youtube.com/watch?v=ibF36Yyeehw#t=1827 –

ответ

283

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

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

+14

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

+69

Хорошая причина, по которой _not_ использовать 'register': вы не можете взять адрес переменной, объявленной 'register' –

+17

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

33

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

+7

Стоит добавить для людей, использующих C++, C++ позволяет вам принимать адрес переменной регистра – Will

+4

@ Будет: ... но компилятор, скорее всего, проигнорирует ключевое слово в результате. См. Мой ответ. – bwDraco

+0

Да, кажется, что «register» - это плацебо в C++, он просто позволяет скомпилировать C-код как C++. И было бы нецелесообразно запрещать & var, позволяя передавать его по ссылке или const-reference, и без перекрестной ссылки вы серьезно нарушили C++. – greggo

16

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

2

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

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

2

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

4

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

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

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

+12

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

+2

@JUSTMYcorrectOPINION Действительно, X86 имеет в основном шесть, оставляя максимум 1 или 2 для преданности «регистрации». На самом деле, поскольку так много кода было написано или перенесено на машину с плохой регистрацией, я подозреваю, что это сильно повлияло на то, что ключевое слово «register» становится плацебо - нет смысла указывать регистры, когда их нет.Здесь мы находимся 4+ года спустя, и к счастью x86_64 увеличил его до 14, и теперь ARM - большая вещь. – greggo

58

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

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

+74

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

+2

@Alexandre C. Достаточно честный. Я не думал об этом. – qrdl

+6

Компиляторы, как известно, ужасно доказывают, что псевдонимы не встречаются в нетривиальных случаях, поэтому «register» полезен для этого, даже если компилятор не помещает его в регистр. –

12

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

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

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

+0

"говорит компилятору .." Нет, нет. Все автоматические переменные имеют это свойство, если вы не берете его адрес * и * не используйте его способами, которые превышают определенные анализируемые применения. Таким образом, компилятор знает это из кода, независимо от того, используете ли вы ключевое слово register. Так получилось, что ключевое слово «register» делает незаконным запись такой конструкции, но если вы не используете ключевое слово и * dont * берете адрес таким образом, то компилятор все еще знает, что это безопасно. Такая информация имеет решающее значение для оптимизации. – greggo

+1

@greggo: Слишком плохо 'register' запрещает принимать адрес вообще, так как в противном случае было бы полезно сообщить компиляторам о случаях, когда компилятор сможет применять оптимизацию регистров * несмотря на то, что * адрес переменной передается внешней функции (переменная должна быть сброшена в память * для этого конкретного вызова *, но как только функция вернется, компилятор снова может рассматривать ее как переменную, адрес которой никогда не был взят). – supercat

+0

@supercat Я думаю, все равно будет очень сложный разговор с компилятором. Если это то, что вы хотите сообщить компилятору, вы можете сделать это, скопировав первую переменную на вторую, которая не имеет на ней «&», а затем никогда больше не будет использовать первый. – greggo

1

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

См. register Keyword на MSDN.

1

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

0

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

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

18

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


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

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

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

10

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

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

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

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

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

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

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

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

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

+0

так что поведение программы [this] (http://ideone.com/QIatqM) действительно не определено в соответствии со стандартом C? Является ли он хорошо определенным в C++? Я думаю, что он хорошо определен в C++. – Destructor

+0

@Destructor: Почему это не определено? Нет никакого объекта регистрации, зарегистрированного в регистре, если это то, о чем вы думаете. –

+0

Ой, я забыл написать ключевое слово register в объявлении массива в main(). Является ли он хорошо определенным в C++? – Destructor

2

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

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

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

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

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

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

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

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

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

c_register_side_effect_performance_boost

4

Просто немного демо (без какой-либо цели в реальном мире) для сравнения: при удалении 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; 
} 
+2

С gcc 4.8.4 и -O3, я не понимаю. Без -O3 и 40000 итераций я получаю * возможно * 50 мс меньше на общее время 1,5 с, но я не запускал его достаточно времени, чтобы узнать, было ли это даже статистически значимым. – zstewart

+0

Нет никакой разницы с CLANG 5.0, платформа AMD64. (Я проверил выход ASM.) – ern0

2

Storytime!

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

Но C - это только абстракция. И, в конечном счете, то, что он извлекает из , вы является языком ассемблера. Assembly - это язык, который читает процессор, и если вы его используете, вы делаете что-то с точки зрения процессора. Что делает процессор? В принципе, он читает из памяти, выполняет математику и записывает в память. CPU не просто выполняет математику по номерам в памяти. Во-первых, вам нужно переместить номер из памяти в память внутри процессора, называемого регистром . Как только вы закончите делать все, что вам нужно сделать, чтобы это число, вы можете переместить его обратно в обычную системную память. Зачем использовать системную память? Регистры ограничены по количеству. В современных процессорах вы получаете около ста байтов, а более старые популярные процессоры были еще более фантастически ограничены (у 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: Краткоживущие переменные, которые выполняют много математики. Не объявляйте слишком много сразу.

Смежные вопросы