我有两个数组,我希望将一个数组复制到另一个数组中.例如,我有
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)
可能是一个太具体的答案,但在支持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毫秒.
有没有办法在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项复制到新阵列.