每 n 个字节复制内存
Copy memory every n bytes
我有一个 uint8_t 值数组,我的目标是将每 3 个字节复制到一个 dst 数组,但要注意的是我在 dst 数组中从 4 个字节迭代到 4 个字节,如图所示下面。
src = {1,2,3,4,5,6};
dst = {0,0,0,0,0,0,0,0};
...
dst = {1,2,3,0,4,5,6,0}
现在我正在使用以下代码来执行此任务。
for(int i=0; i<arr_size ; i++)
memcpy(dst + i*4, arr_ptr + i*3, 3);
是否有 faster/efficient 方法来做到这一点?
编辑更多上下文:
我有以下结构需要用图像数组中的数据填充,其中 a
将始终初始化为 0.
typedef struct {unsigned char r,g,b,a} uchar4;
...
// init dst
...
*dst = (uchar4 *)malloc(height * width * sizeof(uchar4));
通过为 uchar4 数组赋值 struct.variable = value
,需要花费大量时间,这让我认为从存储 [=33 的图像数组中复制值会更快=] 值,到 uchar4 数组,因为 uchar 和 uint8 在内存中占用 1 个字节。这样,structs 数组用 0 初始化,并且将扁平化图像中的每 3 个字节每 4 个字节粘贴到 uchar arr 中。
Edit2: 代码更正
您根本不需要 memcpy
。仅使用指针运算,您可以执行以下操作:
uint8_t *src = some_values;
uint8_t *end = src + some_values_size;
uint8_t *dst = some_buffer;
for (; src < end; src += 3, dst += 4) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
对于上面的示例,您可以将代码定义为宏并将其用于不同的数据类型。 memcpy
想知道它要复制多少字节,因此你需要一个类型。
注意:代码假设数组长度src
是3的倍数,数组长度dst
等于: (length(src) / 3) * 4
.
我假设arr_size
是要复制的三胞胎的数量。
for(size_t i=0; i<arr_size ; i++)
memcpy(dst + i*3, src + i*4, 3);
这是错误的
for(size_t i=0; i<arr_size ; i++)
memcpy(dst + i*4, src + i*3, 3);
现在是上下文。
typedef struct {unsigned char r,g,b,a} uchar4;
不保证编译器不会添加任何填充。任何指针双关语都可能无法正常工作。添加静态断言以检查结构的大小为 4,如果不使用,则需要使用一些编译器扩展来打包结构。
效率:
很难判断,但是此处答案中带有代码的微不足道的功能表明 memcpy
版本很可能是最有效的。
我曾尝试删除一个内存访问并写得很糟糕(它调用 UBs!一般来说,但它适用于 X86 和 Cortex-M3 及更新版本)。这样做只是出于好奇:(警告!!图形编程内容!!!不适合所有观众)https://godbolt.org/z/Pefc6T
有很多方法可以尝试和优化您的转化循环。正如 0___________ 所建议的那样,您应该考虑 memcpy
大小的块,因为大多数优化器将为目标平台生成非常有效的代码,击败手工编码的天真替代方案。
这是比较 3 种方法的快速基准:
- 显式复制 3 个字节,递增指针。
- 为此副本使用
memcpy
可以添加其他方法,例如尝试利用 SIMD 指令,这应该以牺牲可移植性为代价提供显着的性能改进。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct rgb {
uint8_t r, g, b;
} rgb;
typedef struct rgba {
uint8_t r, g, b, a;
} rgba;
void copy3to4_simple(void *to, const void *from, size_t count) {
const uint8_t *src = from;
uint8_t *dst = to;
uint8_t *end = dst + count * 4;
while (dst < end) {
dst[0] = src[0];
dst[1] = src[1];
dst[3] = src[2];
dst += 4;
src += 3;
}
}
void copy3to4_memcpy(void *to, const void *from, size_t count) {
const uint8_t *src = from;
uint8_t *dst = to;
for (size_t i = 0; i < count; i++) {
memcpy(dst + i * 4, src + i * 3, 3);
}
}
int main() {
int width = 1920, height = 1080;
rgb *src = calloc(sizeof(*src), width * height);
rgba *dst = calloc(sizeof(*dst), width * height);
const char *name[10];
clock_t c[10];
int n = 0;
#define RUNS 100
name[n] = "simple";
for (int i = 0; i < RUNS + 10; i++) {
if (i == 10)
c[n] = -clock();
copy3to4_simple(dst, src, width * height);
}
c[n++] += clock();
name[n] = "memcpy";
for (int i = 0; i < RUNS + 10; i++) {
if (i == 10)
c[n] = -clock();
copy3to4_memcpy(dst, src, width * height);
}
c[n++] += clock();
for (int i = 0; i < n; i++) {
printf("%s: %.3f msec\n", name[i], c[i] * 1000. / CLOCKS_PER_SEC / RUNS);
}
free(src);
free(dst);
return 0;
}
运行 在我的旧 Macbook 上,我得到了这个:
simple: 2.478 msec
memcpy: 1.840 msec
memcpy
比 simple
高出 25%,但您可能会在不同的架构上得到不同的结果。
我有一个 uint8_t 值数组,我的目标是将每 3 个字节复制到一个 dst 数组,但要注意的是我在 dst 数组中从 4 个字节迭代到 4 个字节,如图所示下面。
src = {1,2,3,4,5,6};
dst = {0,0,0,0,0,0,0,0};
...
dst = {1,2,3,0,4,5,6,0}
现在我正在使用以下代码来执行此任务。
for(int i=0; i<arr_size ; i++)
memcpy(dst + i*4, arr_ptr + i*3, 3);
是否有 faster/efficient 方法来做到这一点?
编辑更多上下文:
我有以下结构需要用图像数组中的数据填充,其中 a
将始终初始化为 0.
typedef struct {unsigned char r,g,b,a} uchar4;
...
// init dst
...
*dst = (uchar4 *)malloc(height * width * sizeof(uchar4));
通过为 uchar4 数组赋值 struct.variable = value
,需要花费大量时间,这让我认为从存储 [=33 的图像数组中复制值会更快=] 值,到 uchar4 数组,因为 uchar 和 uint8 在内存中占用 1 个字节。这样,structs 数组用 0 初始化,并且将扁平化图像中的每 3 个字节每 4 个字节粘贴到 uchar arr 中。
Edit2: 代码更正
您根本不需要 memcpy
。仅使用指针运算,您可以执行以下操作:
uint8_t *src = some_values;
uint8_t *end = src + some_values_size;
uint8_t *dst = some_buffer;
for (; src < end; src += 3, dst += 4) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
对于上面的示例,您可以将代码定义为宏并将其用于不同的数据类型。 memcpy
想知道它要复制多少字节,因此你需要一个类型。
注意:代码假设数组长度src
是3的倍数,数组长度dst
等于: (length(src) / 3) * 4
.
我假设arr_size
是要复制的三胞胎的数量。
for(size_t i=0; i<arr_size ; i++)
memcpy(dst + i*3, src + i*4, 3);
这是错误的
for(size_t i=0; i<arr_size ; i++)
memcpy(dst + i*4, src + i*3, 3);
现在是上下文。
typedef struct {unsigned char r,g,b,a} uchar4;
不保证编译器不会添加任何填充。任何指针双关语都可能无法正常工作。添加静态断言以检查结构的大小为 4,如果不使用,则需要使用一些编译器扩展来打包结构。
效率:
很难判断,但是此处答案中带有代码的微不足道的功能表明 memcpy
版本很可能是最有效的。
我曾尝试删除一个内存访问并写得很糟糕(它调用 UBs!一般来说,但它适用于 X86 和 Cortex-M3 及更新版本)。这样做只是出于好奇:(警告!!图形编程内容!!!不适合所有观众)https://godbolt.org/z/Pefc6T
有很多方法可以尝试和优化您的转化循环。正如 0___________ 所建议的那样,您应该考虑 memcpy
大小的块,因为大多数优化器将为目标平台生成非常有效的代码,击败手工编码的天真替代方案。
这是比较 3 种方法的快速基准:
- 显式复制 3 个字节,递增指针。
- 为此副本使用
memcpy
可以添加其他方法,例如尝试利用 SIMD 指令,这应该以牺牲可移植性为代价提供显着的性能改进。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct rgb {
uint8_t r, g, b;
} rgb;
typedef struct rgba {
uint8_t r, g, b, a;
} rgba;
void copy3to4_simple(void *to, const void *from, size_t count) {
const uint8_t *src = from;
uint8_t *dst = to;
uint8_t *end = dst + count * 4;
while (dst < end) {
dst[0] = src[0];
dst[1] = src[1];
dst[3] = src[2];
dst += 4;
src += 3;
}
}
void copy3to4_memcpy(void *to, const void *from, size_t count) {
const uint8_t *src = from;
uint8_t *dst = to;
for (size_t i = 0; i < count; i++) {
memcpy(dst + i * 4, src + i * 3, 3);
}
}
int main() {
int width = 1920, height = 1080;
rgb *src = calloc(sizeof(*src), width * height);
rgba *dst = calloc(sizeof(*dst), width * height);
const char *name[10];
clock_t c[10];
int n = 0;
#define RUNS 100
name[n] = "simple";
for (int i = 0; i < RUNS + 10; i++) {
if (i == 10)
c[n] = -clock();
copy3to4_simple(dst, src, width * height);
}
c[n++] += clock();
name[n] = "memcpy";
for (int i = 0; i < RUNS + 10; i++) {
if (i == 10)
c[n] = -clock();
copy3to4_memcpy(dst, src, width * height);
}
c[n++] += clock();
for (int i = 0; i < n; i++) {
printf("%s: %.3f msec\n", name[i], c[i] * 1000. / CLOCKS_PER_SEC / RUNS);
}
free(src);
free(dst);
return 0;
}
运行 在我的旧 Macbook 上,我得到了这个:
simple: 2.478 msec
memcpy: 1.840 msec
memcpy
比 simple
高出 25%,但您可能会在不同的架构上得到不同的结果。