0

Давайте начнем с этого:Как скопировать байты из памяти, используя шаблон (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-й байт, а не только четные байты.

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

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

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

+0

он ограничен полосой пропускания памяти в любом случае, зачем беспокоиться? – user3528438

ответ

1

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

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

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

2

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

ПОГОДА это быстрее, чем версия или нет, зависит от конкретного типа процессора и какие компиляторы генерируют. т. е. проверить и увидеть, что быстрее, как упоминалось в других узких местах памяти, будет затенять все, что требуется для малого массива.

+0

dude ... just, awesome ... thanks –

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 

Convertion выглядит в генерируемой сборки (MSVC2013):

movdqa xmm0, XMMWORD PTR [rsi] 
pshufb xmm0, XMMWORD PTR [email protected] 
movq QWORD PTR [rax], xmm0 

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

+0

Должен ли я делать это за каждые 16 байт? Спасибо, что поделились. –

+0

@ RafaelLucio: Да, для каждой 16 байтов ввода выполняется одна операция. – stgatilov