Pracuję nad projektem DSP (filtrowanie IIR) na cyfrowym procesorze sygnałów Analog Devices (BF706) z dołączonym pakietem kompilatorów, CrossCore Studio. Zawiera kilka przykładów prostych rzeczy DSP, takich jak filtry FIR i IIR oraz funkcje biblioteczne. Instrukcja procesora opisuje zestaw instrukcji montażu i nie komentuje C.
MOJE pytanie wynika z tej konkretnej aplikacji, ale myślałem, że istnieje najlepsza praktyka, którą przestrzegają programiści DSP. Tak więc ułożę to w ogólny sposób:
Z przykładów dostarczonych z tym DSP zdałem sobie sprawę, że jeśli chcę korzystać z obwodów zaprojektowanych dla aplikacji DSP, muszę zaprogramować w asemblerze, aby bezpośrednio uruchamiać te instrukcje. (Np. Mnożenie i dodawanie itp.) Moje pytanie brzmi, czy Po prostu programuję w C, czy kompilator (który również pochodzi od producenta układów DSP) nie zoptymalizuje go pod kątem tego DSP i nie wykorzysta jego możliwości? Czy też naprawdę muszę pisać procedury DSP bezpośrednio w asemblerze?
źródło
Odpowiedzi:
Zawsze lepiej jest mieć algorytm zaimplementowany w języku wyższego poziomu (który C jest porównywany do asemblera), nawet jeśli planujesz zaimplementować wszystko w asemblerze.
są szanse, że nie będziesz nawet potrzebował montażu . Jeśli kod wygenerowany przez kompilator spełnia założone cele projektowe, zadanie jest wykonane.
jeśli nie, nie zaczniesz kodowania asemblera od zera . Pozwól kompilatorowi wygenerować kod początkowy i użyj go jako podstawy dla zoptymalizowanej wersji zestawu.
później, gdy będziesz musiał przetestować zoptymalizowany kod asemblera , będziesz zadowolony z posiadania wersji C. Zamiast ręcznie obliczać poprawne dane wyjściowe dla testowych danych wejściowych, możesz po prostu wprowadzić te dane wejściowe do niezoptymalizowanej implementacji C, a następnie sprawdzić, czy zespół produkuje dokładnie takie same dane wyjściowe po dokonanych optymalizacjach.
Jeśli po kilku latach nowy programista będzie musiał zmodyfikować algorytm, a wszystko, co ma pod ręką, to wysoce zoptymalizowany kod asemblera, istnieje duża szansa, że będą musieli zacząć od zera.
źródło
Jeśli autorzy kompilatora włożą trochę wysiłku w zoptymalizowanie go pod kątem tego celu, przynajmniej skorzystają ze specjalnych instrukcji / architektury DSP. Ale dla najwyższej wydajności nigdy nie będzie tak dobry, jak ręcznie dostrojony montaż. Może to być wystarczająco dobre - zależy od twojej aplikacji.
Inne alternatywy to:
źródło
Przedwczesna optymalizacja jest źródłem wszelkiego zła. - Donald Knuth
Jeśli okaże się, że kod nie zapewnia wystarczającej wydajności, najpierw profiluj swój program, znajdź wąskie gardła, analizuj wymagania dotyczące wydajności, a dopiero potem zacznij optymalizować. Pisanie kodu asemblera jest ostatecznością.
Tak, kompilator C może przeprowadzić sporo optymalizacji. Zależy to jednak od jakości kompilatora. Często człowiek może napisać szybszy kod zestawu niż skompilowany kod C. To znaczy kosztem ludzkiego bólu i cierpienia.
Najpierw napisz w C, następnie profil, a następnie zdecyduj, czy chcesz pisać w asemblerze. Mamy nadzieję, że nie będziesz potrzebował zgromadzenia.
źródło
Twój DSP będzie reklamowany z maksymalną trwałością adresów MAC, przy założeniu, że wszystkie potoki są wypełnione. Jest to oczywiście górna granica tego, co można osiągnąć. Wiesz, ile adresów MAC zabiorą twoje filtry i inne przetwarzanie, z analizy. Staraj się mieć pierwszy przynajmniej dwa razy drugi, ponieważ nie będziesz w stanie utrzymać rdzenia DSP na maksimum. Podobnie jak nie próbowałbyś wypełnić FPGA powyżej 70% zasobu (PAR staje się bardzo wolny powyżej tego), rozwój może być bardzo powolny, próbując wycisnąć kilka ostatnich teoretycznych MAC z DSP.
Będziesz kodować całą aplikację w C. Zapisywanie wszystkich dodatkowych niezbędnych elementów w asemblerze, testowanie wtrysku i widoczności, sprzątanie itp. Niepraktyczne jest napisanie wersji C filtra testowego. Napisz wersję asemblera tego samego filtra, aby sprawdzić, czy rzeczywiście możesz napisać asembler dla tej bestii.
Teraz zrób trochę czasu. Użyj RTOS zatwierdzonego przez dostawcę. Porównaj czas działania modułu asemblera testowego z wersją C. Jeśli są w granicach kilku procent, przejdź dalej. Jeśli jest trzykrotnie, przeczytaj dokumentację, wypytaj sprzedawcę i dowiedz się, dlaczego kompilator go nie dostraja. Być może będziesz musiał nauczyć się pisać jego smak C tak samo, jak ustawić poprawne flagi kompilatora, szybciej będzie dowiedzieć się, jak właściwie prowadzić kompilator, niż przepisać wszystko w asemblerze.
Zrobiłeś to wszystko przed zaangażowaniem się w procesor DSP, w łańcuch narzędzi.
Gdy masz już zestaw narzędzi, z którym możesz pracować, kompilator, który możesz dostroić, aby uzyskać rozsądnie zbliżone do maksimum, procesor DSP z pewną wolną przestrzenią czasową, możesz mieć pewność, że bardzo mało części pakietu kodu będzie wymagało wprowadzenia asembler, aby zakończyć zadanie.
źródło
Mimo że już odpowiedziałem na to pytanie, dodam inną odpowiedź, aby zilustrować inny punkt widzenia:
Napisz w C, przeczytaj w zestawie!
Zamiast pisać w asemblerze, logika zostanie napisana w C, uważnie upewniając się, że wyjście asemblera kodu C jest optymalne. Często można wykonać pewne sztuczki w kodzie C, aby wpłynąć na dane wyjściowe asemblera. Użyj statycznych funkcji wstawianych, gdy ma to sens. Jeśli potrzebujesz użyć specjalnych instrukcji obsługiwanych przez DSP, wykonaj statyczną abstrakcyjną funkcję inline specjalnej instrukcji i wywołaj specjalną instrukcję za pomocą abstrakcji.
Chociaż muszę powiedzieć, że nigdy nie programowałem DSP, to podejście polegające na pisaniu kodu C przy jednoczesnym uważnym obserwowaniu skompilowanego zestawu działa na mnie wyjątkowo dobrze na maszynach x86. Tak dobrze, że nigdy nie musiałem nic pisać w asemblerze, aby uzyskać najlepszą możliwą wydajność. Zamiast optymalizować kod zestawu zmodyfikuję kod C w taki sposób, aby zestaw był optymalny.
Oczywiście zależy to od dostępności dobrych kompilatorów C. W przypadku x86 dostępne są takie kompilatory (często trzeba określić wyższy poziom optymalizacji niż domyślny). W przypadku procesorów DSP szczerze mówiąc nie wiem, czy kompilatory są tak dobre.
Zaletą tego podejścia jest to, że masz pojedynczą przenośną bazę kodu, zoptymalizowaną pod kątem optymalnego złożenia dla danego DSP, ale działa również, jeśli DSP zostanie zmieniony na coś innego. Oczywiście może być konieczne nieznaczne dostosowanie kodu C, aby uzyskać najlepszą możliwą wydajność nowego DSP.
źródło
Zasadniczo pisanie źródeł asemblera nie jest konieczne, jeśli:
Oznacza to ręczne sprawdzenie asemblera wygenerowanego przez kompilator C (dla części krytycznych) i modyfikowanie źródła aż do wystarczającego poziomu optymalizacji.
źródło
Powiedziałbym tutaj, że jeśli robisz filtry FIR / IIR, o wiele ważniejsze jest, którego algorytmu używasz (algorytm trywialny kontra szybka transformata Fouriera (FFT)), niż jakiego języka używasz (C vs. asembler).
Czy napisałbym FFT w asemblerze? Prawdopodobnie nie.
Czy sam napisałbym FFT? Odpowiedź na to pytanie również prawdopodobnie nie jest, ponieważ FFT był już wielokrotnie wdrażany. Są szanse, że znajdziesz bibliotekę, która ma już zaimplementowaną FFT. Biorąc pod uwagę, że C jest przenośnym językiem, podczas gdy asembler nie, znacznie bardziej prawdopodobne jest znalezienie istniejących bibliotek już zaimplementowanych w C.
Jeśli chcesz jak najbardziej ekstremalnej wydajności, możesz oczywiście dostroić algorytm FFT, aby działał jak najszybciej w języku asemblera. Ale tak naprawdę nie sądzę, aby miało to sens, chyba że w wyjątkowych okolicznościach.
źródło
Moim zdaniem FWIW jest taki, że za każdym razem, gdy chcesz maksymalnej prędkości / wydajności / przepustowości / cokolwiek, asembler jest twoim przyjacielem, o ile jesteś biegły. Kompilator jest głupi; „wie” tylko to, co jego autor chciał w niego zaprogramować, a jego autor wcale nie znał twojej aplikacji.
Muszę przyznać, że kochałem asemblera od wczesnych lat 80-tych 8-bitowe mikroskopy (pod wieloma względami zupełnie niepodobne do współczesnych MCU), gdzie nauka „kodu maszynowego” była niezbędna do uzyskania z nich jakiejkolwiek użytecznej wydajności, ale myślę, że jego rola pozostaje jak na drodze do programu dla maksymalnej wydajności. Co więcej, jest to bardzo satysfakcjonujące, ponieważ możesz wprowadzić wszelkiego rodzaju skróty optymalizacyjne, o których kompilator nie pomyśli, ponieważ kompilator nie może w ogóle myśleć.
C chyba jest w porządku. Ale jeśli naprawdę wiesz, co chcesz, aby Twoja maszyna robiła na poziomie sprzętowym, przejdź do asemblera.
źródło