C++ 在C+中复制跨步数据+;

C++ 在C+中复制跨步数据+;,c++,stride,C++,Stride,我有两个数组,我想把一个数组复制到另一个数组中。例如,我有 A A A A A A A A ... B B B B B B B B ... 我想将B的每三个元素复制到A以获得 B A A B A A B A ... 从“”一文来看,C中似乎没有这种可能性 然而,我经历过,在某些情况下,memcpy比基于循环的拷贝的更快 我的问题是,;有没有办法有效地执行C++中的内存拷贝,至少作为标准 < < /COD>循环>? 多谢各位 编辑-澄清问题 为了使问题更清楚,让我们用a和b来表示手头的两个数

我有两个数组,我想把一个数组复制到另一个数组中。例如,我有

A A A A A A A A ...

B B B B B B B B ...
我想将
B
的每三个元素复制到
A
以获得

B A A B A A B A ...
从“”一文来看,C中似乎没有这种可能性

然而,我经历过,在某些情况下,
memcpy
比基于循环的拷贝的
更快

我的问题是,;有没有办法有效地执行C++中的内存拷贝,至少作为标准<代码> < < /COD>循环>? 多谢各位

编辑-澄清问题

为了使问题更清楚,让我们用
a
b
来表示手头的两个数组。我有一个函数,它为
循环执行以下唯一的

for (int i=0; i<NumElements, i++)
    a_[i] = b_[i];
<> P>是否有任何方法有效地执行C++中的内存拷贝,至少作为循环的标准? <> > >编辑2:在C++库中没有跨复制的函数。

由于跨步复制不像内存复制那样流行,芯片制造商和语言设计都专门支持跨步复制

假设对
循环使用标准的
,则可以通过使用循环展开获得一些性能。有些编译器有展开循环的选项;这不是一个“标准”选项

循环提供标准的

#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++]; 
}
在展开版本中,循环的数量已减少一半

与其他选项相比,性能改进可能微不足道。 以下问题会影响性能,并且每个问题可能有不同的速度改进:

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

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

可能是一个过于具体的答案,但在支持NEON的ARM平台上,可以使用NEON矢量化来加快复制速度。在资源相对有限的环境中,这可能是拯救生命的方法,这可能就是为什么首先在该环境中使用ARM的原因。一个突出的例子是Android,其中大多数设备仍然使用支持NEON的ARMV7A架构

以下示例演示了这一点,它是一个将YUV420sp图像的半平面UV平面复制到YUV420p图像的平面UV平面的循环。源缓冲区和目标缓冲区的大小都是
640*480/2
字节。所有示例都是在androidndkr9d中使用g++4.8编译的。它们在三星Exynos Octa 5420处理器上执行:

一级:常规

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.15毫秒。根据另一个答案,这可能与在常规体系结构上的速度一样快

第3级:常规+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];
    }
}
void convertUVsp2UVp(
无符号字符*,
无符号字符*uu限制dstptr,
int(步幅)
{

对于(int i=0;iA standard for loop的执行速度至少与standard for loop一样快……讽刺的是,这取决于您使用的数据存储结构。对于阵列,我认为您不能比按模数递增的for loop做得更好。
memcpy
有时比
for
循环更快,因为它可以执行优化因为它运行的内存是连续的。这些优化不能在这里进行。@daupick但是为什么CUDA有
cudaMemcpy2D
用螺距复制?@JackOLantern:CUDA并行运行。@JackOLantern因为
cudaMemcpy2D
在安装在设备上的GPU上并行执行,而
memcpy
在上执行设备本身。谢谢你友好的回答。我已经编辑了我的帖子,以便更好地解释这个问题。你认为通过
#pragma unroll
我有机会改善这种情况吗?我不知道,因为关于副本的一切都是在运行时知道的。正如我所说,这取决于处理器。对于某些处理器,分支刷新指令离子管道和处理器必须重新加载。一些现代处理器有足够的指令管道缓存,它们在指令缓存中保留一个简单的for循环,而不必重新加载。大多数处理器更喜欢执行顺序指令,也不喜欢遇到分支指令。我的首选是不要使用编译器
pragma
但是可以展开循环。这允许更多的可移植代码,我可以控制内容的多样性。在我的工作中,我展开了一个循环,它处理32个插槽的FIFO,使用32个独立语句;工作非常快。我理解你的观点,
pragma
可以引入代码可移植性问题,但一般来说我不知道UCH关于< <代码> > <代码>循环,因此我不能手动执行循环展开。请您编辑您的帖子,提供一个简短的、明确的声明,没有“内置”C++功能来执行跨步副本,这样我就可以接受您的答案了吗?谢谢。
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];
    }
}
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;
    } 
}
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];
    }
}