Может быть, это слишком конкретный ответ, но на платформе ARM, поддерживающей NEON, можно использовать векторизацию NEON, чтобы сделать пошаговое копирование еще быстрее. Это может спасти жизнь в среде, где ресурсы относительно более ограничены, и, вероятно, именно поэтому ARM в первую очередь используется в таких условиях. Ярким примером является Android, где большинство устройств по-прежнему используют архитектуру ARM v7a, поддерживающую NEON.
Следующие примеры демонстрируют это. Это цикл для копирования полуплоской UV-плоскости изображения YUV420sp в плоскую UV-плоскость изображения YUV420p. Размеры исходного и целевого буферов равны 640*480/2
байтам. Все примеры скомпилированы с помощью g++ 4.8 внутри Android NDK r9d. Они выполнены на процессоре Samsung Exynos Octa 5420:
Уровень 1: обычный
void convertUVsp2UVp(
unsigned char* __restrict srcptr,
unsigned char* __restrict dstptr,
int stride)
{
for(int i=0;i<stride;i++){
dstptr[i] = srcptr[i*2];
dstptr[i + stride] = srcptr[i*2 + 1];
}
}
Скомпилировано только с -O3
, занимает в среднем около 1,5 мс.
Уровень 2: развернуто и немного сжато с помощью движущихся указателей.
void convertUVsp2UVp(
unsigned char* __restrict srcptr,
unsigned char* __restrict dstptr,
int stride)
{
unsigned char* endptr = dstptr + stride;
while(dstptr<endptr){
*(dstptr + 0) = *(srcptr + 0);
*(dstptr + stride + 0) = *(srcptr + 1);
*(dstptr + 1) = *(srcptr + 2);
*(dstptr + stride + 1) = *(srcptr + 3);
*(dstptr + 2) = *(srcptr + 4);
*(dstptr + stride + 2) = *(srcptr + 5);
*(dstptr + 3) = *(srcptr + 6);
*(dstptr + stride + 3) = *(srcptr + 7);
*(dstptr + 4) = *(srcptr + 8);
*(dstptr + stride + 4) = *(srcptr + 9);
*(dstptr + 5) = *(srcptr + 10);
*(dstptr + stride + 5) = *(srcptr + 11);
*(dstptr + 6) = *(srcptr + 12);
*(dstptr + stride + 6) = *(srcptr + 13);
*(dstptr + 7) = *(srcptr + 14);
*(dstptr + stride + 7) = *(srcptr + 15);
srcptr+=16;
dstptr+=8;
}
}
Скомпилировано только с -O3
, занимает в среднем около 1,15 мс. Согласно другому ответу, это, вероятно, так же быстро, как и в обычной архитектуре.
Уровень 3: обычная + автоматическая векторизация NEON GCC.
void convertUVsp2UVp(
unsigned char* __restrict srcptr,
unsigned char* __restrict dstptr,
int stride)
{
for(int i=0;i<stride;i++){
dstptr[i] = srcptr[i*2];
dstptr[i + stride] = srcptr[i*2 + 1];
}
}
Скомпилировано с помощью -O3 -mfpu=neon -ftree-vectorize -ftree-vectorizer-verbose=1 -mfloat-abi=softfp
, занимает в среднем около 0,6 мс. Для справки, memcpy
из 640*480
байтов, или вдвое больше, чем протестировано здесь, занимает в среднем около 0,6 мс.
В качестве примечания: второй код (развернутый и с указателем), скомпилированный с указанными выше параметрами NEON, занимает примерно столько же времени, 0,6 мс.
person
Ayberk Özgür
schedule
07.10.2014
memcpy
иногда работает быстрее, чем циклfor
из-за оптимизации, которую он может выполнять, потому что память, с которой он работает, непрерывна. Эти оптимизации не могут быть сделаны здесь. - person Collin Dauphinee   schedule 13.06.2013cudaMemcpy2D
, который копирует с шагом? - person Vitality   schedule 14.06.2013cudaMemcpy2D
выполняется параллельно на графическом процессоре, установленном на устройстве, аmemcpy
выполняется на самом устройстве. - person Captain Obvlious   schedule 14.06.2013cudaMemcpy2D
. - person Vitality   schedule 14.06.2013