[英]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
版本很可能是最有效的。
我試圖刪除一個 memory 訪問並寫得很糟糕(它通常會調用 UB。但它適用於 X86 和 Cortex-M3 及更高版本):它只是為了好奇而完成的:(警告!!圖形編程內容!!! 不適合所有觀眾) https://godbolt.org/z/Pefc6T
有很多方法可以嘗試和優化您的轉換循環。 正如0___________所建議的那樣,您應該考慮將memcpy
用於大小大小的塊,因為大多數優化器將為目標平台生成非常有效的代碼,擊敗手工編碼的幼稚替代方案。
這是比較 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%,但您可能會在不同的架構上得到不同的結果。
你根本不需要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.