Co registersłowo kluczowe robi w języku C? Czytałem, że jest on wykorzystywany do optymalizacji, ale nie jest jasno zdefiniowany w żadnym standardzie. Czy nadal jest to istotne, a jeśli tak, to kiedy go użyjesz?
Cóż, eksperymentowałem z rejestrem, aby ulepszyć moje zgłoszenia ACM, a czasem to naprawdę pomogło. Ale naprawdę musisz być ostrożny, ponieważ złe wybory obniżają wydajność.
ypnos
80
Dobry powód, aby nie używać „register”: nie można wziąć adresu zmiennej zadeklarowanej „register”
Adam Rosenfield
22
Zauważ, że niektóre / wiele kompilatorów całkowicie zignoruje słowo kluczowe register (co jest całkowicie legalne).
Euro Micelli
4
ypnos: W rzeczywistości szybkość rozwiązania problemów ACM ICPC zależy znacznie bardziej od wyboru algorytmu niż od takich mikrooptymalizacji. 5-sekundowy limit czasu jest zwykle wystarczający do poprawnego rozwiązania, szczególnie gdy używasz C zamiast Java.
Joey,
65
@Euro: Prawdopodobnie wiesz o tym, ale dla jasności, kompilator jest wymagany, aby zapobiec registerpobieraniu adresu zmiennej; jest to jedyny obowiązkowy efekt registersłowa kluczowego. Nawet to wystarcza, aby poprawić optymalizacje, ponieważ powiedzenie, że zmienną można modyfikować tylko w ramach tej funkcji, staje się banalne.
Dale Hagglund
69
Dziwi mnie, że nikt nie wspomniał, że nie można wziąć adresu zmiennej rejestru, nawet jeśli kompilator zdecyduje się zachować zmienną w pamięci, a nie w rejestrze.
Więc za pomocą register nic nie wygrywasz (zresztą kompilator sam decyduje, gdzie umieścić zmienną) i traci &operatora - nie ma powodu, aby go używać.
Właściwie istnieje powód. Sam fakt, że nie można wziąć adresu zmiennej, daje pewne możliwości optymalizacji: kompilator może udowodnić, że zmienna nie będzie aliasu.
Alexandre C.,
8
Kompilatory są wyjątkowo okropne w udowadnianiu, że aliasing nie występuje w nietrywialnych przypadkach, więc registerjest to przydatne, nawet jeśli kompilator nie umieści go w rejestrze.
Miles Rout
2
@AlexandreC, mile, kompilatory są całkowicie OK, sprawdzając, czy zmienna jest pobierana gdziekolwiek. Niezależnie od innych trudności związanych z wykrywaniem aliasingu, ponowne utworzenie go nic nie kosztuje. Kiedy K + R stworzony C, to rzeczywiście warto wiedzieć z góry , że i nie zostaną wykorzystane, ponieważ kompilator faktycznie podjął decyzję przydziału rejestru widząc oświadczenia przed patrząc na poniższym kodzie. Właśnie dlatego zakaz obowiązuje. Słowo kluczowe „register” jest obecnie zasadniczo przestarzałe.
greggo
25
Dzięki tej logice constjest również bezużyteczna, ponieważ nic ci nie wygrywa, tracisz jedynie możliwość zmiany zmiennej. registermożna użyć, aby upewnić się, że nikt nie przyjmie adresu zmiennej w przyszłości bez zastanowienia. Nigdy nie miałem jednak powodu, aby z tego korzystać register.
Tor Klingberg
34
Mówi kompilatorowi, aby spróbował użyć rejestru procesora zamiast pamięci RAM do przechowywania zmiennej. Rejestry znajdują się w procesorze i dostęp do nich jest znacznie szybszy niż w przypadku pamięci RAM. Jest to jednak tylko sugestia dla kompilatora i może nie zostać zrealizowana.
Warto dodać dla osób korzystających z C ++, C ++ pozwala wziąć adres zmiennej rejestru
będzie
5
@Will: ... ale kompilator prawdopodobnie w rezultacie zignoruje słowo kluczowe. Zobacz moją odpowiedź.
bwDraco,
Tak, wydaje się, że „register” jest placebo w C ++, po prostu pozwala na kompilację kodu C jako C ++. I nie ma sensu zabraniać & var, pozwalając na przekazywanie go przez referencję lub const-referen, a bez pass-by-referencji poważnie złamałeś C ++.
greggo
22
Wiem, że to pytanie dotyczy C, ale to samo pytanie dla C ++ zostało zamknięte jako dokładna kopia tego pytania. Ta odpowiedź może zatem nie dotyczyć C.
Najnowszy projekt standardu C ++ 11, N3485 , mówi o tym w 7.1.1 / 3:
Specyfikator registerjest wskazówką dla implementacji, że tak zadeklarowana zmienna będzie intensywnie używana. [ uwaga: podpowiedź można zignorować, aw większości implementacji zostanie ona zignorowana, jeśli adres zmiennej zostanie przyjęty. To użycie jest przestarzałe ... - uwaga ]
W C ++ (ale nie w C) standard nie stwierdza, że nie można pobrać adresu deklarowanej zmiennej register; jednak ponieważ zmienna przechowywana w rejestrze procesora przez cały okres jej istnienia nie ma powiązanej z nią lokalizacji w pamięci, próba przejęcia jego adresu byłaby nieprawidłowa, a kompilator zignoruje registersłowo kluczowe, aby umożliwić przyjęcie adresu.
Nie było to istotne przez co najmniej 15 lat, ponieważ optymalizatorzy podejmują lepsze decyzje w tej sprawie niż możesz. Nawet gdy było to istotne, miało to o wiele większy sens w architekturze procesora z dużą liczbą rejestrów, takich jak SPARC lub M68000, niż w przypadku Intel z niewielką liczbą rejestrów, z których większość jest zarezerwowana przez kompilator na własne potrzeby.
W rzeczywistości rejestr informuje kompilator, że zmienna nie ma aliasu z niczym innym w programie (nawet char).
To może być wykorzystywane przez współczesne kompilatory w różnych sytuacjach i może pomóc kompilatorowi w złożonym kodzie - w prostym kodzie kompilatory mogą to rozwiązać samodzielnie.
W przeciwnym razie nie służy celowi i nie jest wykorzystywany do przydzielania rejestrów. Zazwyczaj nie powoduje to obniżenia wydajności, o ile kompilator jest wystarczająco nowoczesny.
„mówi kompilatorowi…” nie, nie robi. Wszystkie zmienne automatyczne mają tę właściwość, chyba że weźmiesz jej adres i użyjesz go w sposób, który wykracza poza określone zastosowania. Kompilator wie o tym z kodu, niezależnie od tego, czy używasz słowa kluczowego register. Zdarza się, że słowo kluczowe „register” sprawia, że pisanie takiej konstrukcji jest nielegalne, ale jeśli nie użyjesz tego słowa kluczowego i nie weźmiesz adresu w taki sposób, to kompilator nadal wie, że jest bezpieczny. Takie informacje są kluczowe dla optymalizacji.
greggo
1
@greggo: Szkoda registerzabrania w ogóle przyjmowania adresu, ponieważ w przeciwnym razie przydatne może być poinformowanie kompilatorów o przypadkach, w których kompilator byłby w stanie zastosować optymalizację rejestru pomimo przekazania adresu zmiennej do funkcji zewnętrznej (zmienna musiałaby zostać opróżnione do pamięci dla tego konkretnego wywołania , ale gdy funkcja wróci, kompilator może ponownie potraktować ją jako zmienną, której adresu nigdy nie podano.
supercat
@ supercat Myślę, że nadal byłaby bardzo trudna rozmowa z kompilatorem. Jeśli właśnie to chcesz powiedzieć kompilatorowi, możesz to zrobić, kopiując pierwszą zmienną do drugiej, która nie ma na niej znaków „&”, a następnie nigdy więcej nie używając pierwszej.
greggo
1
@greggo: Mówiąc, że jeśli barjest to registerzmienna, kompilator może w swoim wolnym zastąpić foo(&bar);z int temp=bar; foo(&temp); bar=temp;, ale biorąc pod adres barbyłyby zakazane w większości innych kontekstach nie wydawać się zbyt skomplikowane reguły. Jeśli zmienna mogłaby być przechowywana w rejestrze, podstawienie zmniejszyłoby kod. Jeśli zmienna i tak musiałaby być przechowywana w pamięci RAM, podstawienie zwiększyłoby kod. Pozostawienie pytania, czy zastąpić kompilator, doprowadziłoby do lepszego kodu w obu przypadkach.
supercat
1
@greggo: Zezwolenie na registerkwalifikację zmiennych globalnych, niezależnie od tego, czy kompilator zezwala na pobranie adresu, pozwoliłoby na pewne dobre optymalizacje w przypadkach, gdy funkcja wbudowana, która używa zmiennej globalnej, jest wywoływana wielokrotnie w pętli. Nie mogę wymyślić żadnego innego sposobu, aby ta zmienna była przechowywana w rejestrze między iteracjami pętli - prawda?
supercat
13
Czytałem, że jest on wykorzystywany do optymalizacji, ale nie jest jasno zdefiniowany w żadnym standardzie.
W rzeczywistości jest to wyraźnie określone przez standard C. Cytując wersję roboczą N1570 sekcja 6.7.1 akapit 6 (inne wersje mają to samo brzmienie):
Deklaracja identyfikatora obiektu ze specyfikatorem klasy pamięci registersugeruje, że dostęp do obiektu powinien być jak najszybszy. Zakres, w jakim takie sugestie są skuteczne, zależy od wdrożenia.
Jednoargumentowy &operator nie może być stosowany do obiektu zdefiniowanego za pomocą registeri registernie może być stosowany w deklaracji zewnętrznej.
Istnieje kilka innych (dość niejasnych) reguł, które są specyficzne dla registerobiektów zakwalifikowanych:
Definiowanie obiektu tablicowego za pomocą registerniezdefiniowanego zachowania. Korekta: Definiowanie obiektu tablicy jest legalne register, ale nie można zrobić nic użytecznego z takim obiektem (indeksowanie do tablicy wymaga przyjęcia adresu jego początkowego elementu).
Specyfikator _Alignas(nowy w C11) nie może być zastosowany do takiego obiektu.
Jeśli nazwa parametru przekazana do va_startmakra jest register-kwalifikowana, zachowanie jest niezdefiniowane.
Może być kilka innych; pobierz projekt standardu i wyszukaj „zarejestruj się”, jeśli jesteś zainteresowany.
Jak sama nazwa wskazuje, pierwotnym znaczeniem registerbyło wymaganie, aby obiekt był przechowywany w rejestrze procesora. Jednak dzięki ulepszeniom w optymalizacji kompilatorów stało się to mniej przydatne. Nowoczesne wersje standardu C nie odnoszą się do rejestrów procesora, ponieważ nie muszą już (trzeba) zakładać, że istnieje coś takiego (istnieją architektury, które nie używają rejestrów). Powszechnie wiadomo, że zastosowanie registerdo deklaracji obiektowej z większym prawdopodobieństwem pogorszy generowany kod, ponieważ zakłóca to alokację rejestru przez kompilator. Może być jeszcze kilka przypadków, w których jest to przydatne (powiedzmy, jeśli naprawdę wiesz, jak często zmienna będzie dostępna, a twoja wiedza jest lepsza niż to, co może zrozumieć nowoczesny kompilator optymalizujący).
Głównym namacalnym efektem registerjest to, że uniemożliwia jakąkolwiek próbę przejęcia adresu obiektu. Nie jest to szczególnie przydatne jako wskazówka optymalizacyjna, ponieważ można ją zastosować tylko do zmiennych lokalnych, a kompilator optymalizujący może przekonać się, że adres takiego obiektu nie jest brany.
więc czy zachowanie tego programu jest naprawdę niezdefiniowane zgodnie ze standardem C? Czy jest dobrze zdefiniowany w C ++? Myślę, że jest dobrze zdefiniowany w C ++.
Destructor
@Destructor: Dlaczego byłby niezdefiniowany? Nie ma registerniewykwalifikowanego obiektu tablicowego, jeśli tak myślisz.
Keith Thompson,
Och przepraszam, zapomniałem napisać słowo kluczowe register w deklaracji tablicy w main (). Czy jest dobrze zdefiniowany w C ++?
Destructor
Myliłem się, definiując registerobiekty tablicowe; zobacz zaktualizowany pierwszy punkt w mojej odpowiedzi. Zdefiniowanie takiego obiektu jest legalne, ale nie można z nim nic zrobić. Jeśli dodasz registerdo definicji sw swoim przykładzie , program jest nielegalny (naruszenie ograniczenia) w C. C ++ nie nakłada takich samych ograniczeń register, więc program będzie poprawny w C ++ (ale używanie registerbyłoby bezcelowe).
Keith Thompson,
@KeithThompson: Słowo registerkluczowe mogłoby służyć pożytecznemu celowi, gdyby przyjęcie adresu takiej zmiennej było zgodne z prawem, ale tylko w przypadkach, w których semantyka nie miałaby wpływu na kopiowanie zmiennej do tymczasowej, gdy jej adres jest pobierany, i ponowne ładowanie jej z tymczasowej w następnym punkcie sekwencji. Pozwoliłoby to kompilatorom założyć, że zmienna może być bezpiecznie przechowywana w rejestrze we wszystkich dostępach do wskaźnika, pod warunkiem że zostanie opróżniona w dowolnym miejscu, w którym zostanie pobrany adres.
supercat
9
Opowiadanie!
C, jako język, jest abstrakcją komputera. Umożliwia robienie rzeczy w zakresie tego, co robi komputer, tj. Manipulowanie pamięcią, matematyka, drukowanie itp.
Ale C to tylko abstrakcja. I w końcu wyciąga z ciebie język asemblera. Asembler to język, który czyta procesor, a jeśli go używasz, robisz różne rzeczy pod względem procesora. Co robi procesor? Zasadniczo czyta z pamięci, robi matematykę i zapisuje w pamięci. Procesor nie tylko wykonuje matematykę na liczbach w pamięci. Najpierw musisz przenieść liczbę z pamięci do pamięci wewnątrz procesora o nazwie a rejestrem. Gdy skończysz robić wszystko, co musisz zrobić z tym numerem, możesz przenieść go z powrotem do normalnej pamięci systemowej. Po co w ogóle korzystać z pamięci systemowej? Liczba rejestrów jest ograniczona. W nowoczesnych procesorach dostajesz tylko około sto bajtów, a starsze popularne procesory były jeszcze bardziej fantastycznie ograniczone (6502 miał 3 8-bitowe rejestry do bezpłatnego użytku). Twoja średnia operacja matematyczna wygląda następująco:
load first number from memory
load second number from memory
add the two
store answer into memory
Wiele z nich to ... nie matematyka. Te operacje ładowania i przechowywania mogą zająć nawet połowę czasu przetwarzania. C, będący abstrakcją komputerów, uwolnił programistę od zmartwień związanych z używaniem i żonglowaniem rejestrami, a ponieważ liczba i typ różnią się między komputerami, C nakłada odpowiedzialność za przydzielanie rejestrów wyłącznie na kompilator. Z jednym wyjątkiem.
Kiedy deklarujesz zmienną register, mówisz kompilatorowi: „Tak, zamierzam, aby ta zmienna była często używana i / lub krótkotrwała. Gdybym był tobą, spróbowałbym zachować ją w rejestrze”. Kiedy standard C mówi, że kompilatory nie muszą właściwie nic robić, to dlatego, że standard C nie wie, dla jakiego komputera kompilujesz, i może być tak jak w 6502 powyżej, gdzie wszystkie 3 rejestry są potrzebne tylko do działania , i nie ma rezerwowego rejestru, aby zachować Twój numer. Jednak gdy mówi, że nie możesz wziąć adresu, to dlatego, że rejestry nie mają adresów. To ręce procesora. Ponieważ kompilator nie musi podawać adresu, a ponieważ nigdy nie może mieć adresu, dla niego dostępnych jest kilka optymalizacji. Może, powiedzmy, zawsze przechowywać numer w rejestrze. Nie robi muszę się martwić, gdzie jest przechowywany w pamięci komputera (poza koniecznością odzyskania go ponownie). Może nawet przebić ją do innej zmiennej, przekazać innemu procesorowi, zmienić lokalizację itp.
tl; dr: Zmienne krótkotrwałe, które wykonują wiele obliczeń matematycznych. Nie deklaruj zbyt wielu naraz.
Masz problem z wyrafinowanym algorytmem kolorowania grafik kompilatora. Służy do przydzielania rejestrów. Cóż, głównie. Działa jako wskazówka dla kompilatora - to prawda. Ale nie ignorowane w całości, ponieważ nie wolno ci przyjmować adresu zmiennej rejestru (pamiętaj, że kompilator, teraz na twoją łaskę, spróbuje działać inaczej). Co w pewien sposób mówi ci, żebyś go nie używał.
Słowo kluczowe zostało użyte dawno, dawno temu. Gdy rejestrów było tak mało, że można je było policzyć za pomocą palca wskazującego.
Ale, jak powiedziałem, przestarzałe nie oznacza, że nie możesz go użyć.
Niektóre starsze urządzenia miały więcej rejestrów niż współczesne maszyny Intela. Liczba rejestrów nie ma nic wspólnego z wiekiem, a wszystko z architekturą procesora.
WŁAŚNIE MOJA poprawna OPINIA,
2
@JUSTMYcorrectOPINION Rzeczywiście, X86 ma w zasadzie sześć, pozostawiając co najwyżej 1 lub 2 na dedykację do „rejestracji”. W rzeczywistości, skoro tyle kodu zostało napisanych lub przeniesionych na maszynę z biednymi rejestrami, podejrzewam, że przyczyniło się to w znacznym stopniu do tego, że słowo kluczowe „register” stało się placebo - nie ma sensu sugerować rejestrom, kiedy ich nie ma. Mamy ponad 4 lata później i na szczęście x86_64 podniosło go do 14, a ARM jest teraz wielką rzeczą.
greggo
4
Tylko mała wersja demonstracyjna (bez żadnego rzeczywistego celu) do porównania: podczas usuwania registersłów kluczowych przed każdą zmienną ten fragment kodu zajmuje 3,41 sekundy na moim i7 (GCC), aregister ten sam kod kończy się w 0,7 sekundy.
Z gcc 4.8.4 i -O3 nie mam różnicy. Bez iteracji -O3 i 40000 otrzymuję może 50 ms mniej przy łącznym czasie 1,5 s, ale nie uruchomiłem go wystarczająco dużo razy, aby wiedzieć, czy to jest statystycznie znaczące.
zstewart
Z CLANG 5.0 nie ma różnicy, platforma to AMD64. (Sprawdziłem wyjście ASM.)
ern0
4
Testowałem słowo kluczowe register pod QNX 6.5.0, używając następującego kodu:
#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;registerint 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;}
Rejestr poinformuje kompilator, że programista uważa, że zmienna ta zostanie zapisana / odczytana w stopniu wystarczającym do uzasadnienia jej przechowywania w jednym z niewielu rejestrów dostępnych do wykorzystania w zmiennej. Odczyt / zapis z rejestrów jest zwykle szybszy i może wymagać mniejszego zestawu kodów operacyjnych.
W dzisiejszych czasach nie jest to zbyt przydatne, ponieważ większość optymalizatorów kompilatorów jest lepsza od ciebie w określaniu, czy rejestr powinien być używany dla tej zmiennej i na jak długo.
W latach siedemdziesiątych, na samym początku języka C, wprowadzono słowo kluczowe register, aby programista mógł udzielać wskazówek kompilatorowi, informując go, że zmienna będzie używana bardzo często i że należy mądrze zachowaj jego wartość w jednym z wewnętrznych rejestrów procesora.
W dzisiejszych czasach optymalizatory są znacznie bardziej wydajne niż programiści w zakresie określania zmiennych, które prawdopodobnie będą przechowywane w rejestrach, a optymalizator nie zawsze bierze pod uwagę wskazówkę programisty.
Tak wiele osób niesłusznie nie zaleca używania słowa kluczowego register.
Zobaczmy dlaczego!
Słowo kluczowe rejestru ma powiązany efekt uboczny: nie można odwoływać się (uzyskać adres) zmiennej typu rejestru.
Ludzie odradzający innym nieużywanie rejestrów błędnie traktują to jako dodatkowy argument.
Jednak sam fakt, że nie można pobrać adresu zmiennej rejestru, pozwala kompilatorowi (i jego optymalizatorowi) wiedzieć, że wartości tej zmiennej nie można modyfikować pośrednio za pomocą wskaźnika.
Gdy w pewnym punkcie strumienia instrukcji zmienna rejestru ma przypisaną wartość w rejestrze procesora, a rejestr nie był używany, ponieważ do uzyskania wartości innej zmiennej kompilator wie, że nie musi ponownie ładować wartość zmiennej w tym rejestrze. Pozwala to uniknąć kosztownego, bezużytecznego dostępu do pamięci.
Wykonaj własne testy, a uzyskasz znaczną poprawę wydajności w najbardziej wewnętrznych pętlach.
Kompilator Visual C ++ firmy Microsoft ignoruje registersłowo kluczowe, gdy włączona jest optymalizacja globalnej alokacji rejestru (flaga kompilatora / Oe).
Słowo kluczowe register mówi kompilatorowi, aby zapisał określoną zmienną w rejestrach procesora, aby była szybko dostępna. Z punktu widzenia programisty słowo kluczowe register jest używane do zmiennych, które są często używane w programie, dzięki czemu kompilator może przyspieszyć kod. Chociaż zależy od kompilatora, czy zachować zmienną w rejestrach procesora czy w pamięci głównej.
Rejestr wskazuje kompilatorowi na optymalizację tego kodu poprzez zapisanie tej konkretnej zmiennej w rejestrach, a następnie w pamięci. jest to żądanie do kompilatora, kompilator może, ale nie musi, rozważyć to żądanie. Możesz skorzystać z tej funkcji, gdy bardzo często uzyskuje się dostęp do niektórych zmiennych. Na przykład: Pętla.
Jeszcze jedną rzeczą jest to, że jeśli zadeklarujesz zmienną jako rejestr, nie możesz uzyskać jej adresu, ponieważ nie jest ona przechowywana w pamięci. otrzymuje swój przydział w rejestrze procesora.
#include<stdio.h>int main(void){registerint i =3;
i++;
printf("%d", i);return0;}
.LC0:.string "%d"
main:
push rbp
mov rbp, rsp
push rbx
sub rsp,8
mov ebx,3
add ebx,1
mov esi, ebx
mov edi, OFFSET FLAT:.LC0
mov eax,0
call printf
add rsp,8
pop rbx
pop rbp
ret
Wymusza ebxto użycie w obliczeniach, co oznacza, że należy go wepchnąć na stos i przywrócić na końcu funkcji, ponieważ jest on zapisany przez callee. registerprodukuje więcej wierszy kodu i 1 zapis pamięci i 1 odczyt pamięci (choć realistycznie można by to zoptymalizować do 0 R / Ws, gdyby obliczenia zostały wykonane esi, co dzieje się przy użyciu C ++ const register). Nieużywanie registerpowoduje 2 zapisy i 1 odczyt (chociaż przekierowanie z pamięci do załadowania nastąpi podczas odczytu). Wynika to z faktu, że wartość musi być obecna i aktualizowana bezpośrednio na stosie, aby można było odczytać poprawną wartość według adresu (wskaźnika). registernie ma tego wymogu i nie można go wskazać. consti registersą w zasadzie przeciwieństwem volatilei używaniemvolatilezastąpi optymalizacje const w zakresie plików i bloków oraz registeroptymalizacje w zakresie bloków. const registeri registerwygeneruje identyczne wyniki, ponieważ const nic nie robi na C w zakresie bloków, więc registerzastosowanie mają tylko optymalizacje.
Podczas clang registerjest ignorowany, ale constnadal występują optymalizacje.
register
zmiennej.Odpowiedzi:
Jest to wskazówka dla kompilatora, że zmienna będzie intensywnie używana i zaleca się, aby w miarę możliwości przechowywać ją w rejestrze procesora.
Większość współczesnych kompilatorów robi to automatycznie i lepiej je wybiera niż my ludzie.
źródło
register
pobieraniu adresu zmiennej; jest to jedyny obowiązkowy efektregister
słowa kluczowego. Nawet to wystarcza, aby poprawić optymalizacje, ponieważ powiedzenie, że zmienną można modyfikować tylko w ramach tej funkcji, staje się banalne.Dziwi mnie, że nikt nie wspomniał, że nie można wziąć adresu zmiennej rejestru, nawet jeśli kompilator zdecyduje się zachować zmienną w pamięci, a nie w rejestrze.
Więc za pomocą
register
nic nie wygrywasz (zresztą kompilator sam decyduje, gdzie umieścić zmienną) i traci&
operatora - nie ma powodu, aby go używać.źródło
register
jest to przydatne, nawet jeśli kompilator nie umieści go w rejestrze.const
jest również bezużyteczna, ponieważ nic ci nie wygrywa, tracisz jedynie możliwość zmiany zmiennej.register
można użyć, aby upewnić się, że nikt nie przyjmie adresu zmiennej w przyszłości bez zastanowienia. Nigdy nie miałem jednak powodu, aby z tego korzystaćregister
.Mówi kompilatorowi, aby spróbował użyć rejestru procesora zamiast pamięci RAM do przechowywania zmiennej. Rejestry znajdują się w procesorze i dostęp do nich jest znacznie szybszy niż w przypadku pamięci RAM. Jest to jednak tylko sugestia dla kompilatora i może nie zostać zrealizowana.
źródło
Wiem, że to pytanie dotyczy C, ale to samo pytanie dla C ++ zostało zamknięte jako dokładna kopia tego pytania. Ta odpowiedź może zatem nie dotyczyć C.
Najnowszy projekt standardu C ++ 11, N3485 , mówi o tym w 7.1.1 / 3:
W C ++ (ale nie w C) standard nie stwierdza, że nie można pobrać adresu deklarowanej zmiennej
register
; jednak ponieważ zmienna przechowywana w rejestrze procesora przez cały okres jej istnienia nie ma powiązanej z nią lokalizacji w pamięci, próba przejęcia jego adresu byłaby nieprawidłowa, a kompilator zignorujeregister
słowo kluczowe, aby umożliwić przyjęcie adresu.źródło
Nie było to istotne przez co najmniej 15 lat, ponieważ optymalizatorzy podejmują lepsze decyzje w tej sprawie niż możesz. Nawet gdy było to istotne, miało to o wiele większy sens w architekturze procesora z dużą liczbą rejestrów, takich jak SPARC lub M68000, niż w przypadku Intel z niewielką liczbą rejestrów, z których większość jest zarezerwowana przez kompilator na własne potrzeby.
źródło
W rzeczywistości rejestr informuje kompilator, że zmienna nie ma aliasu z niczym innym w programie (nawet char).
To może być wykorzystywane przez współczesne kompilatory w różnych sytuacjach i może pomóc kompilatorowi w złożonym kodzie - w prostym kodzie kompilatory mogą to rozwiązać samodzielnie.
W przeciwnym razie nie służy celowi i nie jest wykorzystywany do przydzielania rejestrów. Zazwyczaj nie powoduje to obniżenia wydajności, o ile kompilator jest wystarczająco nowoczesny.
źródło
register
zabrania w ogóle przyjmowania adresu, ponieważ w przeciwnym razie przydatne może być poinformowanie kompilatorów o przypadkach, w których kompilator byłby w stanie zastosować optymalizację rejestru pomimo przekazania adresu zmiennej do funkcji zewnętrznej (zmienna musiałaby zostać opróżnione do pamięci dla tego konkretnego wywołania , ale gdy funkcja wróci, kompilator może ponownie potraktować ją jako zmienną, której adresu nigdy nie podano.bar
jest toregister
zmienna, kompilator może w swoim wolnym zastąpićfoo(&bar);
zint temp=bar; foo(&temp); bar=temp;
, ale biorąc pod adresbar
byłyby zakazane w większości innych kontekstach nie wydawać się zbyt skomplikowane reguły. Jeśli zmienna mogłaby być przechowywana w rejestrze, podstawienie zmniejszyłoby kod. Jeśli zmienna i tak musiałaby być przechowywana w pamięci RAM, podstawienie zwiększyłoby kod. Pozostawienie pytania, czy zastąpić kompilator, doprowadziłoby do lepszego kodu w obu przypadkach.register
kwalifikację zmiennych globalnych, niezależnie od tego, czy kompilator zezwala na pobranie adresu, pozwoliłoby na pewne dobre optymalizacje w przypadkach, gdy funkcja wbudowana, która używa zmiennej globalnej, jest wywoływana wielokrotnie w pętli. Nie mogę wymyślić żadnego innego sposobu, aby ta zmienna była przechowywana w rejestrze między iteracjami pętli - prawda?W rzeczywistości jest to wyraźnie określone przez standard C. Cytując wersję roboczą N1570 sekcja 6.7.1 akapit 6 (inne wersje mają to samo brzmienie):
Jednoargumentowy
&
operator nie może być stosowany do obiektu zdefiniowanego za pomocąregister
iregister
nie może być stosowany w deklaracji zewnętrznej.Istnieje kilka innych (dość niejasnych) reguł, które są specyficzne dla
register
obiektów zakwalifikowanych:Definiowanie obiektu tablicowego za pomocąregister
niezdefiniowanego zachowania.Korekta: Definiowanie obiektu tablicy jest legalne
register
, ale nie można zrobić nic użytecznego z takim obiektem (indeksowanie do tablicy wymaga przyjęcia adresu jego początkowego elementu)._Alignas
(nowy w C11) nie może być zastosowany do takiego obiektu.va_start
makra jestregister
-kwalifikowana, zachowanie jest niezdefiniowane.Może być kilka innych; pobierz projekt standardu i wyszukaj „zarejestruj się”, jeśli jesteś zainteresowany.
Jak sama nazwa wskazuje, pierwotnym znaczeniem
register
było wymaganie, aby obiekt był przechowywany w rejestrze procesora. Jednak dzięki ulepszeniom w optymalizacji kompilatorów stało się to mniej przydatne. Nowoczesne wersje standardu C nie odnoszą się do rejestrów procesora, ponieważ nie muszą już (trzeba) zakładać, że istnieje coś takiego (istnieją architektury, które nie używają rejestrów). Powszechnie wiadomo, że zastosowanieregister
do deklaracji obiektowej z większym prawdopodobieństwem pogorszy generowany kod, ponieważ zakłóca to alokację rejestru przez kompilator. Może być jeszcze kilka przypadków, w których jest to przydatne (powiedzmy, jeśli naprawdę wiesz, jak często zmienna będzie dostępna, a twoja wiedza jest lepsza niż to, co może zrozumieć nowoczesny kompilator optymalizujący).Głównym namacalnym efektem
register
jest to, że uniemożliwia jakąkolwiek próbę przejęcia adresu obiektu. Nie jest to szczególnie przydatne jako wskazówka optymalizacyjna, ponieważ można ją zastosować tylko do zmiennych lokalnych, a kompilator optymalizujący może przekonać się, że adres takiego obiektu nie jest brany.źródło
register
niewykwalifikowanego obiektu tablicowego, jeśli tak myślisz.register
obiekty tablicowe; zobacz zaktualizowany pierwszy punkt w mojej odpowiedzi. Zdefiniowanie takiego obiektu jest legalne, ale nie można z nim nic zrobić. Jeśli dodaszregister
do definicjis
w swoim przykładzie , program jest nielegalny (naruszenie ograniczenia) w C. C ++ nie nakłada takich samych ograniczeńregister
, więc program będzie poprawny w C ++ (ale używanieregister
byłoby bezcelowe).register
kluczowe mogłoby służyć pożytecznemu celowi, gdyby przyjęcie adresu takiej zmiennej było zgodne z prawem, ale tylko w przypadkach, w których semantyka nie miałaby wpływu na kopiowanie zmiennej do tymczasowej, gdy jej adres jest pobierany, i ponowne ładowanie jej z tymczasowej w następnym punkcie sekwencji. Pozwoliłoby to kompilatorom założyć, że zmienna może być bezpiecznie przechowywana w rejestrze we wszystkich dostępach do wskaźnika, pod warunkiem że zostanie opróżniona w dowolnym miejscu, w którym zostanie pobrany adres.Opowiadanie!
C, jako język, jest abstrakcją komputera. Umożliwia robienie rzeczy w zakresie tego, co robi komputer, tj. Manipulowanie pamięcią, matematyka, drukowanie itp.
Ale C to tylko abstrakcja. I w końcu wyciąga z ciebie język asemblera. Asembler to język, który czyta procesor, a jeśli go używasz, robisz różne rzeczy pod względem procesora. Co robi procesor? Zasadniczo czyta z pamięci, robi matematykę i zapisuje w pamięci. Procesor nie tylko wykonuje matematykę na liczbach w pamięci. Najpierw musisz przenieść liczbę z pamięci do pamięci wewnątrz procesora o nazwie a rejestrem. Gdy skończysz robić wszystko, co musisz zrobić z tym numerem, możesz przenieść go z powrotem do normalnej pamięci systemowej. Po co w ogóle korzystać z pamięci systemowej? Liczba rejestrów jest ograniczona. W nowoczesnych procesorach dostajesz tylko około sto bajtów, a starsze popularne procesory były jeszcze bardziej fantastycznie ograniczone (6502 miał 3 8-bitowe rejestry do bezpłatnego użytku). Twoja średnia operacja matematyczna wygląda następująco:
Wiele z nich to ... nie matematyka. Te operacje ładowania i przechowywania mogą zająć nawet połowę czasu przetwarzania. C, będący abstrakcją komputerów, uwolnił programistę od zmartwień związanych z używaniem i żonglowaniem rejestrami, a ponieważ liczba i typ różnią się między komputerami, C nakłada odpowiedzialność za przydzielanie rejestrów wyłącznie na kompilator. Z jednym wyjątkiem.
Kiedy deklarujesz zmienną
register
, mówisz kompilatorowi: „Tak, zamierzam, aby ta zmienna była często używana i / lub krótkotrwała. Gdybym był tobą, spróbowałbym zachować ją w rejestrze”. Kiedy standard C mówi, że kompilatory nie muszą właściwie nic robić, to dlatego, że standard C nie wie, dla jakiego komputera kompilujesz, i może być tak jak w 6502 powyżej, gdzie wszystkie 3 rejestry są potrzebne tylko do działania , i nie ma rezerwowego rejestru, aby zachować Twój numer. Jednak gdy mówi, że nie możesz wziąć adresu, to dlatego, że rejestry nie mają adresów. To ręce procesora. Ponieważ kompilator nie musi podawać adresu, a ponieważ nigdy nie może mieć adresu, dla niego dostępnych jest kilka optymalizacji. Może, powiedzmy, zawsze przechowywać numer w rejestrze. Nie robi muszę się martwić, gdzie jest przechowywany w pamięci komputera (poza koniecznością odzyskania go ponownie). Może nawet przebić ją do innej zmiennej, przekazać innemu procesorowi, zmienić lokalizację itp.tl; dr: Zmienne krótkotrwałe, które wykonują wiele obliczeń matematycznych. Nie deklaruj zbyt wielu naraz.
źródło
Masz problem z wyrafinowanym algorytmem kolorowania grafik kompilatora. Służy do przydzielania rejestrów. Cóż, głównie. Działa jako wskazówka dla kompilatora - to prawda. Ale nie ignorowane w całości, ponieważ nie wolno ci przyjmować adresu zmiennej rejestru (pamiętaj, że kompilator, teraz na twoją łaskę, spróbuje działać inaczej). Co w pewien sposób mówi ci, żebyś go nie używał.
Słowo kluczowe zostało użyte dawno, dawno temu. Gdy rejestrów było tak mało, że można je było policzyć za pomocą palca wskazującego.
Ale, jak powiedziałem, przestarzałe nie oznacza, że nie możesz go użyć.
źródło
Tylko mała wersja demonstracyjna (bez żadnego rzeczywistego celu) do porównania: podczas usuwania
register
słów kluczowych przed każdą zmienną ten fragment kodu zajmuje 3,41 sekundy na moim i7 (GCC), aregister
ten sam kod kończy się w 0,7 sekundy.źródło
Testowałem słowo kluczowe register pod QNX 6.5.0, używając następującego kodu:
Mam następujące wyniki:
-> upłynęło 807679611 cykli
-> Ten system ma 3300830000 cykli na sekundę
-> Cykle w sekundach wynoszą ~ 0,244600
A teraz bez rejestracji int:
Mam:
-> 1421694077 cykli, które upłynęły
-> Ten system ma 3300830000 cykli na sekundę
-> Cykle w sekundach wynoszą ~ 0,430700
źródło
Rejestr poinformuje kompilator, że programista uważa, że zmienna ta zostanie zapisana / odczytana w stopniu wystarczającym do uzasadnienia jej przechowywania w jednym z niewielu rejestrów dostępnych do wykorzystania w zmiennej. Odczyt / zapis z rejestrów jest zwykle szybszy i może wymagać mniejszego zestawu kodów operacyjnych.
W dzisiejszych czasach nie jest to zbyt przydatne, ponieważ większość optymalizatorów kompilatorów jest lepsza od ciebie w określaniu, czy rejestr powinien być używany dla tej zmiennej i na jak długo.
źródło
W latach siedemdziesiątych, na samym początku języka C, wprowadzono słowo kluczowe register, aby programista mógł udzielać wskazówek kompilatorowi, informując go, że zmienna będzie używana bardzo często i że należy mądrze zachowaj jego wartość w jednym z wewnętrznych rejestrów procesora.
W dzisiejszych czasach optymalizatory są znacznie bardziej wydajne niż programiści w zakresie określania zmiennych, które prawdopodobnie będą przechowywane w rejestrach, a optymalizator nie zawsze bierze pod uwagę wskazówkę programisty.
Tak wiele osób niesłusznie nie zaleca używania słowa kluczowego register.
Zobaczmy dlaczego!
Słowo kluczowe rejestru ma powiązany efekt uboczny: nie można odwoływać się (uzyskać adres) zmiennej typu rejestru.
Ludzie odradzający innym nieużywanie rejestrów błędnie traktują to jako dodatkowy argument.
Jednak sam fakt, że nie można pobrać adresu zmiennej rejestru, pozwala kompilatorowi (i jego optymalizatorowi) wiedzieć, że wartości tej zmiennej nie można modyfikować pośrednio za pomocą wskaźnika.
Gdy w pewnym punkcie strumienia instrukcji zmienna rejestru ma przypisaną wartość w rejestrze procesora, a rejestr nie był używany, ponieważ do uzyskania wartości innej zmiennej kompilator wie, że nie musi ponownie ładować wartość zmiennej w tym rejestrze. Pozwala to uniknąć kosztownego, bezużytecznego dostępu do pamięci.
Wykonaj własne testy, a uzyskasz znaczną poprawę wydajności w najbardziej wewnętrznych pętlach.
c_register_side_effect_performance_boost
źródło
W obsługiwanych kompilatorach C próbuje zoptymalizować kod, aby wartość zmiennej była przechowywana w rzeczywistym rejestrze procesora.
źródło
Kompilator Visual C ++ firmy Microsoft ignoruje
register
słowo kluczowe, gdy włączona jest optymalizacja globalnej alokacji rejestru (flaga kompilatora / Oe).Zobacz rejestracja słowa kluczowego w MSDN.
źródło
Słowo kluczowe register mówi kompilatorowi, aby zapisał określoną zmienną w rejestrach procesora, aby była szybko dostępna. Z punktu widzenia programisty słowo kluczowe register jest używane do zmiennych, które są często używane w programie, dzięki czemu kompilator może przyspieszyć kod. Chociaż zależy od kompilatora, czy zachować zmienną w rejestrach procesora czy w pamięci głównej.
źródło
Rejestr wskazuje kompilatorowi na optymalizację tego kodu poprzez zapisanie tej konkretnej zmiennej w rejestrach, a następnie w pamięci. jest to żądanie do kompilatora, kompilator może, ale nie musi, rozważyć to żądanie. Możesz skorzystać z tej funkcji, gdy bardzo często uzyskuje się dostęp do niektórych zmiennych. Na przykład: Pętla.
Jeszcze jedną rzeczą jest to, że jeśli zadeklarujesz zmienną jako rejestr, nie możesz uzyskać jej adresu, ponieważ nie jest ona przechowywana w pamięci. otrzymuje swój przydział w rejestrze procesora.
źródło
Dane wyjściowe asm gcc 9.3, bez użycia flag optymalizacji (wszystko w tej odpowiedzi dotyczy standardowej kompilacji bez flag optymalizacji):
Wymusza
ebx
to użycie w obliczeniach, co oznacza, że należy go wepchnąć na stos i przywrócić na końcu funkcji, ponieważ jest on zapisany przez callee.register
produkuje więcej wierszy kodu i 1 zapis pamięci i 1 odczyt pamięci (choć realistycznie można by to zoptymalizować do 0 R / Ws, gdyby obliczenia zostały wykonaneesi
, co dzieje się przy użyciu C ++const register
). Nieużywanieregister
powoduje 2 zapisy i 1 odczyt (chociaż przekierowanie z pamięci do załadowania nastąpi podczas odczytu). Wynika to z faktu, że wartość musi być obecna i aktualizowana bezpośrednio na stosie, aby można było odczytać poprawną wartość według adresu (wskaźnika).register
nie ma tego wymogu i nie można go wskazać.const
iregister
są w zasadzie przeciwieństwemvolatile
i używaniemvolatile
zastąpi optymalizacje const w zakresie plików i bloków orazregister
optymalizacje w zakresie bloków.const register
iregister
wygeneruje identyczne wyniki, ponieważ const nic nie robi na C w zakresie bloków, więcregister
zastosowanie mają tylko optymalizacje.Podczas clang
register
jest ignorowany, aleconst
nadal występują optymalizacje.źródło