Ile użycie makr „prawdopodobnych” i „mało prawdopodobnych” jest zbyt duże?

12

Często znane jako likelyi unlikelymakra pomagają kompilatorowi wiedzieć, czy ifzwykle będzie wprowadzane czy pomijane. Używanie go powoduje pewne (raczej niewielkie) ulepszenia wydajności.

Zacząłem ich używać niedawno i nie jestem pewien, jak często należy korzystać z takich wskazówek. Obecnie używam go do sprawdzania błędów if, które zwykle są oznaczone jako unlikely. Na przykład:

mem = malloc(size);
if (unlikely(mem == NULL))
  goto exit_no_mem;

Wydaje się to w porządku, ale sprawdzanie błędów ifzdarza się dość często iw konsekwencji użycie wspomnianych makr.

Moje pytanie brzmi, czy to zbyt wiele, aby mieć likelyi unlikelymakra na każdym Sprawdzanie błędów if?

Skoro już tu jesteśmy, jakie inne miejsca są często używane?


W moim obecnym użyciu znajduje się w bibliotece, która dokonuje abstrakcji z podsystemu czasu rzeczywistego, więc programy stałyby się przenośne między RTAI, QNX i innymi. To powiedziawszy, większość funkcji jest raczej niewielka i bezpośrednio wywołuje jedną lub dwie inne funkcje. Wiele z nich to nawet static inlinefunkcje.

Przede wszystkim nie jest to aplikacja, którą mógłbym profilować. Identyfikacja „wąskich gardeł” nie ma sensu, ponieważ jest to biblioteka, a nie samodzielna aplikacja.

Po drugie, jest to coś w rodzaju „wiem, że to mało prawdopodobne, równie dobrze mogę to powiedzieć kompilatorowi”. Nie próbuję aktywnie optymalizować if.

Shahbaz
źródło
7
cuchnie mikro optymalizacji dla mnie ...
maniak zapadkowy
2
W przypadku kodu aplikacji dodam je tylko wtedy, gdy profilowanie wykaże, że ten kod jest używany w gorącej ścieżce.
CodesInChaos
@James, który po prostu mówi likelyi unlikelyistnieje i co robią. Nie znalazłem niczego, co faktycznie sugerowałoby, kiedy i gdzie najlepiej ich używać.
Shahbaz
@Shahbaz "Jeśli warunek jest często fałszywy, wykonanie nie jest liniowe. W środku znajduje się duża część nieużywanego kodu, który nie tylko zanieczyszcza L1i z powodu pobierania wstępnego, ale może również powodować problemy z przewidywaniem gałęzi. Jeśli przewidywanie gałęzi jest nieprawidłowe, wyrażenie warunkowe może być bardzo nieefektywne. ” Więc ciasne pętle, w których chcesz się upewnić, że potrzebne instrukcje znajdują się w pamięci podręcznej L1i
James

Odpowiedzi:

12

Czy potrzebujesz wydajności tak bardzo, że zechcesz w ten sposób skazić kod? To niewielka optymalizacja.

  • Czy kod działa w ciasnej pętli?
  • Czy Twoja aplikacja ma problemy z wydajnością?
  • Czy profilowałeś swoją aplikację i ustaliłeś, że ta konkretna pętla kosztuje dużo czasu procesora?

Jeśli nie potrafisz odpowiedzieć yesna wszystkie powyższe pytania , nie przejmuj się takimi rzeczami.

Edycja: w odpowiedzi na edycję. Nawet jeśli nie możesz profilować, zwykle możesz oszacować punkty aktywne. Funkcja alokacji pamięci, która jest wywoływana przez wszystkich, jest dobrym kandydatem, zwłaszcza że wymaga tylko jednego użycia makra do pracy dla całej biblioteki.

Facet z Java
źródło
1
Żeby było jasne, nie głosowałem za tobą. Jednak twoja odpowiedź tak naprawdę nie odpowiada na moje pytanie. Czy próbujesz powiedzieć, że (un)likelymakro jest rzadko używane i tylko w kodzie krytycznym pod względem wydajności? Czy używanie go często jest „złą praktyką”, czy po prostu „niepotrzebne”?
Shahbaz
@Shahbaz To sprawia, że ​​kod jest mniej czytelny, a optymalizacja wydajności może wahać się od trywialnego wzmocnienia do trywialnej utraty. To drugie, gdy założenie o prawdopodobieństwie było albo niepoprawne, albo zmieniło się na niepoprawne z powodu późniejszej zmiany innych części kodu. Jeśli nigdy nie należy go używać, chyba że jest to konieczne.
Peter
3
@Peter: Chociaż szkoda, że ​​składnia nie jest ładniejsza, notacje o tym, co jest prawdopodobne, a które mogą być mało prawdopodobne, mogą dostarczyć użytecznych informacji ludziom czytającym kod. Na przykład ktoś, kto zobaczył if (likely(x==2 || x==3)) doOneThing(); else switch(x) { ... }, mógłby ocenić, że użycie ifprzez programistę wartości 2 i 3 nie było jedynie konsekwencją tego, że programista nie wiedział, że C może skojarzyć dwie caseetykiety z jednym programem obsługi.
supercat
Nikt nie wspomniał o tym, co uważam za punkt krytyczny. Nie chodzi tylko o to, że „nieprawdopodobna” ścieżka zdarza się rzadziej, może być tak, że uwarunkowana tą ścieżką , nie martwisz się w ogóle szybkością. Na przykład urządzenie peryferyjne przestaje odpowiadać, więc musisz zresetować go i i tak spać.
Benjamin Lindqvist,
2

Jeśli piszesz dla x86 / x64 (i nie używasz 20-letnich procesorów), wzrost wydajności przy użyciu __builtin_expect () będzie nieznaczny, jeśli w ogóle. Powodem tego jest fakt, że współczesne procesory x86 / x64 (choć nie w 100% pewna co do Atomu) mają dynamiczne przewidywanie gałęzi, więc w zasadzie procesor „uczy się” o gałęzi, która jest pobierana częściej. Oczywiście te informacje można przechowywać tylko dla ograniczonej liczby oddziałów, jednak możliwe są tylko dwa przypadki. Jeśli (a) jest to gałąź „często używana”, to twój program skorzysta z tej dynamicznej prognozy gałęzi, a jeśli (b) jest to gałąź „rzadka”, naprawdę nie zobaczysz żadnego realistycznego spadku wydajności z powodu nieprzewidzianych takie rzadkie gałęzie (20 cykli procesora błędnego przewidywania gałęzi nie jest ZBYT złe, jeśli zdarzy się to raz na niebieskim księżycu).

Uwaga: NIE oznacza to, że na współczesnym x86 / x64 znaczenie nieprzewidywalności gałęzi spadło: każda gałąź z 50-50 szansą na skok-nojump nadal będzie podlegać karze (IIRC 10-20 cykli CPU), więc w wewnętrznych pętlach gałęzie mogą wciąż trzeba unikać. Jest to tylko ważność __builtin_expect () na x86 / x64, która się zmniejszyła (IIRC, około 10-15 lat temu) - głównie z powodu dynamicznego przewidywania gałęzi.

NB2: dla innych platform innych niż x86 / x64, YMMV.

Zając bez błędów
źródło
Kompilator wie, w której gałęzi procesor będzie się spodziewał. I może to być tak naprawdę mało prawdopodobne. Ale kompilator prawdopodobnie już zna ten wzorzec bez unlikelyadnotacji.
Deduplicator,
@Deduplicator: przy dynamicznym przewidywaniu gałęzi kompilator nie wie, która gałąź jest bardziej prawdopodobna, ponieważ CPU oblicza ją w czasie wykonywania na podstawie poprzednich przebiegów przez ten punkt kodu.
No-Bugs Hare