Pisanie algorytmów DSP bezpośrednio w C lub asemblerze? [Zamknięte]

18

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?

doubleE
źródło
17
Spędziłem wiele lat pisząc asembler dla ADSP-21xx (i asembler i C dla Blackfin, później.) Nie ujawniasz, czego używasz, więc każda odpowiedź będzie bardziej domysłem i opinią niż cokolwiek innego. Ale procesory DSP AD są cholernie dobre i pisarzom kompilatorów C bardzo trudno jest właściwie wypełnić rurkę, że tak powiem. Mam dwie dekady doświadczenia w tej dziedzinie (w tym bardzo skromne doświadczenie w pisaniu kompilatora C) i do czasu, kiedy przestałem pisać kod (kilka lat temu), kompilatory C nie mogły zbliżyć się do ręcznego kodowania. Ale to, co robisz, zależy od twoich celów.
jonk
1
@jonk nadzieję masz zamiar napisać odpowiedź na ten jeden - ja tylko kiedykolwiek jeden Hardcore projektu DSP Blackfin, ale mam dobre wspomnienia z niektórych hacki wydajności to potrzebne :)
pericynthion
6
@pericynthion Nie, nie wyobrażam sobie pisania odpowiedzi, chyba że OP mówi dużo więcej o konkretnym DSP i celach projektu. W przeciwnym razie byłyby to niejasne, niekierowane opinie, które mogłyby być bardzo dobre lub bardzo błędne, w zależności od tego, co OP napisał na ten temat. Więc po prostu poczekam.
jonk
1
Jeśli chcesz, aby działał jak najszybciej, ręcznie zoptymalizuj go w asemblerze. To jest kompromis czasu \ pieniędzy. Jeśli wiesz, jak napisać dobre C, możesz uzyskać większość drogi.
Skok napięcia
2
Nie jestem pewien co do DSP, ale w przypadku większości mikroprocesorów można używać funkcji wewnętrznych, które znajdują się w połowie drogi między pisaniem asemblera i kodu C.
Maciej Piechotka,

Odpowiedzi:

20

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.

Dmitrij Grigoriew
źródło
23

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:

  1. Napisz większość swojego programu w C i tylko najbardziej krytyczną część liczbową w asemblerze.
  2. Napisz program w C i korzystaj z bibliotek dostarczonych przez producenta lub osoby trzecie - jeśli wykonujesz typowe zadania DSP, takie jak FFT, filtry FIR / IIR itp., Ktoś prawdopodobnie napisał już ręcznie dostrojony kod maszynowy, aby to zrobić, więc może z niego skorzystać (być może trzeba będzie za to zapłacić) i połączyć go z aplikacją.
pericynthion
źródło
Zazwyczaj dostawcy DSP dostarczają kod źródłowy dla typowych funkcji. Jeśli ich kod jest „wystarczająco dobry”, możesz wprowadzić go od razu. Jeśli nie jest w porządku, musisz go poprawić. Kilka lat temu musiałem zrobić warstwę FFT, aby uzyskać prawdziwy FFT tylko z częstotliwością. Istnieje sztuczka, która pozwala wykonać rzeczywistą FFT 2-punktową jako FFT złożoną z N-punktów, ale następnie należy wykonać końcowe przejście nad złożonym wyjściem w celu odzyskania danych częstotliwości rzeczywistej. Urządzenia analogowe nie miały tego konkretnego przypadku w przykładowym kodzie.
John R. Strohm,
21

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ą.

Moje pytanie brzmi: czy po prostu programuję w C, czy kompilator (który również pochodzi od firmy produkującej układy DSP) nie zoptymalizuje go dla tego DSP i nie wykorzysta jego możliwoś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.

Czy też naprawdę muszę pisać procedury DSP bezpośrednio w asemblerze?

Najpierw napisz w C, następnie profil, a następnie zdecyduj, czy chcesz pisać w asemblerze. Mamy nadzieję, że nie będziesz potrzebował zgromadzenia.

Nick Alexeev
źródło
20
Ogólnie rzecz biorąc, jest to z pewnością dobra rada, ale DSP jest trochę inna - jeśli OP naprawdę chce efektywnie wykorzystać DSP, prawdopodobnie będzie musiał gdzieś napisać jakiś odręczny kod. W rzeczywistości przy projektach DSP czasami nawet chcesz zacząć od napisania tego jądra numerycznego, aby sprawdzić, czy procesor będzie odpowiedni do danego zadania.
pericynthion
11
Twoje końcowe oświadczenie jest dobrą ogólną radą. Ale to trochę blado, biorąc pod uwagę szczegółowe szczegóły ALU AD DSP. Nie sądzę, żebyś je kiedykolwiek zbadał.
jonk
18

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.

Neil_UK
źródło
7

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.

juhist
źródło
Mam pytanie na ten temat: pracuję na procesorach STM32F4 Cortex-M4 i korzystam z bibliotek CMSIS / Cube. Korzystam również z flagi -O3 kompilatora, ponieważ okazało się, że jest wydajniejsze niż cokolwiek, co mogłem wyprodukować. Problem polega na tym, że skompilowany zestaw jest zawsze zbyt chaotyczny dla właściwej analizy. Czy zawsze kompilujesz bez optymalizacji kompilatora? A może udaje ci się zrozumieć wigilię zgromadzeń, jeśli jest wszędzie?
Florent,
2
@FlorentEcochard: Jeśli asembler kompilatora nie może być zrozumiany przez programistę, prawdopodobnie jest on lepszy niż asembler, który programista może napisać. Jako bezpośrednia odpowiedź na twoje pytanie: użyj maksymalnej optymalizacji i ręcznej analizy asemblera, trudne części mogą być pouczające.
pasaba por aqui
4

Zasadniczo pisanie źródeł asemblera nie jest konieczne, jeśli:

  • optymalizujesz C w sekcjach krytycznych: dobre użycie słowa kluczowego „register”, funkcje wbudowane, ...
  • mogą to być niektóre funkcje programu C wykorzystujące bloki asm

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.

pasaba por aqui
źródło
Praktycznie wszystkie współczesne kompilatory ignorują słowo kluczowe „register”, niezależnie od platformy. Używanie go jest bardzo mało prawdopodobne, aby poprawić kod.
Kef Schecter
@KefSchecter: nie tylko biorą pod uwagę wskazówkę dotyczącą rejestru, ale obecnie pozwalają nawet wybrać rejestr, który będzie używany: gcc.gnu.org/onlinedocs/gcc-6.1.0/gcc/…
pasaba por aqui
1
@ KefSchecter: z wyjątkiem kompilatorów napisanych dla urządzeń osadzonych, gdzie jest to bardzo ważne słowo kluczowe, jeśli programujesz na goły metal.
vsz
@pasabaporaqui: Zapomniałem o tej części składni. Ale jeśli nie podasz nazwy rejestru - innymi słowy, jeśli użyjesz go w standardowy sposób ISO - założę się, że GCC go zignoruje.
Kef Schecter
3

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.

juhist
źródło
2

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.

Ian Bland
źródło