Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Store Values In Non-Contiguous Memory Locations With SSE Intrinsics?

I'm very new to SSE and have optimized a section of code using intrinsics. I'm pleased with the operation itself, but I'm looking for a better way to write the result. The results end up in three _m128i variables.

What I'm trying to do is store specific bytes from the result values to non-contiguous memory locations. I'm currently doing this:

__m128i values0,values1,values2;

/*Do stuff and store the results in values0, values1, and values2*/

y[0]        = (BYTE)_mm_extract_epi16(values0,0);
cb[2]=cb[3] = (BYTE)_mm_extract_epi16(values0,2);
y[3]        = (BYTE)_mm_extract_epi16(values0,4);
cr[4]=cr[5] = (BYTE)_mm_extract_epi16(values0,6);

cb[0]=cb[1] = (BYTE)_mm_extract_epi16(values1,0);
y[1]        = (BYTE)_mm_extract_epi16(values1,2);
cr[2]=cr[3] = (BYTE)_mm_extract_epi16(values1,4);
y[4]        = (BYTE)_mm_extract_epi16(values1,6);

cr[0]=cr[1] = (BYTE)_mm_extract_epi16(values2,0);
y[2]        = (BYTE)_mm_extract_epi16(values2,2);
cb[4]=cb[5] = (BYTE)_mm_extract_epi16(values2,4);
y[5]        = (BYTE)_mm_extract_epi16(values2,6);

Where y, cb, and cr are byte (unsigned char) arrays. This seems wrong to me for reasons I can't define. Does anyone have any suggestions for a better way?

Thanks!

like image 236
Scott Avatar asked Dec 16 '22 20:12

Scott


2 Answers

You basically can't -- SSE doesn't have a scatter store, and it's sort of all designed around the idea of doing vectorized work on contiguous data streams. Really, most of the work involved in making something SIMD is rearranging your data so that it is contiguous and vectorizable. So the best thing to do is rearrange your data structures so that you can write to them 16 bytes at a time. Don't forget that you can reorder the components inside your SIMD vector before you commit them to memory.

Failing that, the PEXTRW op (_mm_extract_epi16 intrinsic) is pretty much the only way to pull a short from an SSE register and store into an integer register. The other approach available to you is to use the unpack and shuffle ops (_mm_shuffle_ps etc) to rotate data into the low word of the register and then MOVSS/_mm_store_ss() to store that low word to memory one at a time.

You will probably find that using a union, or moving data between the SSE and general purpose registers, will provide very poor performance due to a subtle CPU implementation detail called a load-hit-store stall. Basically, there's no direct way to move data between the register types; the processor has to first write the SSE data to memory, and then read it back again into the GPR. In many cases, this means it has to stall the load operation and wait until the store clears before any further instructions can be run.

like image 94
Crashworks Avatar answered Dec 28 '22 23:12

Crashworks


I don't know about SSE specifically, but generally the whole point of vectorised units is that they can operate very fast provided the data obeys particular alignment and formatting. So it's up to you to provide and extract the data in the correct format and alignment.

like image 24
Oliver Charlesworth Avatar answered Dec 29 '22 00:12

Oliver Charlesworth