2016-01-20 7 views
9

"as-if rule" daje kompilatorowi prawo do optymalizacji lub zmiany kolejności wyrażeń, które nie wpłyną na wydajność i poprawność programu zgodnie z określonymi regułami, takimi jak;Reguła as-if i usunięcie przydziału

§1.9.5

Wykonanie zgodne wykonywanie również uformowaną programu są produkować ten sam problem zauważalnego jako jeden z możliwych wykonaniach z odpowiednim przykład abstrakcyjnej maszyny za pomocą samego programu i to samo wejście.

cppreference url I połączone powyżej wyraźnie wymienia szczególne zasady dotyczące wartości lotnych obiektów, a także dla „nowych wyrażeń”, pod C++ 14:

Nowa ekspresja ma inny wyjątek z reguły "jak gdyby": kompilator może usuwać wywołania do wymiennych funkcji przydziału, nawet jeśli dostarczono zdefiniowaną przez użytkownika wymianę i ma zauważalne skutki uboczne.

zakładam „wymienny” tutaj jest to, co mówi się na przykład w

§18.6.1.1.2

wymienianej: programu C++ mogą zdefiniować funkcję z tej funkcji podpisu który zastępuje domyślną wersję zdefiniowaną przez standardową bibliotekę C++ .

Czy to poprawne, że poniższe mem może zostać usunięte lub zmienione zgodnie z zasadą "jak to"?

{ 
    ... some conformant code // upper block of code 

    auto mem = std::make_unique<std::array<double, 5000000>>(); 

    ... more conformant code, not using mem // lower block of code 
    } 

Czy istnieje sposób, aby upewnić się, że nie został usunięty i pozostaje między górnym i dolnym blokiem kodu? Pojawia się dobrze ulotny (albo niestabilny std :: array albo left of auto), ale ponieważ nie ma odczytu z mem, myślę, że nawet to nie pomogłoby zgodnie z zasadą jak-if.

Nota boczna; Nie udało mi się zdobyć pracowni wizualnej 2015, by zoptymalizować mem i alokować w ogóle.

Wyjaśnienie: Droga do obserwować byłoby to, że wywołanie przypisania do systemu operacyjnego znajduje się między każdym wejściu/wyjściu z dwóch bloków. Chodzi o przypadki testowe i/lub próby przydzielenia obiektów w nowych lokalizacjach.

+1

Wierzę, że lotny by tam pomógł, ale jest inny problem: nic nie zależy od wartości mem, więc kompilator może przenosić alokację gdziekolwiek w tym bloku kodu. Może przydzielić na samym początku, na końcu i w każdym innym miejscu. –

+0

@Revolver_Ocelot Nie sądzę, że lotny może nawet coś zmienić. 'make_unique' tworzy nowy obiekt i, o ile wiem, kompilatory C++ _nie_ optymalizują tworzenie obiektów ze względu na potencjalne efekty uboczne. Mogą one powodować niepotrzebne wywołania konstruktorów, ale zawsze zapewniają, że przynajmniej jeden z nich jest wywoływany. W przeciwnym razie kod wykorzystujący obiekty dla RAII posiadającego konstruktory z efektami ubocznymi nie mógł bezpiecznie polegać na tym wzorze bez uwzględnienia barier pamięci. – JAB

+0

** [intro.execution]/8 ** mówi: "Dostęp do obiektów lotnych jest oceniany ściśle według reguł maszyny abstrakcyjnej_". Przeczytałem to jako "jak-jeśli reguła nie może być zastosowana do lotnych obiektów", więc powinna pomóc. –

Odpowiedz

4

Tak; Nie. Nie w C++.

Abstrakcyjna maszyna C++ nie mówi w ogóle o wywołaniach systemowych. Tylko skutki uboczne takiego wywołania, które wpływają na zachowanie abstrakcyjnej maszyny, są naprawiane przez C++, a nawet wtedy kompilator może dowolnie robić coś innego, o ile - jeśli powoduje to to samo obserwowalne zachowanie ze strony program w abstrakcyjnej maszynie.

W maszynie abstrakcyjnej auto mem = std::make_unique<std::array<double, 5000000>>(); tworzy zmienną mem.To, jeśli jest używane, daje dostęp do dużej ilości double s spakowanych w tablicę. Maszyna abstrakcyjna może rzucić wyjątek lub dostarczyć tak dużą liczbę double s; albo jest w porządku.

Należy pamiętać, że jest to legalne kompilator C++, aby zastąpić wszystkie przydziały przez new z bezwarunkową throw wystąpienia awarii alokacji (lub powrocie nullptr dla wersji bez rzutów), ale byłoby to słaba jakość wykonania.

W przypadku, gdy jest przydzielony, standard C++ tak naprawdę nie określa, skąd pochodzi. Kompilator może na przykład używać tablicy statycznej i sprawić, że delete zadzwoni jako no-op (zauważ, że może to wymagać przechwycenia wszystkich sposobów wywołania delete na buforze).

Następnie, jeśli masz tablicę statyczną, jeśli nikt jej nie czyta ani nie zapisuje (a konstrukcja nie może być obserwowana), kompilator może ją wyeliminować.


W związku z powyższym wiele z powyższych elementów opiera się na kompilatorze, który wie, co się dzieje.

Podejście polega na uniemożliwieniu znajomości kompilatora. Czy twój kod ładuje bibliotekę DLL, a następnie przekazuje wskaźnik do unique_ptr do tej biblioteki DLL w punktach, w których chcesz znać jej stan.

Ponieważ kompilator nie może zoptymalizować wywołań DLL w czasie wykonywania, stan zmiennej musi w zasadzie być zgodny z oczekiwaniem.

Niestety, nie ma standardowego sposobu dynamicznego ładowania kodu takiego jak w C++, więc będziesz musiał polegać na bieżącym systemie.

Wspomniana biblioteka DLL może być oddzielnie zapisywana jako odbiór; lub, nawet, możesz zbadać jakiś stan zewnętrzny i warunkowo załadować i przekazać dane do biblioteki DLL na podstawie stanu zewnętrznego. Tak długo, jak kompilator nie może udowodnić, że wymieniony stan zewnętrzny wystąpi, nie można zoptymalizować wokół wywołań , a nie. Wtedy nigdy nie ustawiaj tego zewnętrznego stanu.

Deklaracja zmiennej u góry bloku. Przekaż mu wskaźnik do fałszywej zewnętrznej biblioteki DLL, gdy nie jest ona zainicjowana. Powtórz tuż przed zainicjowaniem, a następnie po. W końcu zrób to na końcu bloku, zanim go zniszczysz, a następnie zrób to jeszcze raz.

+0

Które z kilku pytań zawiera Twój "Nie" odpowiedź? –

+2

@BenVoigt Widzę dwa "?". Odpowiedziałem im teraz w kolejności. – Yakk

+1

To, co wspomniałeś o "rzucie", jest interesujące, ponieważ wyjątek byłby widoczny poza tym blokiem kodu i nie może pojawić się przed efektami ubocznymi górnego kodu lub po efektach ubocznych niższych. –