簡體   English   中英

每 n 個字節復制 memory

[英]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);

有沒有更快/更有效的方法來做到這一點?

編輯以獲取更多上下文:
我有以下結構,需要用圖像數組中的數據填充,其中a將始終用 0 初始化。

typedef struct {unsigned char r,g,b,a} uchar4;
...
// init dst
...
*dst = (uchar4 *)malloc(height * width * sizeof(uchar4));

通過執行struct.variable = value將值分配給 uchar4 數組,需要花費大量時間,這使我認為將存儲 uint8_t 值的圖像數組中的值復制到 uchar4 數組會更快,由於 uchar 和 uint8 在 memory 中占用 1 個字節。 這樣,structs 數組用 0 初始化,並且展平圖像中的每 3 個字節在 uchar arr 中每 4 個字節粘貼一次。

Edit2:代碼更正

我假設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;

不保證編譯器不會添加任何填充。 並且任何指針雙關語都可能無法正常工作。 添加 static 斷言以檢查結構的大小為 4,如果不使用,則需要使用一些編譯器擴展來打包結構。

效率:很難判斷,但這里的答案中帶有代碼的瑣碎函數表明, memcpy版本很可能是最有效的。

https://godbolt.org/z/E4s8sa

我試圖刪除一個 memory 訪問並寫得很糟糕(它通常會調用 UB。但它適用於 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%,但您可能會在不同的架構上得到不同的結果。

你根本不需要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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM