skvely clanek. Diky za nej. Alespon je videt, jak konkretne dokazou tyto instrukce zoptimalizovat program a kolik casu se tim usetri.
K tomu prevodu int16 -> float, ja bych na to vyuzil look up table. Mas tady pouze 12 bitu plus znamenkovy bit. To je 8k float hodnot. Kdyz k tomu pridame i ten bit, co je nevyznamny (a v tabulce ho budeme ignorovat), budeme mit 16k float hodnot. To neni tak moc. Jestli mas chut zkusit, jak by to vypadalo casove, pripadne jestli by se dal i tento program zoptimalizovat pomoci SSE, napis potom, jak ten pokus dopadl.
Nezkousel jsem to prelozit, doufam, ze tam nemam nejaky preklep
float lut[0x4000]; void prepare_lut() { for (int i=0; i<0x4000; i++) { int16_t nr=(int16_t)(((i & 0x2000)?0xf000:0)|(i & 0x0fff)); lut[i]=(float)nr; } } void convert() { float *op1=outbuf1; float *op2=outbuf2; int16_t *ip=inbuf; while (ip<inbuf+4*SAMPLES) { *op1++=lut[*ip++ & 0x3fff]; *op1++=lut[*ip++ & 0x3fff]; *op2++=lut[*ip++ & 0x3fff]; *op2++=lut[*ip++ & 0x3fff]; } }
Lookup table má dva problémy, způsobuje cache thrashing a nejde moc dobře vektorizovat.
Cache thrashing bývá u takových algoritmů s požadavky na vysokou propustnost docela problém, protože je nutné, aby bylo pokud možno všechno v L1 cache, jinak hodně rostou latence, když se čeká na paměť.
Pro konverze int na float existují SIMD instrukce s relativně nízkou latencí, např. _mm_cvtepi32_ps, což je obvykle lepší volba, než lookup table.
Hustý, implementoval jsem to (s tou optimalizací že tabulka stačí 4096 velká, protože metadata při lookupu vyandujeme) a je to skoro stejně rychlé jako SSE! (35 vs. 31us)
Tak se mi líbí jak jednoduše vyjadřuješ ty adresy pro destinaci, mě tohle nikdy nenapadne :)
Tabulka má jenom 16 KiB (4096 * sizeof(float)) takže s cache taky v pohodě.