什么';在C中复制两个相邻字节的最快方法是什么?

什么';在C中复制两个相邻字节的最快方法是什么?,c,performance,memory,C,Performance,Memory,好的,让我们从最明显的解决方案开始: memcpy(Ptr, (const char[]){'a', 'b'}, 2); 调用库函数的开销很大。编译器有时不会对其进行优化,我不会依赖编译器优化,但即使GCC很聪明,如果我要用无用的编译器将程序移植到更奇特的平台上,我也不想依赖它 所以现在有一个更直接的方法: Ptr[0] = 'a'; Ptr[1] = 'b'; 它不涉及任何库函数的开销,而是进行两种不同的赋值。第三,我们有一种双关语: *(uint16_t*)Ptr = *(uint16_

好的,让我们从最明显的解决方案开始:

memcpy(Ptr, (const char[]){'a', 'b'}, 2);
调用库函数的开销很大。编译器有时不会对其进行优化,我不会依赖编译器优化,但即使GCC很聪明,如果我要用无用的编译器将程序移植到更奇特的平台上,我也不想依赖它

所以现在有一个更直接的方法:

Ptr[0] = 'a';
Ptr[1] = 'b';
它不涉及任何库函数的开销,而是进行两种不同的赋值。第三,我们有一种双关语:

*(uint16_t*)Ptr = *(uint16_t*)(unsigned char[]){'a', 'b'};
如果遇到瓶颈,我应该使用哪一个?在C中只复制两个字节的最快方法是什么

问候,

Hank Sauri

您建议的方法中只有两种是正确的:

memcpy(Ptr, (const char[]){'a', 'b'}, 2);

在X86 GCC 10.2上,

mov     eax, 25185
mov     WORD PTR [something], ax
这是可能的,因为这是一个复杂的问题

因为一个好的编译器可以发现它们是相同的,所以使用一个更容易在cse中编写的编译器。如果要设置一个或两个字节,请使用后一个字节;如果有几个字节使用前一个字节,请使用字符串而不是复合文字数组


你建议的第三个

*(uint16_t*)Ptr = *(uint16_t*)(unsigned char[]){'a', 'b'};
也编译为,即在这种情况下,它的行为相同

但除此之外,它还有2-4个未定义的行为点,因为它有两次严格的别名冲突和两次严重的别名冲突,并且在源和目标上都可能存在未对齐的内存访问。未定义的行为并不意味着它不能像您预期的那样工作,但也不意味着它必须像您预期的那样工作。这种行为没有定义。为什么你会如此在意一个坏编译器的性能,以至于你会编写出一个好编译器无法运行的代码

如有疑问,请使用

或(叮当声)


在C中,这种方法无疑是最快的:

Ptr[0]=“a”; Ptr[1]=“b”

这就是为什么:

所有Intel和ARM CPU都能够在选定的汇编指令中存储一些常量数据(也称为即时数据)。这些指令是内存到cpu和cpu到内存的数据传输,如:MOV

这意味着,当这些指令从程序内存提取到CPU时,即时数据将与指令一起到达CPU

“a”和“b”是常量,因此可能会随MOV指令一起进入CPU

一旦即时数据在CPU中,CPU本身只需对数据内存进行一次内存访问,即可将“a”写入Ptr[0]

再见,
恩里科·米利奥雷

*(uint16_t*)Ptr
?否,这取决于端度,取决于
Ptr
是否与
uint16\u t
对齐。测量速度。这真的有什么区别吗?第三个选项通过代码审查的机会为零。我甚至都不想去理解它。浪费我的时间。你花了太多时间为糟糕的编译器优化代码。
如果Ptr分配了3个字节,为什么只取消引用前两个字节会导致segfault?
对齐与分配的字节数无关。没有。在C中复制两个相邻字节的最佳方式取决于编译器、平台、缓存结构以及程序和系统的其他部分正在做什么。而且,这可能不重要。好吧,排除3个,但剩下的两个选项中?嗯,
memcpy(Ptr,“ab”,2)
也可以工作,如果您不介意在二进制文件的静态部分中增加一个空字节的可能性的话。(根据编译器资源管理器的说法,至少GCC会将这个memcpy优化为mov。)如果能够确保指针对齐,并以流行的ARM Cortex-M0等嵌入式平台为目标,“坏”代码将比其他代码效率更高,虽然gcc 9.2.1将通过实际调用
memcpy
来处理
memcpy
。感谢您的时间和详细回答Howdy:P似乎我现在有一个C的竞争对手:DIf知道字节将对齐,并且一个目标是嵌入式目标,如ARM Cortex-M0,即“坏”代码将产生比其他形式更好的结果,因为其他形式将迫使编译器生成可容纳任意对齐的代码。所有Intel和ARM CPU,,,世界并不局限于Intel和ARM CPU。“毫无疑问”–嗯,这三种方法都可以编译成与其他答案完全相同的指令。所有现代CPU都能够将即时数据放入传输汇编指令中。@Enricomiglier:在许多ARM系列中,即时操作数的值范围非常有限;给定
外部x;x=1234567,编译器需要对常量1234567使用PC相对加载,对x地址使用另一个PC相对加载,然后才能将值存储到x。在C中,您经常会发现以下语句:if(a==0)if(a==1)if(a==10)
*(uint16_t*)Ptr = *(uint16_t*)(unsigned char[]){'a', 'b'};
#include <string.h>
#include <stdint.h>

int c1(char *Ptr) {
    memcpy(Ptr, (const char[]){'a', 'b'}, 2);
}

int c2(char *Ptr) {
    Ptr[0] = 'a';
    Ptr[1] = 'b';
}

int c3(char *Ptr) {
    // Bad bad not good.
    *(uint16_t*)Ptr = *(uint16_t*)(unsigned char[]){'a', 'b'};
}
c1:
        mov     eax, 25185
        mov     WORD PTR [rdi], ax
        ret
c2:
        mov     eax, 25185
        mov     WORD PTR [rdi], ax
        ret
c3:
        mov     eax, 25185
        mov     WORD PTR [rdi], ax
        ret
c1:                                     # @c1
        mov     word ptr [rdi], 25185
        ret
c2:                                     # @c2
        mov     word ptr [rdi], 25185
        ret
c3:                                     # @c3
        mov     word ptr [rdi], 25185
        ret