2012-12-11 17 views
5

Jeśli piszesz aplikację, która jest bardzo latency czuły Jakie są granice do osadzania asemblera wewnątrz funkcji C++ (i przy użyciu funkcji C++ zwraca normalnie), tak jak poniżej:Osadzanie asemblera w C++ jest dopuszczalne?

inline __int64 GetCpuClocks() 
{ 

    // Counter 
    struct { int32 low, high; } counter; 

    // Use RDTSC instruction to get clocks count 
    __asm push EAX 
    __asm push EDX 
    __asm __emit 0fh __asm __emit 031h // RDTSC 
    __asm mov counter.low, EAX 
    __asm mov counter.high, EDX 
    __asm pop EDX 
    __asm pop EAX 

    // Return result 
    return *(__int64 *)(&counter); 

} 

(Powyższa funkcja pochodziła z inny post SO, który widziałem)

Czy potrafisz leczyć funkcje asemblera, takie jak czarne pudełko? Czy możesz łatwo pobrać wynik z obliczeń wykonanych w asembler? Czy istnieje niebezpieczeństwo, że nie wiesz, jakie zmienne znajdują się obecnie w rejestrach itp.? Czy powoduje więcej problemów, niż rozwiązuje, czy jest dopuszczalne w przypadku określonych małych zadań?

(zakładamy, że architektura ma być stałe i znane)

EDIT Właśnie znalazłem to jest to co ja sugerując:

http://www.codeproject.com/Articles/15971/Using-Inline-Assembly-in-C-C

Edit2 ta jest bardziej mającej ku Linux i x86 - to tylko ogólne pytanie C++/asemblera (lub tak myślałem).

+1

Czy pytasz o Visual C++? Przypuszczam, że inne kompilatory mogą mieć inne ograniczenia. –

+0

@ Robᵩ Nope, jeśli cokolwiek celowałem w Linuksa, ICC i G ++. Właśnie chwyciłem pierwszą funkcję asemblera, którą widziałem. – user997112

+1

To może być nieco OT, ale jeśli skok i powrót nie powodują zbyt dużych kar, rozważ napisanie asemblera w czystym asemblerze (w oddzielnej jednostce kompilacji), aby twój kod był bardziej przenośny. Unikając wprowadzania danych, można czasami poprawić opóźnienie dzięki bardziej wydajnemu wykorzystaniu pamięci podręcznej. Jest to jednak ważniejsze na platformach wbudowanych. – psyill

Odpowiedz

1

Jeśli omawiany kwestionariusz przesunie wszystkie rejestry, których używa u góry, a następnie wyskakuje u dołu, myślę, że nie należy się tym martwić.

W twoim przykładzie są to instrukcje __asm push EAX i __asm pop EAX.

Prawdziwą odpowiedzią, jak sądzę, jest to, że musisz wiedzieć wystarczająco dużo o tym, co robi asm, aby mieć pewność, że możesz traktować to jako czarną skrzynkę. :)

+0

Więc w zasadzie upewnij się, że stan, w którym zaczynasz, jest stanem, w którym kończysz? Co jeśli chcesz zwrócić obliczenia z asemblera, jak by to zrobić? – user997112

+0

Tak, upewnij się, że nie zepsułeś stanu. Zwrócenie wartości zależeć będzie od kompilatora, jak sądzę. – Almo

3

Chciałbym odpowiedzieć na subquestion:

Czy to spowodować więcej problemów niż rozwiązuje, czy jest to dopuszczalne dla poszczególnych małych zadań?

Z pewnością tak! Korzystając z asemblera wbudowanego, możesz skorzystać z możliwości kompilatora, aby zoptymalizować kod. Nie może wykonywać substytucji wyrażenia częściowego ani żadnej innej fantazyjnej optymalizacji. Naprawdę trudno jest wyprodukować kod, który jest lepszy od tego, co kompilator emituje z -O3. A jako bonus, kod staje się jeszcze lepszy przy następnym wydaniu kompilatora (zakładając, że następne wydanie kompilatora go nie zepsuje;)).

Kompilatory zazwyczaj mają szerszy zasięg niż ludzki mózg, jaki kiedykolwiek mógł (lub powinien, aby zapewnić zdrowie psychiczne), będąc w stanie wstawić odpowiednią funkcję we właściwym miejscu, dokonać substytucji wyrażenia częściowego, co czyni kod bardziej efektywnym. Rzeczy, których nigdy nie zrobiłbyś w ASM, ponieważ twój kod staje się nieczytelny jak diabli.

Jako anegdotyczne odniesienie, chciałbym, aby this post przez Linus Torvalds, odnoszące się do implementacji git SHA1, który przewyższa zoptymalizowany ręcznie SHA1 w libcrypt.

W rzeczywistości wydaje mi się, że jedynym rozsądnym zastosowaniem wbudowanego asemblera jest obecnie wywoływanie instrukcji procesora, które nie są dostępne w inny sposób (ten, który cytowałeś, jest dostępny, na przykład na Linuksie jako clock_gettime, przynajmniej jeśli jesteś tylko po licznik czasu wysokiej rozdzielczości) lub jeśli musisz zrobić rzeczy, w których musisz oszukać kompilator (na przykład podczas implementacji obcych funkcji interfejsów).


Na temat fragmentu i tego, co powiedzieli inni. Zwłaszcza w przypadku takich funkcji dostaniesz karę wykonania. W inline asm musisz być bardzo ostrożny, aby rejestry były przechowywane w stanie, w jakim kompilator je zakłada (push/pop, jak wyżej). Podczas normalnego pisania kodu kompilator może zadbać o zachowanie tych zmiennych, dla których ma sens w rejestrach i tych, które nie mieszczą się na stosie.

Zaufaj kompilatorowi. Jest sprytny. Większość czasu. Zainwestuj czas, który zapiszesz, nie używając wbudowanego asemblera w myśleniu o inteligentnych, szybkich algorytmach i uczeniu się odpowiednich przełączników kompilatora (na przykład w celu umożliwienia optymalizacji SSE itp.).

+0

Z pewnością można się jednak spierać, że kompilator nie może być we wszystkim niesamowity. Aby zrekompensować szeroki zakres przypadków, z którymi może sobie poradzić, prawdopodobnie istnieje wiele obszarów, w których dla niewielkiego określonego zadania programista mógłby napisać mniej instrukcji asm? – user997112

+0

@ user997112 Jakie przypadki masz na myśli? Myśląc o wszystkim, co ma związek z numerami, prawdopodobnie nie będziesz w stanie tego zrobić. Zwróć też uwagę, że zamieniłem referencję, moja oryginalna faktycznie zawierała ASM. –

+0

Nie mam nic na myśli, ale na pewno byłoby przydatne, gdyby można było sprawdzić, czy są jakieś obszary kompilatorów są złe. – user997112