2013-01-12 11 views
7

Mam grupę funkcji, które są zadeklarowane jako static i fastcall. Większość z nich wykorzystuje wskaźnik do struktury, która pełni mniej lub bardziej rolę w C++. Niektóre funkcje nie wymagają niczego w strukturze, ale w celu zachowania jednolitości chcę mimo wszystko przekazać im wskaźnik. Czy kompilator zauważy, że argument nie jest używany i nie przypisuje do niego rejestru?Czy kompilator zoptymalizuje nieużywane argumenty funkcji statycznej?

Odpowiedz

8

Napisałem ten program, aby przetestować ten nonsens. Ma w tym funkcję jakiś nonsensowny kod i wywołuje go kilka razy, ponieważ w przeciwnym razie kompilator po prostu wywołał całe wywołanie funkcji, czyniąc test bezużytecznym. (To dziwna mieszanka C i C++, wiem .... głupiego kodu, ale nadal działa w celu wykazania wystawienia)

#include <stdio.h> 
#include <vector> 
#include <algorithm> 

using namespace std; 

struct demo 
{ 
    int a; 
    char ch; 
}; 

static void __fastcall func(struct demo* d) 
{ 
    for(int i = 0; i < 100; i++) { 
    std::vector<int> a; 
    std::sort(begin(a), end(a)); 
    printf("THis is a test"); 
    printf("Hello world\n"); 
    } 
} 

int main() 
{ 
    // void*p = (void*)&func; 
    struct demo d; 
    func(&d); 
    func(&d); 
    func(&d); 
    func(&d); 
    func(&d); 
    func(&d); 
    func(&d); 
    //printf((const char*)p); 
} 

Jak tam napisane wywołania funkcji kompilacji do tego: -

func(&d); 
00F61096 call  func (0F61000h) 
    func(&d); 
00F6109B call  func (0F61000h) 
    func(&d); 
00F610A0 call  func (0F61000h) 
    func(&d); 
00F610A5 call  func (0F61000h) 
    func(&d); 
00F610AA call  func (0F61000h) 
    func(&d); 
00F610AF call  func (0F61000h) 
    func(&d); 
00F610B4 call  func (0F61000h) 

Co pokazuje, że kompilator rzeczywiście pominie parametr, jeśli nie jest używany. Jednak gdybym odkomentowaniu dwie linie tam wziąć adres funkcji następnie rzeczy zmienić, to zamiast generowane w ten sposób: -

00C71099 lea   ecx,[esp] 
00C7109C call  func (0C71000h) 
    func(&d); 
00C710A1 lea   ecx,[esp] 
00C710A4 call  func (0C71000h) 
    func(&d); 
00C710A9 lea   ecx,[esp] 
00C710AC call  func (0C71000h) 
    func(&d); 
00C710B1 lea   ecx,[esp] 
00C710B4 call  func (0C71000h) 

Gdzie robi wysłać wskaźnik.

Moje założenie jest takie, że w pierwszym przypadku kompilator jest w stanie dowieść, że jeśli wygenerował niestandardową konwencję wywołującą dla funkcji, która nie może być widocznymi dla użytkownika efektami, ale w drugim przypadku, gdy przyjmiemy wskaźnik do funkcja może być wywołana z innego, osobno skompilowanego modułu, który nie ma możliwości sprawdzenia, czy parametr jest potrzebny, czy nie. Chociaż w tym przypadku nie ma znaczenia, czy rejestr został ustawiony, czy nie, ogólnie kompilator musi trzymać się dokładnego wzorca wywołującego, więc zakładam, że wygenerował on najbardziej ogólny kod i nie użyje niestandardowych konwencji wywoływania, jeśli to możliwe. udowodnić, że widzi cały kod, który mógłby wywołać funkcję.

W każdym razie, aby odpowiedzieć na pytanie, nie kompilator nie będzie zawsze przekazywał nieużywanych parametrów w rejestrze, ale na pewno nie napisałbym kodu, który zależy od konkretnego zachowania tutaj, jak zmiana niepowiązanego kodu w innym miejscu (przyjmowanie adresu funkcja), zmieniło to zachowanie i nic nie gwarantuje żaden standard, jaki widzę.

+0

ma sens, że podanie adresu funkcji nie pozwala na zoptymalizowanie nieużywanych parametrów, ponieważ wskaźnik funkcji jest wpisywany do podpisu funkcji (nawet jeśli rzutuje się go na void *), a kompilator nie może na ogół upewnij się, że użyjesz go ponownie przez jakąś dziwną magię. +1 za wskazanie tego problemu. –

+0

Rzeczywiście. Zastanawiam się, czy z generowaniem kodu czasu połączenia kompilator może i tak korzystać z niestandardowej konwencji wywoływania, ponieważ może "zobaczyć" cały kod, ale najwyraźniej nie, przynajmniej w przypadku mojego testu. – jcoder

+0

Głównym wnioskiem, który zabrałbym z mojego testu, nie jest ani jedno, ani drugie. Może, ale nie musi, przekazywać parametr do rejestru. – jcoder

1

Przede wszystkim kompilator powinien ostrzegać o nieużywanych argumentach funkcji, jeśli użyjesz odpowiednich flag kompilatora (na przykład: -Wall) .Przypisz by zostawić argumenty, których nie używasz z listy argumentów , chociaż sądzę, że zostaną zoptymalizowane. Co możesz zrobić, aby upewnić się, to jednak jest skompilowanie kodu dwa razy, raz z argumentami i raz bez, i zrobić diff między binariami i sprawdzić, czy pasują. Myślę, że powinien on zależeć od używanego kompilatora i flag optymalizacyjnych.

Cheers,

Andy

+0

zgodził się, że kompilator optymalizuje go, ale sprawdza go za pomocą 'diff' - jeden nie będzie w stanie sprawdzić różnic (ponieważ wykonanie zależy od wielu innych rzeczy, które mogą się różnić dla każdego procesu wykonywania, ponadto inicjalizacja zmiennych nie przyniesie tak wiele czas – exexzian

+0

@sansix, jeśli pliki binarne są równe, dlaczego zachowanie ma się różnić? –

+0

Przy okazji - diff b/w binaraies czego? (może myślę w inny sposób) – exexzian

0

Możesz pominąć argument w definicji funkcji. To daje wskazówkę do kompilator, że perticular argument nie jest używany, to zajmie korzyści z niego i optymalizować kod

void func (struct demo *) { ... }

Powiązane problemy