每 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 版本很可能是最有效的。

https://godbolt.org/z/E4s8sa

我曾尝试删除一个内存访问并写得很糟糕(它调用 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

memcpysimple 高出 25%,但您可能会在不同的架构上得到不同的结果。