Как сохранить переменную С++ в регистре

Я хотел бы получить некоторое разъяснение относительно вопроса о хранении регистровых переменных: Есть ли способ гарантировать, что если мы объявили переменную регистра в нашем коде, что она будет ТОЛЬКО храниться в регистре?

#include<iostream>
using namespace std;
int main()
{
register int i=10;// how can we ensure this will store in register only.
i++;
cout<<i<<endl;
return 0;
}

Ответ 1

Вы не можете. Это всего лишь намек на компилятор, который предполагает, что переменная сильно используется. Здесь формулировка C99:

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

И вот формулировка С++ 11:

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

Фактически, спецификатор класса register устаревает в С++ 11 (Приложение D.2):

Использование ключевого слова register в качестве спецификатора класса хранения (7.1.1) устарело.

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

Многие современные компиляторы просто игнорируют ключевое слово register в С++ (если оно не используется недействительным способом, конечно). Они намного лучше оптимизируются, чем когда ключевое слово register было полезным. Я бы ожидал, что компиляторы для целевых платформ будут более серьезно относиться к этому.

Ответ 2

Ключевое слово register имеет разные значения в C и С++. В С++ это фактически избыточно и, кажется, даже сейчас не рекомендуется.

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

Используя register, как в C, систематически заставляет вас думать обо всех местах, где вы берете адрес переменной. Вероятно, это не то, что вы хотели бы сделать на С++, который в значительной степени полагается на ссылки на объекты и тому подобное. Это может быть причиной того, что С++ не копировал это свойство переменных register из C.

Ответ 3

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

Ответ 4

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

Используйте правильный уровень оптимизации, например. -O2

Сохраняйте количество переменных

register int a,b,c,d,e,f,g,h,i, ... z;  // can also produce an error
// results in _spilling_ a register to stack
// as the CPU runs out of physical registers

Не принимайте адрес переменной регистра.

register int a;
int *b = &a;  /* this would be an error in most compilers, but
                 especially in the embedded world the compilers
                 release the restrictions */

В некоторых компиляторах вы можете предложить

register int a asm ("eax");  // to put a variable to a specific register

Ответ 5

Ключевое слово "register" - это остаток времени, когда компиляторы должны были помещаться на машинах с 2 МБ ОЗУ (разделяемых между 18 терминалами с зарегистрированным пользователем на каждом). Или ПК/домашние компьютеры с оперативной памятью 128-256 Кбайт. В этот момент компилятор не смог запустить большую функцию, чтобы выяснить, какой регистр использовать для какой переменной, чтобы использовать регистры наиболее эффективно. Поэтому, если программист дал "подсказку" с register, компилятор поставил бы это в регистр (если это возможно).

Современные компиляторы не подходят несколько раз в 2 МБ ОЗУ, но они намного умнее при назначении переменных в регистры. В приведенном примере я нахожу очень неактивным, что компилятор не помещает его в регистр. Очевидно, что регистры ограничены по числу, и, учитывая достаточно сложную часть кода, некоторые переменные не будут вписываться в регистры. Но для такого простого примера современный компилятор сделает регистратор i, и он, вероятно, не коснется памяти, пока где-нибудь внутри ostream& ostream::operator<<(ostream& os, int x).

Ответ 6

Обычно компиляторы CPP (g++) выполняют довольно много оптимизаций для кода. Поэтому, когда вы объявляете регистрационную переменную, нет необходимости, чтобы компилятор сохранил это значение непосредственно в регистре. (т.е. код "register int x" не может привести к тому, что компилятор сохранит этот int непосредственно в регистре. Но если мы можем заставить компилятор сделать это, мы можем быть успешными.

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

int main() {
    volatile register int x asm ("eax"); 
    int y = *(&x);
    return 0;
}

Для меня компилятор g++ бросает в этом случае следующую ошибку.

[[email protected] cpp]$ g++ register_vars.cpp 
register_vars.cpp: In function ‘int main()’:
register_vars.cpp:3: error: address of explicit register variable ‘x’ requested

Строка "volatile register int x asm (" eax ")" инструктирует компилятор, чтобы сохранить целое число x в регистре "eax" и при этом не делать никаких оптимизаций. Это позволит убедиться, что значение хранится в регистре напрямую. Вот почему доступ к адресу переменной вызывает ошибку.

В качестве альтернативы, компилятор C (gcc) может выходить из строя с помощью самого кода.

int main() {
    register int a=10;
    int c = *(&a);
    return 0;
}

Для меня компилятор gcc генерирует следующую ошибку в этом случае.

[[email protected] cpp]$ gcc register.c 
register.c: In function ‘main’:
register.c:5: error: address of register variable ‘a’ requested

Ответ 7

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

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

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

Ответ 8

Здесь вы можете использовать volatile register int i = 10 в С++ для обеспечения сохранения i в регистре. Ключевое слово volatile не позволяет компилятору оптимизировать переменную i.