2016-05-12 13 views
7

Zakładając, że posiada struct C/C++ z elementów macierzy stałym rozmiarze, npCzy kompilator zoptymalizuje funkcje zwracające struktury z macierzami o ustalonych rozmiarach?

#define SIZE 10000 
struct foo{ 
    int vector_i[SIZE]; 
    float vector_f[SIZE]; 
}; 

i am, aby utworzyć funkcję powróci instancją foo jak:

foo func(int value_i, float value_f){ 
    int i; 
    foo f; 
    for(i=0;i<SIZE;i++) f.vector_i[i] = value_i; 
    for(i=0;i<SIZE;i++) f.vector_f[i] = value_f; 
    return f; 
} 

Gdybym wywołania funkcji przy użyciu:

foo ff = func(1,1.1); 

będzie kompilator wykonać jakąś optymalizację (tj TCO)?

Czy wykonywalny wypełnić bezpośrednio ff zmienną, czy będzie to wypełnienie pierwszy f z func a następnie skopiować wszystkie wartości z f do ff?

Jak mogę sprawdzić, czy optymalizacja jest wykonywana?

+2

Jest to dość ogromny obiekt być przechowywane w postaci zmiennej lokalnej. Jeśli wyliczyłem to poprawnie, powinno to być 625kb (na 32-bitowej platformie), gdy w oknach wydaje mi się, że każdy stos wątków może wynosić do 1mb – GeorgeAl

+3

Przekaż zamiast tego wskaźnik (lub odwołanie) do struktury jako argumentu, a po prostu nie " Nie martw się o to. –

+2

Możesz spojrzeć na wygenerowany asembler. W C++ można również zdefiniować konstruktor kopii i sprawdzić, czy zostanie wywołany. –

Odpowiedz

6

Moja odpowiedź dotyczy C++.

Czy kompilator wykona jakąś optymalizację (np. TCO)?

Według TCO masz na myśli "optymalizację połączeń"? Funkcja nie wywołuje wywołania funkcji na końcu (wywołanie tail, jeśli chcesz), więc optymalizacja nie ma zastosowania.

Kompilator możeelide skopiować z wartości zwracanej do tymczasowej z powodu nazwanej optymalizacji wartości zwracanej. Inicjowanie kopii z tymczasowego może również zostać anulowane.


Jak mogę sprawdzić, czy optymalizacja jest wykonywana?

Odczyt wygenerowanego kodu zespołu.

Jeśli nie można odczytać zestawu, innym podejściem byłoby dodanie kopiowania i przenoszenia konstruktorów, które mają skutki uboczne i obserwowanie, czy występują te efekty uboczne. Jednak modyfikowanie programu może mieć wpływ na to, czy kompilator zdecyduje się zoptymalizować (ale efekty uboczne nie są wymagane, aby zapobiec kopiowaniu).


Jeśli nie chcesz polegać na optymalizacji, należy wyraźnie przekazać obiekt wychodzący do funkcji przez odniesienie (wskaźnik w C) i zmodyfikować go w miejscu.


standard odniesienia dla kopiowania elizji [class.copy] §31 (current standard draft)

Kiedy pewne kryteria są spełnione, to implementacja wolno pominąć budowę kopia/ruch obiektu klasy, nawet jeśli konstruktor wybrany dla kopii/operacja move i/lub destruktor obiektu mają efekty uboczne. [...]

W tej sekcji opisano kryteria, które są w tym przypadku spełnione. Oferta została wygenerowana ze standardowego projektu dokumentu na dzień 2016-04-07. Numeracja może się różnić w różnych wersjach standardowego dokumentu, a zasady nieznacznie się zmieniły. Cytowana część została niezmieniona od C++ 03, gdzie sekcja jest [class.copy] §15.

+0

Czy nie wywoływanie tych efektów ubocznych naruszałoby poprawność kodu? Nie jestem pewien co do C++, ale w C istnieje coś, co nazywa się "obserwowalnym zachowaniem", które nie może ulec zmianie przez optymalizacje kompilatora. – Olaf

+3

@Olaf copy elision ma specjalny wyjątek do zignorowania reguły as-if. Nie wiem o c, więc dodałem zastrzeżenie (chociaż kopiowanie nie może mieć efektów ubocznych oprócz samej kopii c), czyż nie?). – user2079303

+0

@ user2079303 Kopiowanie w C nie ma żadnych skutków ubocznych, więc nie ma tam problemu. – molbdnilo

2

Jest to dość dobrze udokumentowane w Agner Fog's Calling Conventions document, § 7.1 Mijające i zwracające obiekty, Tabela 7. Metody zwracania obiektów struktury, klasy i połączeń.

Obiekt struktury, klasy lub związku można zwrócić z funkcji w rejestrach tylko wtedy, gdy jest wystarczająco mały i niezbyt skomplikowany. Jeśli obiekt jest zbyt skomplikowany lub nie mieści się w odpowiednich rejestrach, wówczas wywołujący musi zapewnić przestrzeń pamięci dla obiektu i przekazać wskaźnik do tej przestrzeni jako parametr tej funkcji. Wskaźnik można przekazać w rejestrze lub na stosie. Ten sam wskaźnik jest zwracany przez funkcję. Szczegółowe zasady podano w tabeli 7.

Innymi słowy, duże obiekty zwracające są konstruowane bezpośrednio w buforze dostarczonym przez wywołującego (na stosie wywołującego).

Dodatkowa kopia jest nadal wymagane, jeżeli tożsamość obiektu do powrotu nie jest znany w czasie kompilacji, np .:

foo func(bool a) { 
    foo x, y; 
    // fill x and y 
    return a ? x : y; // copying is required here 
} 
Powiązane problemy