让我们从这开始:
我有一个16字节的内存块,我只需要将偶数字节复制到8字节的内存块。
我目前的算法是做这样的事情:
unsigned int source_size = 16, destination_size = 8, i;
unsigned char * source = new unsigned char[source_size];
unsigned char * destination = new unsigned char[destination_size];
// fill source
for( i = 0; i < source_size; ++i)
{
source[i] = 0xf + i;
}
// source :
// 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e
// copy
for( i = 0; i < destination_size; ++i)
{
destination[i] = source[i * 2];
}
// destination :
// 0f 11 13 15 17 19 1b 1d
这只是一个例子,因为我想知道当我需要获取每个第3个字节或每个第4个字节时,是否有更好的方法来执行此操作,而不仅仅是字节。
我知道使用循环我可以实现这个但我需要优化这个...我不知道如何使用SSE所以我不知道是否可以在这种情况下使用,但像memcpy magic这样的东西有点事情会很棒。
我还考虑过使用宏来摆脱循环,因为源和目标的大小都是不变的,但这看起来不是什么大问题。
如果我说这是为了提取YUYV像素格式的YCbCr字节,也许你可以开箱即用。另外我需要强调的是,我这样做是为了摆脱libswscale。
答案 0 :(得分:2)
虽然我怀疑编译器和cpu在这种情况下已经做得很好;如果你真的想要替代方案,请研究扭转莫顿数的技巧。这个问题How to de-interleave bits (UnMortonizing?)显示了如何在位上进行,但是这个想法也可以扩展到字节。
类似的东西(仅限示例,这不是生产质量)
// assuming destination is already zero...
For (int i=0; i < destination_size; i += 2) {
long* pS = (long*) &source[ i * 2 ];
long* pD = (long*) &destination[ i ];
long a = *pS &0xff00ff00;
*pD |= *pS | ( *pS << 8 );
}
这比你的版本还要快,取决于确切的cpu类型和编译器生成的内容。即测试并查看哪个更快,正如其他人所提到的,内存获取瓶颈会使给定的小数组的所有内容都蒙上阴影。
答案 1 :(得分:2)
使用 SSSE3 :
可以有效解决此问题#include <tmmintrin.h> //SSSE3 and before
...
//source must be 16-byte aligned
unsigned char * source = (unsigned char *)_mm_malloc(source_size, 16);
//destination must be 8-byte aligned (that's natural anyway)
unsigned char * destination = (unsigned char *)_mm_malloc(destination_size, 8);
...
__m128i mask = _mm_set_epi8( //shuffling control mask (constant)
-1, -1, -1, -1, -1, -1, -1, -1, 14, 12, 10, 8, 6, 4, 2, 0
);
__m128i reg = *(const __m128i*)source; //load 16-bit register
__m128i comp = _mm_shuffle_epi8(reg, mask); //do the bytes compaction
_mm_storel_epi64((__m128i*)destination, comp); //store lower 64 bits
转换在生成的程序集(MSVC2013)中看起来像这样:
movdqa xmm0, XMMWORD PTR [rsi]
pshufb xmm0, XMMWORD PTR __xmm@ffffffffffffffff0e0c0a0806040200
movq QWORD PTR [rax], xmm0
这种方法应该非常快,特别是当你做很多这样的转换时。它只需要一个shuffling指令(不计算加载/存储),它似乎有1 clock latency and 0.5 clocks throughput。请注意,此方法也可用于其他字节模式。
答案 2 :(得分:1)
不幸的是,您不能仅使用memcpy()
技巧执行此操作。现代处理器具有64位寄存器,是存储器传输的最佳尺寸。现代编译器总是尝试优化memcpy()
调用,一次执行64位(或32位或甚至128位)位传输。
但在你的情况下,你需要'奇怪的'24或16位传输。这正是我们为什么要有SSE,NEON和其他处理器扩展的原因。这就是为什么它们被广泛用于视频处理。
因此,在您的情况下,您应该使用SSE优化的库之一或编写自己的汇编代码来执行此内存传输。