2015-02-23 20 views
11

Często widzę kod, który konwertuje ints na debla na ints na debel i znowu na powrót (czasami z dobrych powodów, czasami nie), i przyszło mi do głowy, że to wydaje się być "ukrytym" kosztem w moim programie. Załóżmy, że metodą konwersji jest obcięcie.Jak droga jest konwersja między int i double?

Tak, jak drogie jest to? Jestem pewien, że różni się w zależności od sprzętu, więc przyjmijmy nowy procesor Intela (Haswell, jeśli chcesz, chociaż weźmie wszystko). Niektóre dane byłbym zainteresowany (choć dobra odpowiedź nie musi mieć wszystkie z nich):

  1. # wygenerowanych instrukcji
  2. # cykli stosowanych
  3. koszt względna w stosunku do podstawowych operacji arytmetycznych

Zakładam również, że sposób, w jaki najbardziej dotkliwie odczuwamy wpływ powolnej konwersji, byłby związany z wykorzystaniem mocy, a nie z prędkością wykonywania, biorąc pod uwagę różnicę w liczbie obliczeń, które możemy wykonać w każdej sekundzie wiele danych może rzeczywiście dotrzeć do Procesor co sekundę.

+1

Nie ma sensu dyskutować bez konkretnego systemu. Na początek, niektóre systemy nie mają nawet FPU. – Lundin

+0

[liczba konwersji i wydajność] (http://stackoverflow.com/q/12920700/995714), [Jak przyspieszyć przeliczanie liczb zmiennoprzecinkowych na liczbę całkowitą?] (Http://stackoverflow.com/q/429632/995714), [Jaki jest najszybszy sposób konwersji floata na int na x86] (http://stackoverflow.com/q/78619/995714), [Czy typecasting zużywa dodatkowe cykle procesora] (http://stackoverflow.com/q/16539412/995714) –

Odpowiedz

20

Oto co mogę wykopać sobie:

  1. Kiedy take a look at the generated assembly z brzękiem i gcc, wygląda obsady int do double, to sprowadza się do jednej instrukcji: cvttsd2si. Od double do int jest cvtsi2sdl na klang, cvtsi2sd na gcc. Więc przypuszczam, że pojawia się pytanie: jaki jest koszt tych?
  2. Zgodnie z instrukcją Intel® 64 and IA-32 Architectures Optimization Reference Manual koszt instrukcji cvttsd2si wynosi 5 lat (patrz Załącznik C-16). Nie mogę znaleźć odwołania do cvtsi2sdl, ale cvtsi2sd, w zależności od architektury, ma opóźnienie w zakresie od 1 na Silvermont do bardziej podobnych do 7-16 na kilku innych architekturach. Podręcznik definiuje opóźnienie jako: "Liczba cykli zegara wymaganych dla rdzenia wykonawczego w celu zakończenia wykonywania wszystkich μ μs, które tworzą instrukcję."
  3. Ta sama instrukcja mówi, że instrukcja add kosztuje 1 latencję i mul kosztuje 3-4 (Załącznik C-27)

Tak, odpowiedź sprowadza się do: 1) To sprzętu zoptymalizowane, a kompilator wykorzystuje maszyny sprzętowe. 2) Kosztuje tylko nieco więcej niż mnożenie w kategoriach liczby cykli w jednym kierunku i bardzo zmiennej ilości w drugim (w zależności od architektury). Jego koszt nie jest ani darmowy, ani absurdalny, ale prawdopodobnie wymaga większej uwagi, biorąc pod uwagę, jak łatwo jest pisać kod, który ponosi koszty w nieoczywisty sposób.

+4

Dla jasności: Wspaniały podręcznik Agner Fog "Tabele instrukcji" informuje, że Haswell, rejestracja _integer_ "add" ma opóźnienie = 1, przepustowość = 0.25; rejestr rejestrów całkowitych 'mul/imull' 64x64-bit ma lat = 3, 1/thru = 1, rejestr rejestru zmiennoprzecinkowego' addss/ps/sd/pd' ma lat = 3, 1/thru = 1, pływający rejestru punktów 'mulss/ps/sd/pd' ma lat = 5, 1/thru = 0.5 i różne konwersje' cvt * 'pomiędzy 32-bitowymi i 64-bitowymi liczbami całkowitymi i wartościami zmiennoprzecinkowymi dla większości część ma lat = 3-4 i 1/thru = 1. –

+1

@IwillnotexistIdonotexist - Thorough :). Bardzo zobowiązany! – Mark

3

Oczywiście tego rodzaju pytanie zależy od dokładnego sprzętu, a nawet od trybu.

Na x86 moja i7 gdy używany w trybie 32-bitowym z domyślnymi opcjami (gcc -m32 -O3) konwersja z int do double jest dość szybki, odwrotnie natomiast jest znacznie wolniejszy ponieważ C Standard mandatów absurdalne reguły (obcinanie miejsc dziesiętnych).

Ten sposób zaokrąglania jest szkodliwy zarówno dla matematyki, jak i dla sprzętu i wymaga przełączenia FPU na ten specjalny tryb zaokrąglania, przeprowadzenia skracania i przejścia z powrotem do rozsądnego sposobu zaokrąglania.

Jeśli potrzebujesz prędkości, wykonując konwersję float-> int używając prostej instrukcji fistp, jest ona szybsza, a także znacznie lepsza dla wyników obliczeń, ale wymaga pewnego wbudowanego zespołu.

inline int my_int(double x) 
{ 
    int r; 
    asm ("fldl %1\n" 
     "fistpl %0\n" 
     :"=m"(r) 
     :"m"(x)); 
    return r; 
} 

więcej niż 6 razy szybciej niż naiwnych x = (int)y; konwersji (i nie ma odchylenie ku 0).

Ten sam procesor w trybie 64-bitowym nie ma jednak problemów z szybkością, a użycie kodu fistp powoduje, że kod działa nieco wolniej.

Najwyraźniej sprzęt komputerowy poddał się i zaimplementował algorytm złego zaokrąglania bezpośrednio w sprzęcie (więc zły kod może teraz działać szybko).

+1

Na jakiej platformie doszliście do wniosku, że jest 6 razy szybszy? Rok lub dwa wstecz, brałem udział w podobnym pytaniu, w którym ktoś zapytał, dlaczego kod w twojej odpowiedzi był lepszy, a moja natychmiastowa odpowiedź brzmiała: "skąd wiesz, że to jest lepsze", i bardzo się okazuje, że jeśli masz procesor obsługujący SSE (tak dla x86, coś wprowadzonego od około 2000), wtedy szybciej NIE używa się tej sztuczki, ale po prostu pozwól kompilatorowi wygenerować "właściwą" instrukcję. Zobaczę, czy uda mi się znaleźć odpowiedź, ale teraz muszę iść do pracy, więc zrobię to później. –

+0

@MatsPetersson: testowano na i7, ale kompilując '-m32', problem nie występuje (w rzeczywistości jest to szybsze w użyciu naiwna konwersja) podczas kompilowania 64-bitowego kodu. – 6502

+1

Co zrobić, jeśli używasz '-m32 -msse2'? –

Powiązane problemy