Как скопировать байты из памяти, используя шаблон (YUYV упакован в план YUV420)

Давайте начнем с этого:

У меня есть блок памяти в 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 вроде бы быть великим

Я также подумал об использовании макроса, чтобы избавиться от цикла, так как размер источника и назначения оба постоянны, но это не выглядит большой проблемой.

Может быть, вы можете подумать «из коробки», если я скажу, что это для извлечения байтов YCbCr формата пикселей YUYV. Также я должен подчеркнуть, что я делаю это, чтобы избавиться от libswscale.

0

Решение

К сожалению, вы не можете сделать это с memcpy() только хитрости Современные процессоры имеют 64-битные регистры, и это оптимальный размер для передачи памяти. Современные компиляторы всегда стараются оптимизировать memcpy() вызовы для выполнения 64- (или 32- или даже 128-битной) передачи одновременно.

Но в вашем случае вам нужны «странные» 24 или 16-битные передачи. Именно поэтому у нас есть SSE, NEON и другие расширения процессора. И именно поэтому они широко используются в обработке видео.

Так что в вашем случае вы должны использовать одну из оптимизированных SSE библиотек или написать свой собственный ассемблерный код, который будет выполнять эту передачу памяти.

1

Другие решения

Хотя я подозреваю, что компилятор и процессор уже отлично справятся с этой задачей; если вы действительно хотите альтернативы, посмотрите на методы для изменения числа смертных. Этот вопрос Как удалить чередование битов (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 );
}

Будет ли это быстрее, чем ваша версия или нет, зависит от типа процессора и от того, что генерируют компиляторы. т. е. тестируйте и смотрите, что быстрее, как упоминалось другими, узкое место выборки памяти затмит все для небольшого заданного массива.

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

Этот метод должен быть довольно быстрым, особенно когда вы делаете много таких преобразований. Это стоит только одна инструкция перетасовки (не считая загрузки / хранения), которая, кажется, имеет 1 тактовая задержка и пропускная способность 0,5 такта. Обратите внимание, что этот подход можно использовать и для других шаблонов байтов.

2
По вопросам рекламы [email protected]