Słowo kluczowe restrict to specyfikator dla wskaźników, wprowadzony w standardzie C99. Informuje kompilator, że wskaźnik jest jedynym sposobem dostępu do obiektu pamięci w obrębie zasięgu wskaźnika. Znacząco pomaga to optymalizatorowi w generowaniu bardziej wydajnego kodu maszynowego, zwłaszcza przy pracy z dużymi buforami.
Na przykład:
void vector_add(int * restrict a, int * restrict b, int * restrict c, size_t n) { for (size_t i = 0; i < n; ++i) c[i] = a[i] + b[i]; }
Tutaj zakłada się, że tablice a, b i c nie nakładają się na siebie. Naruszenie tego wymogu prowadzi do nieokreślonego zachowania i trudnych do wykrycia błędów.
Użycie restrict zaleca się tylko wtedy, gdy masz pewność, że żadne inne wskaźniki ani ścieżki poboczne nie wskazują na tę samą pamięć.
Czy ta sama wartość pamięci może być jednocześnie widoczna przez dwa wskaźniki restrict?
Odpowiedź:
Nie, to prowadzi do undefined behavior. Nie ma gwarancji, że kompilator uwzględni zmiany wprowadzone przez drugi wskaźnik. Przykład — krytycznie błędny kod:
void f(int * restrict x, int * restrict y) { x[0] = 1; y[0] = 2; } int main() { int v; f(&v, &v); // Naruszenie warunku restrict }
Historia
W finansowym rdzeniu obliczeniowym funkcje optymalizowały tablice z dodaniem restrict, ale nie uwzględniły, że tablice mogą się nakładać zgodnie z wymaganiami części logiki biznesowej. Doprowadziło to do błędnego obliczenia salda przy niewłaściwym użyciu.
Historia
Metoda mnożenia macierzy w partiach przyspieszyła po zastosowaniu restrict, ale w jednej z iteracji tablica wyników nakładała się na jedną z tablic wejściowych — wynik stał się nieprzewidywalny, a błąd wychwytywano tylko podczas testów obciążeniowych.
Historia
W jednej z funkcji przetwarzania obrazu dwa wskaźniki na fragmenty tego samego bufora zostały przypadkowo zadeklarowane z restrict. Po aktualizacji kompilatora i jego optymalizatora wynik przetwarzania obrazów nagle stał się zniekształcony — powód: kompilator zaczął aktywnie ponownie używać pamięci podręcznej i ignorował modyfikacje.