Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
+ООП_Навч_посібник.doc
Скачиваний:
8
Добавлен:
01.07.2025
Размер:
6.58 Mб
Скачать

9.2.4. Регістрові змінні

Можливо, найчастіше використовується специфікатор класу пам'яті register. Для компілятора модифікатор register означає розпорядження забезпечити таке зберігання відповідної змінної, щоб доступ до неї можна було отримати максимально швидко. Зазвичай змінна у цьому випадку зберігатиметься або в регістрі центрального процесора (ЦП), або в кеші (швидкодіючій буферній пам'яті невеликої місткості). Ймовірно, Ви знаєте, що доступ до регістрів ЦП (або до кеш-пам'яті) принципово швидший, ніж доступ до основної пам'яті комп'ютера. Таким чином, змінну, що зберігається в регістрі, буде обслужено набагато швидше, ніж змінну, що зберігається, наприклад, в оперативній пам'яті (ОЗП). Оскільки швидкість, з якою до змінних можна отримати доступ, визначає, по суті, швидкість виконання Вашої програми, то для отримання задовільних результатів програмування важливо розумно використовувати специфікатор класу пам'яті register.

Специфікатор класу пам'яті register в визначенні змінної означає вимогу оптимізувати код для отримання максимально можливої швидкості доступу до неї.

Формально специфікатор класу пам'яті register є тільки запитом, який компілятор має право проігнорувати. Це легко пояснити: адже кількість регістрів (або пристроїв пам'яті з малим часом вибірки) обмежена, причому для різних середовищ вона може бути різною. Тому, якщо компілятор вичерпає пам'ять швидкого доступу, то він зберігатиме register-змінні звичайним способом. У загальному випадку незадоволений register-запит не зашкодить, але, звичайно ж, і не дає ніяких переваг зберігання в регістровій пам'яті.

Оскільки насправді тільки для обмеженої кількості змінних можна забезпечити швидкий доступ, то важливо ретельно вибрати, до яких з них застосувати модифікатор register1. Як правило, ніж частіше до змінної потрібен доступ, тим більша вигода буде отримана внаслідок оптимізації коду програми за допомогою специфікатора класу пам'яті register. Тому оголошувати регістровими має сенс змінні параметри циклу, або змінні, до яких здійснюється доступ в тілі циклу. На прикладі наведеної нижче функції показано, як register-змінна типу int використовують для керування циклом. Ця функція обчислює результат виразу me для цілочисельних значень із збереженням знаку початкового числа (тобто при m = -2 і е = 2 результат буде дорівнювати -4).

int signed_pwr(register int m, register int e)

{

register int tmp;

int znak;

if(m < 0) znak = -1;

else znak = 1;

tmp = 1;

for( ; e; e--) tmp = tmp * m;

return tmp * znak;

}

У наведеному прикладі змінні m, e і tmp оголошені як регістрові, оскільки всі вони використовуються в тілі циклу, і тому до них часто здійснюється доступ. Проте змінна znak оголошена без специфікатора класу пам'яті register, оскільки вона не є частиною циклу і використовується рідше.

9.2.5. Походження модифікатора register

Модифікатор register був вперше визначений у мові С. Спочатку він застосовувався тільки до змінних типу int і char або до покажчиків і примушував зберігати змінні цього типу в регістрі ЦП, а не в ОЗП, де зберігаються звичайні змінні. Це означало, що операції з регістровими змінними могли виконуватися набагато швидше, ніж операції з іншими (що зберігаються в пам'яті), оскільки для запиту або модифікації їх значень не потрібен був доступ до пам'яті.

Після стандартизації мови C було ухвалене рішення розширити визначення специфікатора класу пам'яті register. Згідно з ANSI-стандартом мови С, модифікатор register можна застосовувати до будь-якого типу даних. Його використання стало означати для компілятора вимогу зробити доступ до змінної типу register максимально швидким. Для ситуацій, що містять символи і цілочисельні значення, це, як і раніше, означає занесення їх у регістри ЦП, тому традиційне визначення все ще є в силі. Оскільки мова C++ побудована на ANSI-стандарті мови С, то він також підтримує розширене визначення специфікатора класу пам'яті register.

Як ми вже зазначали вище, точна кількість register-змінних, які реально будуть оптимізовані в будь-якій одній функції, визначається як типом процесора, так і конкретною реалізацією мови програмування C++, яку Ви використовуєте. У загальному випадку можна розраховувати принаймні на дві. Проте не варто турбуватися про те, що Ви могли оголосити дуже багато register-змінних, оскільки мова C++ автоматично перетворить регістрові змінні на нерегістрові, коли їх ліміт буде вичерпаний. Це гарантує перенесність С++-коду програми у межах широкого діапазону процесорів.

Щоб показати вплив, що надається register-змінними на швидкодію програми, у наведеному нижче прикладі вимірюється час виконання двох циклів for, які відрізняються один від одного тільки типом змінних, що ним керують. У програмі використовується стандартна бібліотечна С++-функція clock(), яка повертає кількість імпульсів сигналу часу системного годинника, підрахованого з початку виконання цієї програми. Програма повинна містити заголовок <ctime>.

Код програми 9.6. Демонстрація впливу використання register-змінної на швидкість виконання програми

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

#include <ctime> // Для використання системного часу і дати

using namespace std; // Використання стандартного простору імен

unsigned int i; // Не register-змінна

unsigned int delay;

int main()

{

register unsigned int j;

long start, end;

start = clock();

for(delay=0; delay<50; delay++)

for(i=0; i < 64000000; i++);

end = clock();

cout << "Кількість тиків для не register-циклу: ";

cout << end-start << "\n";

start = clock();

for(delay=0; delay<50; delay++)

for(j=0; j < 64000000; j++);

end = clock();

cout << "Кількість тиків для register-циклу: ";

cout << end-start << "\n";

getch(); return 0;

}

У процесі виконання цієї програми Ви переконаєтеся у тому, що цикл з "регістровим" керуванням виконується приблизно в два рази швидше, ніж цикл з керуванням "нерегістровим". Якщо Ви не побачили сподіваної різниці, це може означати, що Ваш компілятор оптимізує всі змінні. Спробуйте виконати декілька розрахунків і переконайтеся в тому, що ця різниця на якомусь із них стає очевидною.

Необхідно пам'ятати! Під час написання цього навчального посібника було використано середовище C++Builder 6, яке ігнорує ключове слово register. C++Builder 6 застосовує оптимізацію "як вважає за потрібне". Тому Ви можете не помітити впливу специфікатора класу пам'яті register на виконання попередньої програми. Проте ключове слово register все ще приймається компілятором без повідомлення про помилку. Воно просто не спонукає до ніякої дії.