View Full Version : speed up conversion raw RGB-> DIB-RGB32
hanfrunz
25th May 2003, 00:05
Hello,
i'd like to optimize a routine of my importuncompressedfile-filter.
The new version will be able to import uncompressed-picture-sequences. In most fileformats like tiff, pixar or quicktime-movies the RGB-data is stored like this:
line1 R1,G1,B1; R2,G2,B2...
line2 R1,G1,B1; R2,G2,B2...
...
but avisynth uses this "strange" DIB-format which stores the picture upsidedown and the byte order is GBR...
Here the routine i use to do the conversion from RGB 24bit to avisynth's RGB32(DIB).
start=src_width*src_height; // start=last byte of source
file.read(source,src_width*src_height); // get whole pic at once
for (h=0; h < dst_height;h++) {
w2=0;
for (w = 0; w < src_width; w+=3) {
dummy = w+start;
*(dstp+w2) = source[dummy+2];
*(dstp+w2+1)= source[dummy+1];
*(dstp+w2+2)= source[dummy];
*(dstp+w2+3)= 0;
w2+=4;
}
dstp += dst_pitch;
start -= src_width;
}
I think this could be done much faster in assembler , but the last time i coded in asm was on a C=64 a few ;) years ago.
Any tipps and tricks for me?
thanks,
hanfrunz
[BTW is it possible to use tabs or spaces in this forum?]
Use
[ c o d e] and [/ c o d e]
(without the spaces)
regards
Simon
trbarry
25th May 2003, 03:57
You really need a GBR0 byte order?
This might get you started. The most inner loop could just be something like:
__asm
{
cli // clear direction flag
sub ax, ax // zero ax
mov esi, src? // load the start of the line somehow
mov edi, dstp // start of output line
mov ecx, count // need src_width / 3 here, pixel ct
align 8
pixelloop:
lodsb // get R (of RGB) into reg al, bump esi+1
movsw // move GB from *esi to *edi, bump ptrs+2
stosw // store R0 from reg ax, bump edi+2
loop pixelloop
}
But I have not compiled or tested this, so be prepared for debug. ;)
- Tom
hanfrunz
25th May 2003, 20:00
thanks siswalters,
thanks trberry,
@trberry: i will try your asm code in a few minutes. Yes rgb32 needs to be [edit]ABGR, if not i would be very irritated.
I did some cleanup in my code myself:
start=src_width*src_height; // start=last byte of source
file.seekg(offset1,ios::beg); // seek to start of picture n
file.read(source,src_width*src_height); // get whole pic at once
for (h=0; h < dst_height;h++) {
for (w = 0; w < width*4; w+=4) {
dstp[w] = source[start+2];
dstp[w+1]= source[start+1];
dstp[w+2]= source[start];
start+=3;
}
dstp += dst_pitch;
start -= src_width_dbl;
}
Whats the best way to check if changes make an improvement? I always use vdubmod and render a few hundret frames. There should be a better way?
hanfrunz
trbarry
25th May 2003, 20:15
Whats the best way to check if changes make an improvement? I always use vdubmod and render a few hundret frames. There should be a better way?
For casual timings I just choose Fast Recompress in vdub and then preview a grey screen for a minute or 2, then check the relative estimated times for the total job. But there's probably better ways.
- Tom
hanfrunz
25th May 2003, 23:23
@trberry: you were right i was debugging..., you code generates grb not gbr, so i tried myself and came to this code, but the compiled c++ is twice as fast... :(
__asm {
cld
mov esi, linestart // linestartp->edi
mov edi, dstp // dstp->edi
mov ecx, swidth // width -> ecx this the w counter
align 8
pixelloop:
lodsd // load dblword=4byte from esi into eax
dec esi // go back 1 byte, because we have only 3 byte of interest
// these four lines swap rgb -> agbr
ror ax, 8
ror eax, 16
ror ax, 8
ror eax, 8
stosd // copy eax->edi, buffer to dst
dec ecx // decr. counter
jnz pixelloop // if counter>0 do next pixel
}
hanfrunz
trbarry
26th May 2003, 00:20
@trberry: you were right i was debugging..., you code generates grb not gbr
That is very strange and I don't understand why.
- Tom
Prettz
26th May 2003, 19:38
Originally posted by trbarry
You really need a GBR0 byte order?
This might get you started. The most inner loop could just be something like:
__asm
{
cli // clear direction flag
sub ax, ax // zero ax
mov esi, src? // load the start of the line somehow
mov edi, dstp // start of output line
mov ecx, count // need src_width / 3 here, pixel ct
align 8
pixelloop:
lodsb // get R (of RGB) into reg al, bump esi+1
movsw // move GB from *esi to *edi, bump ptrs+2
stosw // store R0 from reg ax, bump edi+2
loop pixelloop
}
But I have not compiled or tested this, so be prepared for debug. ;)
- Tom
just a quick question, why are you using the 'loop' instruction?
edit: also, I haven't thought enough about it probably, but have you considered using the 'bswap' instruction? It seems from documentation that it's still decently fast on Athlons and P4s.
sh0dan
26th May 2003, 19:58
Edit: Just saw your source is RGB24.
Integer SSE - converts RGB24 -> BGR32. Alpha is garbage. Requires mod 4 width (non-mod4 should work, but there will be out-of buffer reads/writes).
int swidth = (vi.width+3)/4;
for (int i=0;i<vi.height;i++) {
__asm {
mov esi, linestart // linestartp->edi
mov edi, dstp // dstp->edi
mov ecx, swidth // width -> ecx this the w counter
pxor mm7,mm7 // Clear mm7
align 16 // 16 is faster than 8
pixelloop:
movd mm0,[esi] // move 4 bytes to mm0
movd mm1,[esi+4] // move next 4 to mm1
movd mm2,[esi+8] // move next 4 to mm2
punpcklbw mm0,mm7 // Unpack to words |00r2|00b1|00g1|00r1|
punpcklbw mm1,mm7 // Unpack to words |00g3|00r3|00b2|00g2|
punpcklbw mm2,mm7 // Unpack to words |00b4|00g4|00r4|00b3|
movq mm5,mm1
psllq mm1,32 // mm1: |00b2|00g2|0000|0000|
psrlq mm5,32 // mm5: |0000|0000|00g3|00r3|
pshufw mm3,mm0,11000110b // mm3: |00r2|00r1|00g1|00b1| // Ready!
pshufw mm4,mm2,00011011b // mm4: |00b3|00r4|00g4|00b4| // Ready!
psrlq mm0,48 // mm0: |0000|0000|0000|00r2|
pshufw mm1,mm1,00001011b // mm1: 0000|0000|00g2|00b2|
psllq mm2,48 // mm2: 00b3|0000|0000|0000|
pshufw mm5,mm5,11000111b // mm5: 0000|00r3|00g3|0000|
psllq mm0,32 // mm0: 0000|00r2|0000|0000|
pshufw mm2,mm2,00000011b // mm2: 0000|0000|0000|00b3|
por mm0,mm1 // mm0: 0000|00r2|00g2|00b2| // Ready!
por mm2,mm5 // mm2: 0000|00r3|00g3|00b3| // Ready!
packuswb mm3,mm0
packuswb mm2,mm4
movq [edi],mm3
movq [edi+8],mm2
add esi,12 // Increment Source
add edi,16 // Increment Dest
dec ecx // decr. counter
jnz pixelloop // if counter>0 do next pixel
}
linestart+=src->GetPitch();
dstp += dst->GetPitch();
}
hanfrunz
27th May 2003, 18:47
Hi sh0dan,
i like your code, but the compiled c++ is 10fps faster... (30:20)
It seems, that the ms-compiler is pretty good...
hanfrunz
sh0dan
27th May 2003, 18:55
Is this code shared with other C++ code?
MSVC has a tendency to disable optimizations completely once inline assembler is used. Try moving the code to a separate funciton. There is still a (slight) possibility that the code is actaully slower.
hanfrunz
27th May 2003, 19:16
hello sh0dan,
now its 22fps vs 30fps fps... so a little speed increase.
Why must rgb32 in this weired DIB format? :(
Will the Intelcompiler be even faster?
hanfrunz
sh0dan
27th May 2003, 19:34
It's how bitmaps are handled by Windows - it is how BMP's are stored and AVIs also decompress to this format.
Is the source aligned? I Assume you write to a PVideoFrame so that one should be aligned properly.
If the code above is slower than C, then the C-functions are very fast already, and not being a bottleneck. It was still a nice execise in pshufw ;)
You could still skip the write to the alpha channel - since no alpha channel is read there is no point in writing to it.
trbarry
28th May 2003, 01:59
edit: also, I haven't thought enough about it probably, but have you considered using the 'bswap' instruction? It seems from documentation that it's still decently fast on Athlons and P4s.
Prettz -
If it is really BGRX output desired then that might indeed be simplest and best.
And what's wrong with the loop instruction?
- Tom
sh0dan
28th May 2003, 09:02
AMD optimization guide:
Avoid the Loop Instruction
The LOOP instruction in the AMD Athlon™ processor requires
eight cycles to execute. Use the preferred code shown below:
Example 1 (Avoid):
LOOP LABEL
Example 1 (Preferred):
DEC ECX
JNZ LABEL
trbarry
29th May 2003, 16:43
"Avoid the loop instruction"
I tend to optimize for my own machines, which usually means P4 these days. But AMD has been supporting the loop instruction since the dawn of time and you would have to think they would have fixed (optimzed) that by now.
Has anyone tested it recently?
- Tom
sh0dan
29th May 2003, 16:57
AMD (just as Intel) primarily optimizes instructions often used by compilers. I haven't seen a "loop" instruction generated by MSVC yet (even though there might be cases), so it's probably one of those instructions that are supported - but not recommended. P4 probably also have a bunch of emulated (Vectorpath) instructions, even though I haven't been able to find a table with each instruction and it's latency.
Anyway - there is no point in using loop - it'll probably also be faster to use dec/jnz on P4 - makes me want the days, when one instruction=one cycle. Superscalar execution and multipipeline processors are funny - but not always logical. ;)
trbarry
29th May 2003, 17:07
hanfrunz -
If you are still playing with this, here another for your testing. It just swaps the byte order.
It assumes you want to input RGB,RGB,... and output BGRx,BGRx, ...
It also assumes you can legally access 1 byte before the beginning of the line, fix if this isn't true.
__asm
{
mov esi, src? // load the start of the line somehow
mov edi, dstp // start of output line
mov ecx, count // need src_width / 3 here, pixel ct
align 8
pixelloop:
mov eax, dword ptr[esi-1] // get xRGB
// xor al, al // if you want to clear alpha
bswap eax // reverse byte order
mov dword ptr[edi], eax // store result BGRX
lea esi, [esi+3] // bump source ptr
lea edi, [edi+4] // bump dest ptr
// dec ecx // if you don't want loop instr
// jnz pixelloop // if you don't want loop instr
loop pixelloop // to try loop instr speed
}
hanfrunz
29th May 2003, 17:46
Hello trbarry,
i like your code, it's so very simple, but it's as "slow" as sh0dans sse2 code. :(
I'd like to disassemble this function, but in vc7 i had to disable the optimization for debuging. How can i do it?
thanks,
hanfrunz
sh0dan
29th May 2003, 18:40
You can try inserting __asm{int 3} before the loop - this should bring up the disassembly. A similar function gives:
037E5C43 mov dl,byte ptr [ecx+1]
037E5C46 mov byte ptr [eax-2],dl
037E5C49 mov dl,byte ptr [ecx]
037E5C4B mov byte ptr [eax-1],dl
037E5C4E mov dl,byte ptr [ecx+2]
037E5C51 mov byte ptr [eax],dl
037E5C53 mov byte ptr [eax+1],bl
037E5C56 mov edx,dword ptr [edi]
037E5C68 inc esi
037E5C69 add ecx,3
037E5C6C add eax,4
037E5C6F cmp esi,edx
This code is able to execute in three cycles, assuming there is no cache-miss. ecx = source, eax = dest, "bl" contains the alpha value. esi is row counter.
The reason is is so fast is probably because the instructions can be executed in parallel.
bswap is 1 cycle - however it stalls all the following instructions as they depend on the result from it. "lea esi, [esi+3]" might be relocated and executed at the same time as the code above it, but edi also stalls.
trbarry
30th May 2003, 20:18
Sh0dan -
You always seem to know. Do you have an up to date list of instruction timings, and which instructions are vectorized?
- Tom
sh0dan
30th May 2003, 22:56
The last chapter in the AMD optimization Guide (http://www.amd.com/us-en/Processors/DevelopWithAMD/0,,30_2252_739_1144,00.html) lists all instructions and their latencies. I haven't been able to find a corresponding table for P4. I do however expect the latencies to be roughly the same. The guide is great, and has been my bible - many of the things are also true for P4.
The main differences (beside SSE2) as I understand:
P4 has 2 pipelines, K7 3 pipelines - this is mostly outweighed by the higher frequency of the P4.
Dependecy stalls have slightly higher impact on K7 because it relies on using all pipes - however the dynamic instruction reordering often takes care of this (expect in very small loops).
Branch mispredictions have larger impact on P4. However it should always be avoided - even if it cost a few cycles.
P4 has smaller L1 cache (8kb as opposed to 64k data/64k instruction cache). Important in loop unrolling.
So there are no major differences - optimizations for P4 usually also speed op K7 and vice versa.
trbarry
31st May 2003, 01:33
Well, I've gotten bitten by a couple differences recently. :(
We saw how the vectorized bsf instruction slows down things and someone just reminded me about the loop instruction above.
I noticed I could cut the number of instructions in Nics MPEG2DEC3 SimpleIDCT setup about in half using a bunch of unrolled xchg instructions. However they have an implied lock and I was wondering about the performance effects. Any idea?
In any event it looks like I may not be coding anything this weekend. XP on my dev machine will not even boot in safe mode. :( :( :(
- Tom
Guest
31st May 2003, 05:06
Originally posted by trbarry
XP on my dev machine will not even boot in safe mode. Just restore from your most recent backup. Shouldn't take more than 10-15 minutes. :)
You do have a boot diskette, don't you?
trbarry
31st May 2003, 05:40
Just restore from your most recent backup. Shouldn't take more than 10-15 minutes.
Assuming it's not a hardware problem I'll probably do that. But my most recent backup is a ghost from Win/Me. I never did get XP running solid.
- Tom
Maybe this link can be useful
http://www.intel.com/design/pentium4/manuals/24896608.pdf
In app C (page 419) there is table with Latency and anothers informations
Arda
trbarry
31st May 2003, 14:51
ARDA -
10x. I'd previously read that but forgotten it had the tables.
But sadly it had another little blurb that said to avoid using xchg on memory locations, so I guess my idea is not a very good one.
BTW, that was a funny PDF document. It would not let me select or copy the comment to quote it here.
- Tom
sh0dan
31st May 2003, 18:54
Thanks for the link ARDA!
Most things about memory access, branch prediction, out-of-order execution is in fact the same as K7.
There are a few surprises there - mostly related to pairing:
MMX multiply: P4 - 8 cycles, K7 - 3 cycles.
MMX multiply now takes 4 times as many cycles as other MMX commands - actually quite a big problem when having to pair them. Therefore there should ideally now be at least 4 instructions that doesn't mulitply between two multiply instructions.
MMX shifter restrictions:
It seems like there are still issues with one shifter on the P4 - this is (alongside the MMX multiplier) one of the biggest problems. I can see that pshufw is actually done in the MMX shifter - in contrast to the K7, where this restriction has been lifted.
This means that on P4, none of these instructions are pairable: Pack, unpack, shift, shuffle. This is a problem, since it will stall the processor, if any of these two instructions follow eachother - even if they are not depending on eachother. This is however too often the case. :(
In case I'm being too crypitic above: None of the instructions marked with MMX_SHFT can be paired with eachother, just as two instructions with FP_MUL cannot be paired with eachother. Therefore to have efficient code, the second pipe should have code that doesn't use the same part of the CPU - and doesn't depend on the result from the other pipe. The ISSE code above is a perfect example of this - almost none of the instructions pair on P4. :(
I will see if I can find the time to update the InstructionPairing guide (http://www.avisynth.org/index.php?page=InstructionPairing) on avisynth.org.
(This is in no way any attempt at bashing P4 in any way - please don't look at the text above that way - this is just news to me - so we can have the fastest code on all platforms!)
Prettz
1st June 2003, 23:09
Originally posted by sh0dan
(This is in no way any attempt at bashing P4 in any way - please don't look at the text above that way - this is just news to me - so we can have the fastest code on all platforms!)
Don't worry about that, it's true that pretty much every x86 instruction (and also especially any multiplication and division instructions) is slower on a P4 than on a P1 or P2, with the only exceptions being add/sub/and/or/xor. I understand that Intel is trying to push the internal operations further toward RISC, but I think they definitely overdid it. Even including any bit-shifting in a complex address apparently now causes speed penalties on the P4.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.