View Full Version : MxN DCTFilter using MMX asm optimization (developmnent stage)
redfordxx
3rd December 2007, 18:22
Hi,
is somewhere a code for DCT+IDCT 16x16 for MS VC 6.0 to use? I'd like to use it in my avisynth filter...
If it worked for values>255 or float (the former option is better), would be even better.
Second thing, somewhere a code to load quant matrix from a file must exist (or something similar).
Can someone tell me?
Thanx
R*
Fizick
3rd December 2007, 20:52
I tried to find integer 16 dct (MMX)without success.
but fftw library has float dct (any size)
IanB
3rd December 2007, 22:18
Dig very deap into DGDecode (Mpeg2Source) source.
Also XviD source.
Also troll the AMD site.
redfordxx
4th December 2007, 03:06
Dig very deap into DGDecode (Mpeg2Source) source.
Also XviD source.
Wow I thought MPEG2 or XviD work only with 8x8 DCT
IanB
4th December 2007, 04:08
It's not that great a stretch to double the number of points.
You could probably take the MMX code and re-do it in SSE to go from 8 to 16 elements per cycle.
redfordxx
4th December 2007, 09:10
You could probably take the MMX code and re-do it in SSE to go from 8 to 16 elements per cycle.Well, last time I saw the code I remember this part was asm.
Being real beginner in C, I am walking around anything in asm in big distance;-)
IanB
4th December 2007, 13:45
Well if you don't want it fast, then just use the fftw library, it will do 2**n by 2**m, just set n=m=4.
There is also the reference code in DGDecode it should be very easy to increase that to 16x16. If you replace the trig functions it uses with very fast approximations it can be made to walk at a passable speed. Have a look at the library at neuron2.net, I think there is a copy of the paper about it.
redfordxx
5th December 2007, 10:28
Well if you don't want it fast, then just use the fftw library.What's the difference between fast and slow? Rough estimation, I have no clue. 2x, 5x, 10x...?
IanB
5th December 2007, 13:01
At this point a 16x16 DCT is vapourware, a custom SSE2 routine could be fastest, but by how much, there is no way to know. The point is pretty mote, you have admitted you are unwilling to rework the current assembler to do 16x16. So go with what you are confortable with. If the end result is not fast enough, you have fall back positions.
The fftw library is pretty fast for a general purpose fft. Really good implementations are of order N*log(N) so I would expect a top notch 16x16 to be about 5.6 times slower than an 8x8
Fizick
5th December 2007, 19:03
redfordxx,
I was need in forward DCT for blocksize 16x16 (ond other sizes beside 8x8) in MVTools (in special dct mode). And I use fftw currently. You may compare speed yourself :) as well as get example code (under GPL).
IanB,
reworking 8x8 mmx to 16x16 code is not so easy IMHO :)
I considered it but even do not know how to begin.
There are several impementations with various tricks for speed and accuracy (rounding).
BTW, if somebody know 4x4 forward dct (MMX) it would be useful for MVTools
redfordxx
7th December 2007, 11:19
Hi guys,
I started to dig into other sources, I think, there will be success...but some things I just cannot get.
For example, this is my global.h shortened, #included in other files:
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
#include <windows.h>
extern "C" void *_aligned_malloc(size_t size, size_t alignement);
extern "C" void _aligned_free(void *aligned);
#endif
// Ex-DctFilter
// define forward and backward dct functions and pointers, actual code version from Xvid project
#ifndef _FDCT_H_
#define _FDCT_H_
extern "C" {
typedef void (fdctFunc)(short * const block);
typedef fdctFunc* fdctFuncPtr;
extern "C" fdctFuncPtr fdct;
fdctFunc fdct_mmx;
fdctFunc fdct_altivec;
fdctFunc fdct_sse2;
//this line is a problem
fdctFuncPtr pfdct;
#endif
#ifndef _IDCT_H_
#define _IDCT_H_
typedef void (idctFunc)(short * const block);
typedef idctFunc* idctFuncPtr;
extern "C" idctFuncPtr idct;
idctFunc idct_mmx;
idctFunc idct_xmm; // sse verson
idctFunc idct_sse2;
idctFunc idct_altivec;
#define DO_ADJUST(pwa) __asm {
.......blah
}
}
#endif
Some thing I don't understand, for example, how is possible structure like this:#ifndef _FDCT_H_
#define _FDCT_H_
extern "C" {
...
#endif
#ifndef _IDCT_H_
#define _IDCT_H_
....
}
#endifI mean #ifndef #endif mixed with {}. But I can live with that, as long as they work.
But what does not work is
//this line is a problem
fdctFuncPtr pfdct;In DCTFilter, pfdct is defined inside each class. I wanted to make the variable global. But when I build, I got the error, that _pfdct was already defined in other objects. I thought to prevent this, #ifndef is there. All other variables and types are fine. Where is the problem?
gioowe
7th December 2007, 21:53
You cannot declare a global variable in a header file. Header files are just include files that are added into .c files. So if 2 .c files include this header then 2 .c files declare the same variable name and the linker cannot combine both together as it doesn't know which one is right.
You have to declare the global variable in one file and declare an external reference in the header.
extern fdctFuncPtr pfdct;
And in one and only one .c file you add
fdctFuncPtr pfdct;
Why do you need an 16x16 IDCT? By the way: You cannot use the optimized (AAN) 8x8 IDCT of Xvid and upscale it to 16x16. It is optimized and will result in a wrong transform. So simply execute the following equation
http://www.mathworks.com/access/helpdesk/help/toolbox/vipblks/ref/eqn1100535176.gif
from: http://www.mathworks.com/access/helpdesk/help/toolbox/vipblks/index.html?/access/helpdesk/help/toolbox/vipblks/ref/2didct.html
redfordxx
8th December 2007, 00:37
So simply execute the following equation
Hmm, the reason was that when I use FFTW I thought it would be faster than when I code it in C.
Now, I know the FFTW is in float.
As far as I can see, I will need two types of DCT:
BYTE[0,255]==>FDCT==>here_something_to_do==>IDCT==>short==>something==>BYTE[0,255]
or
WORD[0,65535]==>FDCT==>here_something_to_do==>IDCT==>int==>something==>BYTE[0,255]
So, when I do all calculations scaled to integer and precalculate the coefficients, is it worth trying? Or the fftw will be faster even if it is real,float or whatever.
If I am at least roughly correct, using __int32 for calculation will allow me for example scaling coeffs [-1,1]->[-2048,2048] for DCT of 16x16 [0,65535] values. Considering the result will end in BYTE...
About the precalculation: it will be (M*N)^2 coeficients and during the process there will be appropriate number of multiplications and additions both forward and backward and finally shifts for downscaling...
so for 16x16....65536
Just naive question: Is there any faster way to multiply lot of numbers in some array in memory than just going through with for-cycles?
gioowe
8th December 2007, 01:35
Well, you have to do the FDCT in YUV colorspace first. Then these values are normally in range 16..241 (give or take).
If you don't use SIMD instructions then it'll be best to perform integer scaling to 32bit, preferably signed. But consider overflows so don't scale up to high. 2^12 is a good approach. And then you use constants to get rid of all trigonometric functions. On a 32-bit machine 32-bit multiplications are as fast as 16-bit multiplications (indeed they are faster due to skipped 16-bit alignment/scaling issues).
Another important point is loop unrolling. Conditional jumps are nightmares on current processors. If incorrectly predicted they cost up to 42 cycles where you could have done the same amount of add/mul operations. So don't 'if' in loops and perform loop-unrolling. Like
for (int i = 0 ; i < 16 ; ++i)
{
x[0] = y[0] * 2;
x[1] = y[1] * 2;
x[2] = y[2] * 2;
x[3] = y[3] * 2;
} instead of for (int i = 0 ; i < 16 ; ++i)
{
for (int j = 0 ; j < 4 ; ++j) x[j] = y[j] * 2;
}
akupenguin
8th December 2007, 01:37
SSE2 and C optimized int16 and float 4x4, 8x8, and 16x16 DCTS for x86_64: http://akuvian.org/src/dcts-r65.tar.gz
It shouldn't be hard to port it to x86_32, but I haven't done so as I don't have any 32bit computers and only coded it for my own research purposes.
gioowe
8th December 2007, 01:40
Just naive question: Is there any faster way to multiply lot of numbers in some array in memory than just going through with for-cycles?
Yes, SIMD instructions as found in SSE1,2,3,4a. But for that you need to code in assembler. Current compilers are not "intelligent" enough to analyze code and generate efficient SSE instructions. Incorrectly used they can easily decrease performance. With SIMD instructions you can perform multiple operations (like add, mul) at the same time at the cost of having to shuffle/pack data round. Operating values is the easy part, getting your data to fit using that operations is the really hard one.
gioowe
8th December 2007, 01:45
SSE2 and C optimized int16 and float 4x4, 8x8, and 16x16 DCTS for x86_64: http://akuvian.org/src/dcts-r65.tar.gz
It shouldn't be hard to port it to x86_32, but I haven't done so as I don't have any 32bit computers and only coded it for my own research purposes.
It actually might be. First it requires nasm and second it uses 16 xmm registers. 32-bit CPUs only have 8.
akupenguin
8th December 2007, 01:49
Sure, porting requires reallocating registers. That's what I meant by "porting" as opposed to just recompiling.
redfordxx
8th December 2007, 03:32
Well, you have to do the FDCT in YUV colorspace first. Then these values are normally in range 16..241 (give or take).I want to make kinda weighted quantization. I multiply weight clip and original DCT-quantize-IDCT and divide with quantized weightclip only. I am not perfectly sure of the result but I hope;-)
the multiplication of the clips is the reason of [0,65535]
If you don't use SIMD instructions then it'll be best to perform integer scaling to 32bit, preferably signed. But consider overflows so don't scale up to high. 2^12 is a good approach. And then you use constants to get rid of all trigonometric functions.
On a 32-bit machine 32-bit multiplications are as fast as 16-bit multiplications (indeed they are faster due to skipped 16-bit alignment/scaling issues).
Another important point is loop unrolling. Conditional jumps are nightmares on current processors. If incorrectly predicted they cost up to 42 cycles where you could have done the same amount of add/mul operations. So don't 'if' in loops and perform loop-unrolling. Like
for (int i = 0 ; i < 16 ; ++i)
{
x[0] = y[0] * 2;
x[1] = y[1] * 2;
x[2] = y[2] * 2;
x[3] = y[3] * 2;
} instead of for (int i = 0 ; i < 16 ; ++i)
{
for (int j = 0 ; j < 4 ; ++j) x[j] = y[j] * 2;
}
Does it make big difference constants vs. precalculated variables? Because when N,M come as parameters for the filter, only then I can calculate the trigs...
Same for cycles...I can unroll only if I know M,N...if I understood your point.
But maybe I can unroll it like for main part and then I do the rest:
MN=M*N;
cycles=MN>>5;
cyclesMN=cycles<<5;
for (int i = 0 ; i < cyclesMN ; i+=32)
{
x[0+i] = y[0+i] * 2;
x[1+i] = y[1+i] * 2;
x[2+i] = y[2+i] * 2;
...
x[31+i] = y[31+i] * 2;
}
for (int i = cyclesMN ; i < MN ; ++i)
{
x[i] = y[i] * 2;
}
or maybe
for (int i = 0 ; i < cyclesMN)
{
x[i++] = y[i] * 2;
x[i++] = y[i] * 2;
x[i++] = y[i] * 2;
...
x[i++] = y[i] * 2;//32
}
instead the unrolled part. I just don't know how big nightmare is it and how much care of it.
BTW: is the ++ on correct place?
gioowe
8th December 2007, 05:26
Well, you know M and N. They are 16. Otherwise your first version is the better one.
++i is pre-increment
i++ is post-increment
i = 5
a = ++i means a = 6, i = 6
a = i++ means a = 5, i = 6
Don't do x[i++]. This is another addition. (Actually it should be x[i] = y[i++] * 2). Simple table lookup addition is without cost, it is handled by the memory address generator, so do x[i], x[i+1], x[i+2], ..., x[i+31], x += 32. This is only ONE extra addition.
gioowe
8th December 2007, 05:28
/* cycles unused */
cyclesMN = MN & ~31;
;)
Fizick
8th December 2007, 14:56
So, when I do all calculations scaled to integer and precalculate the coefficients, is it worth trying? Or the fftw will be faster even if it is real,float or whatever.
Probably fftw will be faster if you do not use SIMD (SSE,MMX) in your code.
(but use special fast int to float routine instead microsoft, see MVTools again)
redfordxx
8th December 2007, 20:17
Well, you know M and N. They are 16. You know, when I do it, then for all N generally, if the the speed does not increase significantly because of not using constants;-)
redfordxx
9th December 2007, 01:04
Hmm...starting to have something working... slow of course.. I will apply the advices above now, but..
there is surely some tool in VS6 to find out which lines cost me most of the time...can you give direction?
redfordxx
9th December 2007, 01:13
Yeah, one more thing, when I choose optimize for speed in the project release settings, that's all I can do in this case or there are some other switches I should change?
Fizick
9th December 2007, 10:07
VC6 professional (or may be enterprise ?) has profiler tool. (I never use it).
Other profilers are VTune from Intel and CodeAnalyst (AMDCodeAnalyst) from AMD (I used it sometimes).
I can say that it is hard work, you neet big experience for hand optimize, and you do not get many without MMX. :)
http://avisynth.org/mediawiki/Filter_SDK/Assembler_optimizing
redfordxx
9th December 2007, 13:29
http://avisynth.org/mediawiki/Filter_SDK/Assembler_optimizing
Thanks
so I tried for fun co convert inner part of my loops to asm:start1=(int)pTrigs+ij*size2d*4;
start2=(int)pWorkInput;
_asm {
push eax //sum
push ebx //multi
push ecx //adr trig
push edx //adr work
push esi //counter
mov esi, _size2d
mov ecx, start1
mov edx, start2
mov eax, 0
add esi, -4
align 16
LoopToop:
mov ebx, dword ptr [ecx+esi]
imul ebx, dword ptr [edx+esi]
add eax, ebx
add esi, -4
jns LoopToop
mov temp,eax //result
pop esi
pop edx
pop ecx
pop ebx
pop eax
}
I did it just by learning from reading other sources... Hm, it's slower than compiled C at the moment. I assume it's because compliler cannot optimize my asm together with C code. But will improve I hope after incorporating outer loops.
It is just to multiply two int number arrays of size _size2d in memory locations start1 and start2.
Now questions:
- I heard about MMX but afaik there are multiple byte or word operations but I need signed dword operations. Is also available?
- Is there any way to at least roughly spot area in the translated code where is my particular command located...so I can see how it was translated?
- I cannot find how to enable viewing MMX registers in VC6...I see only the normal ones...Advice?
- Where can I read fast which registers have which purpose, so I don't mess something by using wrong reqister.
- I should use push, pop instructions on all registers I use in each _asm block?
- Any recommendation for good online asm language reference? With and index or search tool, so when I want to know what exactly does certain instruction I find it there..
Fizick
9th December 2007, 15:27
1. try use words instead of doublewords
2. listing
3. do not know, probably no way in VC6
4. probably free use any registers eax ebx ecx edx
5 no, compiler do it itself for registers above (almost always, ask IanB for ebx bug ;)) but not esi, edi
I may be wrong (remember who know...) ;)
6 you may got nasm assembler manual. It contains reference for all assembler instructions.
http://nasm.sourceforge.net/
BTW, recenly nasm 2.0 was released with x64 support.
But Appendix B (x86 Assembler instructions reference) was removed in v.2.0.
Download older v0.99.02 doc
Thera are many other articles at inet
redfordxx
9th December 2007, 17:05
1) when I have DCT from [0,65535] I don't think I can scale to words...
3) last year before I reinstalled the comp it was there, and now not (I think so at least)
IanB
9th December 2007, 22:41
@redfordxx,
You are on a hiding to nothing with the ASM above. Modern processors have parallel execution i.e. a core can do 2 things at once if the dependancies in the thread allow it. i.e. add eax, edx can happen at the same time as add ebx, edx but not add eax, ecx because it has to wait for eax to get the answer from the first add. Your code above stalls like this on every instruction.
Also you have lot of entry and exit code that will get recalled many times by the outer loop. And you scan memory backwards, this will destroy any L2 cache prefetch the CPU may attempt.
Modern compilers are pretty smart. Express your algorithm as cleanly as you can, compile it and ask for an assembly listing. Look at the generated code for constructs you can see cause dumb code to be emitted. Express that part of your algorithm differently, see if the compiler does a better job. Loop!
Most dumb code is a result of too many variables active at once, a cleaner re-expression of your algorithm usually can work wonders.
The code above would have been like this int *a=pTrigs+ij*size2d*4;
int *b=pWorkInput;
int sum = 0;
for (int i=0; i<size2d; i++) sum += a[i] * b[i];I doubt you will be able to write better code than the compiler for this example particularly if there is an outer loop to also consider.
Also when using MMX there are compromises. Data is mostly either 8 or 16 bits. There are exception like PMADD which does L32=a16*e16+b16*f16 | R32=c16*g16+d16*h16
Just get your plugin working first, then worry about getting it faster. Once you have working code others can review it and offer suggestions.
redfordxx
9th December 2007, 22:52
OK...I am doing it with words. I "discovered" it is possible when learned more instructions;-)
Now I stuck in following situation:
At the end of the cycle I have in one mm registry 2 signed doublewords. I need to add them to each other, scale down by let's say 10 bits and put the result into short variable/address.
Any advice?
IanB
9th December 2007, 23:10
punpckldq mm1, mm0
paddd mm0, mm1
psrlq mm0, 10+32
movd dword ptr[x], mm0
redfordxx
9th December 2007, 23:12
ask for an assembly listing.
That was my question before already...how?...the only thing it know is to debug assembly window and when it is compiled for release, I don't know how to find the area of the code desired, because it lists whole memory or what.
There are exception like PMADD which does L32=a16*e16+b16*f16 | R32=c16*g16+d16*h16
Hour before I discovered PMADD...thats why I have 2 doublewords...;-)
And you scan memory backwards
Yeah...jns was the surest thing for me;-)
redfordxx
9th December 2007, 23:23
thanks
psrlq mm0, 10+32
and what happens to the sign bit?
Fizick
10th December 2007, 00:38
redfordxx,
As I wrote above, try find "Listing Files" options in VC in listbox on C/C++ tab of project settings ;)
squid_80
10th December 2007, 03:28
and what happens to the sign bit?
If they've been scaled properly the sign won't change.
IanB
10th December 2007, 05:01
Unfortunatly the is no psraq only psra[bwd] variants, so you need to adust my sample code if you need to do signed.
You were warned, MMX has compromises.
Also the sample I gave suffers badly from V pipe stalling, normally I would find other instructions to interleave into the code stream to keep both pipes busy. Well written MMX uses both processing pipes to the max. Poor MMX has every step dependant on the previous and runs at 50% potential speed.
redfordxx
10th December 2007, 10:01
If they've been scaled properly the sign won't change.Can you be more specific?
redfordxx
10th December 2007, 10:30
Also the sample I gave suffers badly from V pipe stalling, normally I would find other instructions to interleave into the code stream to keep both pipes busy. Well written MMX uses both processing pipes to the max. Poor MMX has every step dependant on the previous and runs at 50% potential speed.
Ok so taking care of this (http://avisynth.org/mediawiki/Filter_SDK/Instruction_pairing), right? I will rearange data in memory, so I can do better;-)
This above is the most important, right, but how is it about speed of memory access?
Is
movq mm0,mm1
faster than
movq mm0,[eax]
faster than
movq mm0,[eax+8]
faster than
movq mm0,[eax+ebx]
faster than
movq mm0,[eax+ebx+8]
faster than
add eax,ebx
movq mm0,[eax]?
How many cycles or delay or whatever?
Similarily, is
mov ecx,edx
faster than
push ecx
or
pop ecx
faster than
mov ecx,[eax]
faster than
mov ecx,[eax+4]
faster than
mov ecx,[eax+ebx]
faster than
add eax,ebx
mov ecx,[eax]?
How many cycles or delay or whatever?
IIRC, I saw somewhere, pmadd takes 3 cycles and paddd takes 1?
redfordxx
10th December 2007, 10:43
And you scan memory backwards, this will destroy any L2 cache prefetch the CPU may attempt.I listed the assembly (thanx, Fyzick). For scanning the memory, there are two counters...one increasing(add 4) to scan the memory and one decreasing(dec) to check the end of cycle...so this is the best way for me,I assume...(except I do add 8 or 16, depending on your above post answer...maybe some combination with lea will improve something...?)
Leak
10th December 2007, 10:55
I'd also recommend having a look at the PDFs found on Agner Fog's page (http://www.agner.org/optimize/) about optimizing assembly.
sh0dan
10th December 2007, 12:20
This above is the most important, right, but how is it about speed of memory access?
Actual cycle count varies from processor to processor, just as the pairing capabilities of different processors vary.
To answer your question:
* Moves between registers (of same type) are faster than reads from memory (even cached data).
* There is no penalty for complex address references. [edi*2+4] is same speed as [edi].
AMD's Optimization Guide lists cycle counts, and should give you a general idea of how instructions compare. There are small differences to Intel systems - mainly faster SSE2, but code that is faster on one system is generally faster on both.
90+% of all systems today have SSE2, so I usually do an SSE2 version, and a fall back to C code for the rest.
But feel free to ask.
redfordxx
10th December 2007, 13:30
In the most inner loop I will use only movq, paddd, pmadd. can I pair pmadd in V-pipe with one or more instructions (movq,paddd) in U-pipe?
squid_80
10th December 2007, 15:07
Can you be more specific?
It's a bit hard to explain in text on a forum. But in this case you've got a 16-bit value, scaled up by 10 bits = 26 bits total. So as long as your previous calculations haven't produced a result that has exceeded 25 bits you're not going to lose the most significant bit (hence the sign).
redfordxx
10th December 2007, 15:34
Thanks --- the Agner Fog's and AMD docs I will read in the evening...
Here is my current idea of the most inner loop in my code. What do you think about the pairing?
Assume that the algo and instructions are correct...but there can be change in the order of instructions, addressing method and loop control/decision. (*) For example is there gain in using the AddressCounter somehow also for loop control?
//version pairing ... in one loop 6memory reads and one AddressCounter increase...means minimum 7pairs...+loop control decision at the end
mov eax, AddressSrc
mov ebx, AddressTrigs
mov ecx, NumberOfLoops
mov edx, 0 //AddressCounter
//----- intended pair delimiter
movq mm0, [eax] //U pipe (load 2+2 values0 src)
pxor mm6, mm6 //v pipe (sum0=0)
//-----
movq mm1, [ebx] //U pipe (load 2+2 values00 trig)
pxor mm7, mm7 //v pipe (sum1=0)
LoopLabel:
//-----
movq mm2, [ebx+edx*2+8] //U pipe (load 2+2 values01 trig)
pmaddwd mm1, mm0 //V pipe (multiply src0*trig00 for 1+1 sum0)
//-----
movq mm3, [ebx+edx*2+16] //U pipe (load 2+2 values10 trig)
pmaddwd mm2, mm0 //V pipe (multiply src0*trig01 for 1+1 sum1)
//-----
movq mm0, [eax+edx+8] //U pipe (load 2+2 values1 src)
pmaddwd mm3, mm0 //V pipe (multiply src1*trig10 for 1+1 sum0)
//-----
movq mm4, [ebx+edx*2+24] //U pipe (load 2+2 values11 trig)
paddd mm7, mm2 //V pipe (add to 1+1 sum1)
//-------
add edx, 16 //Increase AddressCounter
paddd mm6, mm1 //U pipe (add to 1+1 sum0)
pmaddwd mm4, mm0 //V pipe (multiply src1*trig11 for 1+1 sum1)
//-------
movq mm0, [eax+edx] //U pipe (load 2+2 values0 src)
paddd mm6, mm3 //V pipe (add to 1+1 sum0)
//-----
movq mm1, [ebx+edx*2] //U pipe (load 2+2 values00 trig)
paddd mm7, mm4 //V pipe (add to 1+1 sum1)
//-----
dec ecx
jnz LoopLabel
//here some downscaling mm6 and mm7 and packing to mm7 to be decided later
mov edx, AddressDst
//-----
movq [edx], mm7 //U pipe (saving all 4 values)
This does 4 cycles from previous page calculating values from 2 loops on last page per one loop...on rearranged data...
It will have one useless pair at the end of last loop (prereading the data to mm0 and mm1 for next loop which won't happen.
[EDIT] (*) I already see...negative memory offset and one increasing counter for addressing and loop control;-)
One worry: If between add,sub,dec,inc and jnz are few mmx instructions, will jnz still work? Like in example:
//here is edx negative
add edx, 16 //Increase AddressCounter
paddd mm6, mm1 //U pipe (add to 1+1 sum0)
pmaddwd mm4, mm0 //V pipe (multiply src1*trig11 for 1+1 sum1)
//-------
movq mm0, [eax+edx] //U pipe (load 2+2 values0 src)
paddd mm6, mm3 //V pipe (add to 1+1 sum0)
//-----
movq mm1, [ebx+edx*2] //U pipe (load 2+2 values00 trig)
paddd mm7, mm4 //V pipe (add to 1+1 sum1)
//-----
jnz LoopLabel
IanB
10th December 2007, 21:20
If between add,sub,dec,inc and jnz are few mmx instructions, will jnz still work? Yes, MMS/SSE intructions generally do not effect the flags. Sprinkling the loop control code in between awkward stall point is a good technique.
redfordxx
10th December 2007, 21:45
It's a bit hard to explain in text on a forum. But in this case you've got a 16-bit value, scaled up by 10 bits = 26 bits total.No...I have 4 normally signed doubleword in two registers. So I see two options:
- add something then shift then subtract something else
- arit shift and then "and" with mask
redfordxx
11th December 2007, 03:27
Wooow....12fps (pure C++) --> 100 fps (inner loop MMX)
(approx)
...and some more to come
redfordxx
11th December 2007, 03:44
How did I solve the downscaling:
Somewhere in the beginning:
unsigned __int64 i128SignMask = 0x8000000080000000;
_asm {
movq mm0, i128SignMask
movq mm1, mm0
psrad mm0, scale
pxor mm1,mm0
movq i128SignMask, mm1
}
And the shifting/downscaling during the processing://here some downscaling mm6 and mm7 and packing to mm7
//-----
movq mm5, i128SignMask
psrad mm6, scale
//-----
mov edx, CurrentDst
pxor mm6, mm5
psrad mm7, scale
//-----
pxor mm6, mm5
//-----
packssdw mm7, mm6
//-----
movq [edx], mm7 //U pipe (saving all 4 values)
redfordxx
11th December 2007, 20:34
* Moves between registers (of same type) are faster than reads from memory (even cached data).So, what is faster?
movq mm0, [eax]
movq mm1, [eax]
ormovq mm1, [eax]
punpcklbw mm0, mm1
pxor mm0, mm0
punpckubw mm1, mm0
don’t worry about pairing, I have other instructions to interleave.
AMD's Optimization Guide lists cycle counts, and should give you a general idea of how instructions compare. Well, there is written latency... I didn't understand the x-y formula...but can I take it as "some unit of time?"
90+% of all systems today have SSE2, so I usually do an SSE2 version, and a fall back to C code for the rest.AthlonXP here;-)
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.