2012-01-26 10 views
7

Visual C++ może emitować C4738 warning:Dlaczego nie ma ostrzeżenia takiego jak C4738 dla podwójnego?

przechowywania 32-bitowy wynik pływakowego w pamięci, możliwe straty wydajności

w przypadkach, gdy 32-bitowa float ma być przechowywany w pamięci, a przechowywane w rejestrze.

Opis mówi dalej, używając rozwiązania double. Nie rozumiem, dlaczego ta ostatnia jest prawdziwa.

Dlaczego przechowywanie float w pamięci skutkuje utratą wydajności, a przechowywanie double nie powoduje?

Odpowiedz

7

Ostrzeżenie łączy dwie kwestie:

  • Pływaki muszą być przechowywane w pamięci zamiast rejestrów, które mogą zmniejszyć wydajność (ponieważ pamięć jest znacznie wolniejszy niż rejestrów)
  • pływaków zostanie zaokrąglona (bo rejestrów mają zawsze 64 lub 80 bitów, ale w pamięci float ma tylko 32 bity).

Używanie podwójnego rozwiązania drugiego problemu (przynajmniej częściowo, 64 bity są nadal mniej precyzyjne niż 80 bitów), ale nie ma wpływu na możliwą utratę wydajności. Dlatego też DECRIPTION ostrzeżenie wspomina o dwóch środków:

Aby rozwiązać to ostrzeżenie i uniknąć zaokrąglania, skompilować z/fp: szybkie lub użycie podwaja zamiast pływaków.

Aby rozwiązać to ostrzeżenie i uniknąć wyczerpania rejestrów, zmień kolejność obliczeń i modyfikować swoje zastosowanie inline

+0

Dlaczego "unoszenie się" powodowałoby utratę wydajności, ale nie "podwójne"? – tenfour

+0

@tenfour: * oba * spowodują utratę wydajności, ale podwójna wartość nie zostanie zaokrąglona tak bardzo. –

+2

@tenfour: Gdy przechowujesz 64-bitowy rejestr zmiennoprzecinkowy w 32-bitowej pamięci, musisz zaokrąglić, a zaokrąglanie zajmuje trochę czasu. Kiedy przechowujesz 64-bitowy rejestr zmiennoprzecinkowy w 64-bitowej pamięci, nie musisz zaokrąglać, po prostu przechowywać. (Większość liczb zmiennoprzecinkowych jest teraz nieczynna z rejestrami SSE, więc rzeczy 80-bitowe są nieistotne.) –

3

Chociaż nie jestem w 100% pewien przyczyny, oto moje przypuszczenie.

Podczas kompilacji na x86 i SSE2 nie jest włączona, kompilator musi używać stosu XP x87 dla wszystkich rejestrów zmiennoprzecinkowych. W MSVC domyślnie tryb FP jest ustawiony na 53-bitowe zaokrąglenie precyzyjne. (Myślę, że nie jestem w 100% pewny).

Dlatego wszystkie operacje wykonywane na stosie FP są wykonywane z podwójną precyzją.

Jednak, gdy coś jest rzucane do float, precyzja musi być zaokrąglona do pojedynczej precyzji. Jedynym sposobem, aby to zrobić, jest zapisanie go w pamięci za pomocą instrukcji fstp przez 4-bajtowy operand pamięci - i ponowne załadowanie.


Spójrzmy na przykład na C4738 warning page ty związane z:

float func(float f) 
{ 
    return f; 
} 

int main() 
{ 
    extern float f, f1, f2; 
    double d = 0.0; 

    f1 = func(d); 
    f2 = (float) d; 
    f = f1 + f2; // C4738 
    printf_s("%f\n", f); 
} 

Po wywołaniu func(), d jest prawdopodobnie przechowywane w rejestrze x87. Jednak połączenie z numerem func() wymaga obniżenia precyzji do pojedynczej precyzji. To spowoduje, że d zostanie zaokrąglone/zapisane w pamięci. Następnie ponownie załadowano i ponownie promowano do podwójnej precyzji linii f = f1 + f2;.

Jednakże, jeśli używasz double przez cały czas, kompilator może przechowywać d w rejestrze - tym samym pomijając napływ do i z pamięci.


Co do tego, dlaczego może zabraknąć rejestrów ... Nie mam pojęcia. Możliwe, że semantyka programu może spowodować uzyskanie zarówno podwójnej precyzji, jak i wartości o pojedynczej precyzji, o tej samej wartości - co w tym przypadku wymaga dodatkowego rejestru.

Powiązane problemy