Sziasztok!
Tud-e valaki veletlenul jo kis FIR algoritmust, ami akar meg gyors/hatekony is lehet? :) A win[] ablakfuggveny minden esetben lehet L + 1 = 2^N+1 elemszamu es szimmetrikus, ezt "er" kihasznalni. Azaz ahol L az mondjuk 32 ... 256 lenne a gyakorlatban mint ketto-hatvany, es win[n] = win[L-n] fennall. Nyilvan ha L az kisebb lenne mint az itteni ertekek (pl 4...8), akkor teljesen felesleges tultolni valami szofisztikalt eljarassal, ha pedig tul nagy, akkor mar ugyis inkabb konvolucionak hivnank mintsem FIR filteringnek.
Egy naiv implementacio az kb ilyen most:
typedef struct
{ int fir_length;
double *fir_buffer;
double *fir_window;
int fir_index;
} finite_impulse_response;
int fir_init(finite_impulse_response *fir,int len,double initial)
{
int i;
fir->fir_length=len;
fir->fir_buffer=(double *)malloc(sizeof(double)*len);
for ( i=0; i<len; i++ )
{ fir->fir_buffer[i]=initial;
}
fir->fir_window=(double *)malloc(sizeof(double)*(len+1));
fir->fir_window= ...; /* computation of FIR window filter goes here */
fir->fir_index=0;
return(0);
}
double fir_apply(finite_impulse_response *fir,double in)
{
double w;
int j,p;
w=0.0;
for ( j=0,p=fir->fir_index; j<fir->fir_length; j++ )
{ w += fir->fir_buffer[p] * fir->fir_window[j];
p=(p+1)%fir->fir_length;
}
w+=in*fir->fir_window[j];
fir->fir_buffer[fir->fir_index]=in;
fir->fir_index=(fir->fir_index+1)%fir->fir_length;
return(w);
}
int fir_buffer(finite_impulse_response *fir,float *input,int ninput)
{
int i;
for ( i=0; i<ninput ; i++ )
{ input[i]=fir_apply(fir,input[i]);
}
return(0);
}
Lenyeg itt a legutolso fir_buffer() hivas, ami siman a fir_apply() hivason keresztul lekonvolvalja es a `fir` objektumban szepen tarolja az allapotgepet hogy kifele elrejtse az egesz hobelevancot. Szoval a feladat az a fir_apply() minel hatekonyabb, O(fir->fir_length) muveletigenynel jobban skalazo valtozata lenne. Nyilvan egy O(log(fir->fir_length)) lenne a legjobb :) Extrem hataresetekben, pl boxcar filterignel ugye O(1) muvelettel es O(length) memory footprinttel megoldhato, szoval csakcsak van valami a ketto kozott, kihasznalva hogy a length az ketto-hatvany es/vagy hogy a fir_window[] az szimmetrikus meg ilyesmi...
thx, A.
Hozzászólások
Szabalyzohoz a reakcioido is szamit. Szoval jobban jarsz vele, ha n lepesben kapsz valaszt, mint ha n log n lepesben kapsz mondjuk n*n mintara valaszt. Ha erre hasznalnad, akkor a buta megoldas valoszinuleg jobb.
A konvolucio algoritmikusan jonak tunik, bar kerdes, mennyire vagy vele kisegitve. (FFT aztan inverzben ugyanez - egyutthatoidat legalabb csak 1x kell - Cooley-Tukey pl. jo a szamaidra - vagy ha fftw3 vagy valami masik letezo lib is jo, azzal is jobban jarsz)
A masik lehetoseg meg, hogy az algoritmikus optimalizalast nem noveled tovabb, az architekturahoz viszont minel inkabb hozzaigazitod. GPU-n ez azt hiszem pont jol szamolhato (ket vektor skalarszorzata), de CPU-n is vektormuveletekkel (SSE es tarsai) gyorsan fut. Mikrokontrolleren is ami szabalyzokhoz van, talan rendelkezik ilyen jellegu utasitassal. Persze ez attol is fugg, mennyi hianyzik meg (gondolom real-time kell mennie).
Ha meg utolagos jelfeldolgozasrol van szo, elso korben nekiesnek a scipy-ban levo szurokkel (plusz matplotlibbel), hogy kb. kepben legyek vele, mennyire kell szurni es milyen parameterekkel.
A strange game. The only winning move is not to play. How about a nice game of chess?
Igen, lenne ennek elegge valos ideju alkalmazasa is, de a stream az nagyon hosszu - szoval az boven belefer ha nem 1 lepes van n muvelettel kiszamolva hanem n lepes n*log(n) muveletben.
Igen, kozben ezen en is gondolkodtam. Hogy a fir_window[]-bol csinalok egy fir_window_matrix[]-ot, ami ugyanugy n*n-es, csak az egyes sorai ciklikusan el vannak tolva szepen, minden sora aligned_alloc()-olt es fir_buffer[]-t is aligned_alloc()-cal foglalom le... Na es akkor a critical section egy sima skalaris szorzat, amit FMA-ra optimalizalt SIMD-ekkel mar - remelhetoleg - jol fel lehet dolgozni. Szoval GPU az azert nem kene, annal legyen altalanosabb, de talan igy mar jo. Gyakorlatban pl nem lenne rossz ha kenyelmesen (50%/mag loaddal) elmenne mondjuk 2MSPS + n=64...128 ertekekkel.
Szerk: kozben megcsinaltam ezt a fir_window_matrix[]-os trukkot. Egyreszt igy tenyleg 7x gyorsabb lett, szepen felismerte a fordito a dolgot es kioptimalizalta SIMD-re. Az AVX2 is kicsit gyorsitott rajta, de az mar nem sokat (par %-ot az SSE4.2-hoz kepest). Erdekes viszont hogy az aligned_alloc() az nem segit(ett) tovabb rajta. Ugyanakkor persze az is igaz hogy ugyanugy O(n)-nel megy ez is, szoval nagyskalan ugyanugy skalazodik.
> Az AVX2 is kicsit gyorsitott rajta, de az mar nem sokat
a legtobb cpu az AVX-et alacsonyabb orajelen tudja, emiatt amit nyersz az utasitaskeszleten elbukod az orajelen :(
> Erdekes viszont hogy az aligned_alloc() az nem segit(ett) tovabb rajta
vszinu a malloc() alapbol is aligned (valamennyire), meg a cache is besegit, ha nincs vegtelen sok adatod
> minden sora aligned_alloc()-olt
talan jobb lenne az egesz matrixot egyben allocolni, nem soronkent, meg neha megdobbentoen sokat segit ha elforgatod 90 fokkal (tehat matrix[y][x] helyett matrix[x][y] ), foleg ha az a cel hogy a matrix sorait parhuzamositsd, mert akkor 1 read be tudja olvasni 8-16 sor tartalmat a simd regiszterbe
meg ha nincs valoban szukseged a double precisionre, akkro erdemes megnezni float-al, a simd inkabb arra van optimalizalva
Amikor megneztem anno ezt a videot, elegge meggyozott az eredmeny.
A strange game. The only winning move is not to play. How about a nice game of chess?
Aha, ezzel nincs gond:
Kiszedi az egyik sorat. oszt hajra. A ciklusmag az egy valodi skalarszorzas. De igen, az egybe-allokacion en is gondolkodtam, vsz az segithet egy kicsit a memoria-overhead dolgokon (avagy: az indirekcio vagy az aritmetika a gyorsabb-e a gyakorlatban).
Mi a hardware, amin szamolsz? Sima PC?
Mit mersz? Ha mondjuk egy 16 bites ADC meri amit kell, biztos kell double? Esetleg int/fixpontos szamolas jatszik? Azzal is kiserleteznek, mennyit szamit.
A strange game. The only winning move is not to play. How about a nice game of chess?
Sima mezei dzsunka-pc/laptopcsy, semmi extra hardver nincs.
Aha, kozben mar atirtam az egeszet float-ra, itt tenyleg semmi szukseg nincs double-ra. Igen, a fixpontos szamolason is gondolkodtam de sok esetben amire ezt hasznalnam, eleg valtozatos a dinamika ahhoz hogy vsz nem feltetlenul jarunk jol azzal...
Eloszor azt hittem, valami speci mikrokontrollere architektura, amire nem talaltal letezo implemetaciot.
Ha sima PC, nem jo egyik mar letezo project sem? Amikor nekem kellett, siman behuztam a pythonos Butterworth filtert, es hasznaltam. Pedig eleg sok adatom volt. Multiprocessinggel at tudtam adni a tobbi magnak, visszafele meg mar csak a feldolgozott/felismert adat jott. Valoszinuleg jobbat irtak, mint amit en tudnek.
A strange game. The only winning move is not to play. How about a nice game of chess?
Letezo projekt jo lehet, de ha standalone C library (opcionalisan C++, de C az a preferred). Egyreszt mert ez egy viszonylag komplexebb pipeline-nak a resze lenne, masreszt mert elegge azt kell feldolgozni majd a vegefele ami a csovon kifer - ld: 2MSPS, de egeszen komplex ~20MSPS-ig rajta van a listan a dolog meg ilyesmi. A filterek is elegge projekt-fuggoek (leginkabb low-pass, az oke, de elegge tuningolt [pl menetkozben valtoztathato parameterekkel kene rendelkeznie], illetve van gaussi is meg minden egyeb nyalanksag menetkozben). Es nehol komplex, nehol valos az adatsor. Szoval netto 5-10k LOC-nal mar nem igazan refaktoralnam a tobbi reszt. Foleg hogy inkabb hobbi-projekt :)
Utánanéztél a szakirodalomban, hogy hogyan lehet a naiv FIR implementációt gyorsítani?
Ez még csak nem is szakirodalom, csak egy blogposzt: https://thewolfsound.com/fir-filter-with-simd/
https://github.com/jpcima/fast-filters
Ha van hozzáféréses IEEExplore-hoz, vagy Springerhez, akkor rengeteg publikációt találhatsz, ha megnézed a jelfeldolgozási szakirodalmat (és alternatív forrás lehet a SciHub, ha van DOI linked a cikkre).