在C++中复制跨步数据

Jac*_*ern 10 c++ stride

我有两个数组,我希望将一个数组复制到另一个数组中.例如,我有

A A A A A A A A ...

B B B B B B B B ...
Run Code Online (Sandbox Code Playgroud)

我想每三个元素复制B到A获得

B A A B A A B A ...
Run Code Online (Sandbox Code Playgroud)

从帖子" 是否有一个标准的,跨步的memcpy版本? ",似乎在C中没有这种可能性.

但是,我经历过,在某些情况下,memcpy比基于for循环的副本更快.

我的问题是; 有没有办法在C++中有效地执行跨步内存复制,至少执行标准for循环?

非常感谢你.

编辑 - 澄清问题

为了使问题更清晰,让我们表示手头上的两个数组a和b.我有一个执行独特的跟随for循环的函数

for (int i=0; i<NumElements, i++)
    a_[i] = b_[i];
Run Code Online (Sandbox Code Playgroud)

例如,两者[]都是重载运算符(我正在使用表达式模板技术),以便它们实际上是有意义的

 a[3*i]=b[i];
Run Code Online (Sandbox Code Playgroud)

Ayb*_*gür 9

可能是一个太具体的答案,但在支持NEON的ARM平台上,NEON矢量化可以用来更快地进行跨步复制.在资源相对较为有限的环境中,这可能是挽救生命的,这可能就是为什么ARM首先在该环境中使用的原因.一个突出的例子是Android,其中大多数设备仍然使用支持NEON的ARM v7a架构.

以下示例演示了这一点,它是将YUV420sp图像的半平面UV平面复制到YUV420p图像的平面UV平面的循环.源缓冲区和目标缓冲区的大小都是640*480/2字节.所有示例都是使用Android NDK r9d中的g ++ 4.8编译的.它们在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];
    }
}
Run Code Online (Sandbox Code Playgroud)

-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;
    } 
}
Run Code Online (Sandbox Code Playgroud)

-O3仅编译,平均需要大约1.15毫秒.根据其他答案,这可能与常规架构上的速度一样快.

3级:常规+ GCC自动NEON矢量化

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

编译-O3 -mfpu=neon -ftree-vectorize -ftree-vectorizer-verbose=1 -mfloat-abi=softfp,平均需要大约0.6毫秒.作为参考,memcpy的640*480字节数,或者怎么在这里测试量的两倍,平均需要约0.6毫秒.

作为旁注,使用上面的NEON参数编译的第二个代码(展开和指针)花费大约相同的时间量,0.6毫秒.


Tho*_*ews 7

有没有办法在C++中有效地执行跨步内存复制,至少作为循环的标准执行?

编辑2: C++库中没有跨步复制功能.

由于跨步复制并不像存储器复制那样流行,因此芯片制造商和语言设计都对跨步复制提供了专门的支持.

假设使用标准for循环,您可以通过使用循环展开获得一些性能.有些编译器可以选择展开循环; 它不是"标准"选项.

给定一个标准 for循环:

#define RESULT_SIZE 72
#define SIZE_A 48
#define SIZE_B 24

unsigned int A[SIZE_A];
unsigned int B[SIZE_B];
unsigned int result[RESULT_SIZE];

unsigned int index_a = 0;
unsigned int index_b = 0;
unsigned int index_result = 0;
for (index_result = 0; index_result < RESULT_SIZE;)
{
   result[index_result++] = B[index_b++];
   result[index_result++] = A[index_a++];
   result[index_result++] = A[index_a++]; 
}
Run Code Online (Sandbox Code Playgroud)

循环展开将重复"标准" for循环的内容:

for (index_result = 0; index_result < RESULT_SIZE;)
{
   result[index_result++] = B[index_b++];
   result[index_result++] = A[index_a++];
   result[index_result++] = A[index_a++]; 

   result[index_result++] = B[index_b++];
   result[index_result++] = A[index_a++];
   result[index_result++] = A[index_a++]; 
}
Run Code Online (Sandbox Code Playgroud)

在展开版本中,循环次数减少了一半.

与其他选项相比,性能提升可以忽略不计.以下问题会影响性能,每个问题可能会有不同的速度改进:

  • 处理数据缓存未命中
  • 重新加载指令流水线(取决于处理器)
  • 操作系统用磁盘交换内存
  • 其他任务同时运行
  • 并行处理(取决于处理器/平台)

并行处理的一个示例是让一个处理器将B项复制到新阵列,而另一个处理器将A项复制到新阵列.