Dlaczego szybkie typy liczb całkowitych są szybsze niż inne typy liczb całkowitych?


107

W ISO / IEC 9899: 2018 (C18) podano w 7.20.1.3:

7.20.1.3 Najszybsze typy całkowite o minimalnej szerokości

1 Każdy z poniższych typów oznacza typ całkowity, który jest zwykle najszybszy 268) do działania z wszystkimi typami liczb całkowitych, które mają co najmniej określoną szerokość.

2 Nazwa typedef int_fastN_t określa najszybszy typ liczby całkowitej o szerokości co najmniej N. Nazwa typedef uint_fastN_toznacza najszybszy typ liczby całkowitej bez znaku o szerokości co najmniej N.

3 Wymagane są następujące typy:

int_fast8_t, int_fast16_t, int_fast32_t, int_fast64_t, uint_fast8_t, uint_fast16_t,uint_fast32_t ,uint_fast64_t

Wszystkie pozostałe typy tego formularza są opcjonalne.


268) Nie można zagwarantować, że wskazany typ będzie najszybszy we wszystkich celach; jeśli implementacja nie ma wyraźnych podstaw do wyboru jednego typu nad drugim, po prostu wybierze typ całkowity spełniający wymagania dotyczące podpisu i szerokości.


Nie wiadomo jednak, dlaczego te „szybkie” typy całkowite są szybsze.

  • Dlaczego te szybkie typy liczb całkowitych są szybsze niż inne typy liczb całkowitych?

Oznacziłem pytanie C ++, ponieważ typy szybkich liczb całkowitych są również dostępne w C ++ 17 w pliku nagłówkowym cstdint. Niestety w ISO / IEC 14882: 2017 (C ++ 17) nie ma takiej sekcji na temat ich wyjaśnienia; W innym przypadku zastosowałem tę sekcję w treści pytania.


Informacja: W C są zadeklarowane w pliku nagłówkowym stdint.h.


24
Kluczową kwestią jest to, że te typy liczb całkowitych nie są osobnymi, magicznie szybszymi typami. Są tylko aliasami do tego, który normalny istniejący typ jest najszybszy na tym komputerze dla tej operacji.
mtraceur

3
Kompilator emituje kody operacji procesora w celu ładowania, przechowywania, maskowania i modyfikowania lokalizacji pamięci i rejestrów o określonych rozmiarach; to wszystko, co widzi procesor. System operacyjny nie ma z tym nic wspólnego. To wszystko robi kompilator, dokładnie tak, jakbyś sam określił typedef. ( Podejrzewam , że kompilator może wewnętrznie przetwarzać go inaczej - być może bardziej wydajnie, jeśli to możliwe - niż typedef, pod warunkiem, że nie ma widocznej różnicy w zachowaniu.)
Peter - Przywróć Monikę

1
@ RobertS-ReinstateMonica Mówiąc ściślej, te „aliasy” to tylko typedefstwierdzenia. Tak zazwyczaj , gdy jest dokonywane na poziomie podstawowym biblioteki. Oczywiście, C Standard stawia nie realne ograniczenie co typedefdo - tak na przykład typowa realizacja jest, aby od na systemie 32-bitowym, ale hipotetyczny kompilator mógł na przykład wdrożyć wewnętrzną typ i obiecuję zrobić wymyślnej optymalizacje w celu wybrania najszybszego typu komputera dla poszczególnych przypadków dla zmiennych tego typu, a następnie biblioteka może to zrobić. int_fast32_ttypedefint__int_fasttypedef
mtraceur

1
@ RobertS-ReinstateMonica Tak, prawda. Otrzymujesz programy o maksymalnej wydajności z flagami kompilacji specyficznymi dla architektury, co sprawia, że ​​plik binarny jest mniej przenośny.
Peter - przywróć Monikę

1
@ Roberts-ReinstateMonica To będzie najbardziej wydajny na platformie został skompilowany dla , niekoniecznie na .
HABO

Odpowiedzi:


152

Wyobraź sobie procesor, który wykonuje tylko 64-bitowe operacje arytmetyczne. Teraz wyobraź sobie, jak zaimplementowałbyś 8-bitowy dodatek bez znaku na takim procesorze. Aby uzyskać właściwy wynik, koniecznie musi być więcej niż jedna operacja. Na takim procesorze operacje 64-bitowe są szybsze niż operacje na innych szerokościach całkowitych. W tej sytuacji wszystkoXint_fastY_t może być aliasem typu 64-bitowego.

Jeśli procesor obsługuje szybkie operacje dla wąskich typów całkowitych, a zatem szerszy typ nie jest szybszy niż węższy Xint_fastY_t nie będzie aliasem szerszego typu, niż jest to konieczne do przedstawienia wszystkich bitów Y.

Z ciekawości sprawdziłem rozmiary poszczególnych implementacji (GNU, Linux) na niektórych architekturach. Nie są one takie same we wszystkich implementacjach tej samej architektury:

┌────╥───────────────────────────────────────────────────────────┐
 Y     sizeof(Xint_fastY_t) * CHAR_BIT                         
    ╟────────┬─────┬───────┬─────┬────────┬──────┬────────┬─────┤
     x86-64  x86  ARM64  ARM  MIPS64  MIPS  MSP430  AVR 
╞════╬════════╪═════╪═══════╪═════╪════════╪══════╪════════╪═════╡
 8   8       8    8      32   8       8     16      8   
 16  64      32   64     32   64      32    16      16  
 32  64      32   64     32   64      32    32      32  
 64  64      64   64     64   64      64    64      64  
└────╨────────┴─────┴───────┴─────┴────────┴──────┴────────┴─────┘

Pamiętaj, że chociaż operacje na większych typach mogą być szybsze, takie typy zajmują również więcej miejsca w pamięci podręcznej, a zatem ich użycie niekoniecznie zapewnia lepszą wydajność. Co więcej, nie zawsze można ufać, że wdrożenie dokonało właściwego wyboru. Jak zawsze, pomiar jest wymagany dla uzyskania optymalnych wyników.


Zrzut ekranu tabeli dla użytkowników Androida:

Zrzut ekranu powyższej tabeli

(Android nie ma znaków rysunkowych w czcionce mono - ref )


Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Samuel Liew

@RobertSsupportsMonicaCellio Nie. „Nie to samo we wszystkich architekturach” jest również prawdziwe, ale jest natychmiast widoczne na podstawie pokazanych danych, więc nie uważałbym za konieczne stwierdzenie oczywistości. Pokazałem tylko wartości z jednej implementacji i rzeczywiście inne będą miały różne możliwości. Sprawdź na przykład x86-64 w systemie Windows. Znajdziesz różne rozmiary w porównaniu do tego, co pokazano tutaj.
eerorika

@RobertSsupportsMonicaCellio Moim zdaniem te komentarze odnoszą się do odpowiedzi i są odpowiednie tutaj. Pozwoliłbym moderatorowi je przenieść, jeśli czują taką potrzebę.
eerorika

11

Nie są, przynajmniej nie niezawodnie.

Szybkie typy to po prostu typy typef dla typowych typów, jednak ich implementacja zależy od implementacji. Muszą mieć przynajmniej żądany rozmiar, ale mogą być większe.

Prawdą jest, że na niektórych architekturach niektóre typy liczb całkowitych mają lepszą wydajność niż inne. Na przykład wczesne ARM implementacje miały instrukcje dostępu do pamięci dla słów 32-bitowych i bajtów niepodpisanych, ale nie miały instrukcji dla pół słów lub bajtów podpisanych. Instrukcje składające się z pół słowa i podpisanego bajtu zostały dodane później, ale nadal mają mniej elastyczne opcje adresowania, ponieważ trzeba było je przywrócić do wolnej przestrzeni kodowania. Ponadto wszystkie rzeczywiste instrukcje przetwarzania danych ARM działają na słowach, więc w niektórych przypadkach może być konieczne maskowanie mniejszych wartości po obliczeniach, aby uzyskać prawidłowe wyniki.

Jednak istnieje również konkurencyjny problem związany z ciśnieniem pamięci podręcznej, nawet jeśli potrzeba więcej instrukcji, aby załadować / zapisać / przetworzyć mniejszą wartość. Mniejsza wartość może nadal działać lepiej, jeśli zmniejsza liczbę braków pamięci podręcznej.

Wydaje się, że definicje typów na wielu popularnych platformach nie zostały przemyślane. W szczególności współczesne platformy 64-bitowe zazwyczaj dobrze obsługują 32-bitowe liczby całkowite, jednak typy „szybkie” są często niepotrzebnie 64-bitowe na tych platformach.

Ponadto typy w C stają się częścią ABI platformy. Nawet jeśli dostawca platformy odkryje, że dokonał głupich wyborów, trudno później zmienić te głupie wybory.

Zignoruj ​​typy „szybkie”. Jeśli naprawdę martwisz się wydajnością liczb całkowitych, sprawdź kod we wszystkich dostępnych rozmiarach.


7

Typy szybkie nie są szybsze niż wszystkie inne typy liczb całkowitych - w rzeczywistości są identyczne z niektórymi „normalnymi” typami liczb całkowitych (są tylko aliasem dla tego typu) - którykolwiek typ jest najszybszy dla utrzymania wartości przynajmniej tyle bitów.

Zależy tylko od platformy, dla jakiego typu liczb całkowitych każdy szybki typ jest aliasem.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.