【问题标题】:How can I copy small data efficiently from a char buffer without breaking the strict aliasing rules?如何在不违反严格的别名规则的情况下有效地从 char 缓冲区复制小数据?
【发布时间】:2015-11-06 06:58:50
【问题描述】:

另一个问题的答案:Strict aliasing rule and 'char *' pointers 表示使用char* 检查T 对象的二进制内容是可以的。但是使用T* 覆盖字符缓冲区是不行的。

现在我有一个函数,它采用二进制数据的 char 缓冲区。并在阅读时做这样的事情:

// unsigned char *pData used to walk through the buffer.
uint32_t value = *(unit32_t*)pData;
pData += 4;

如果我这样做打破了严格的别名,还有什么其他更有效的方法可用?当使用少量字节调用 memcpy 调用时,编译器会优化它们吗?

【问题讨论】:

  • 是的,只需使用memcpy(),它是大多数现代(

标签: c strict-aliasing


【解决方案1】:

如果我这样做打破了严格的别名...

是的,你知道

还有哪些更有效的方法可用?

如果缓冲区必须是char,则需要在访问值之前将memcpy 转换为uint32_t。当然,如果你所有的值都是uint32_ts,你可以创建一个uint32_ts 的缓冲区,并将它传递给用chars 填充它的函数,因为严格的别名是一种单向禁止,即使用uint32_t* 作为char* 是允许的。

当使用少量字节调用 memcpy 调用时,编译器会优化它们吗?

许多 CPU 都有针对 memcpy 的内置指令。现代编译器使用这些指令来提高效率。

【讨论】:

    【解决方案2】:

    当使用少量字节调用 memcpy 调用时,编译器会优化它们吗?

    对于这个示例代码:

    #define _CRT_SECURE_NO_WARNINGS // To allow usage of scanf in vc++2015
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        // printf and scanf to prevent code elimination
        char bytes[ 4 ];
        scanf( "%s", bytes ); 
        char buffer[ 4 ];
        memcpy( buffer, bytes, 4 );
        printf( "%s", buffer );
    
        return 0;
    }
    

    Visual C++ 2015 生成了这个程序集输出(发布版本,x64):

    ; memcpy was replaced by a simple register move
    mov    eax, DWORD PTR bytes$[rsp] 
    lea    rdx, QWORD PTR buffer$[rsp]                   ; setting arguments 
    lea    rcx, OFFSET FLAT:??_C@_02DKCKIIND@?$CFs?$AA@ ; for printf call
    ; at this point copied array was actually stored in memory
    mov    DWORD PTR buffer$[rsp], eax     
    call   printf
    

    所以是的,现代编译器甚至不会调用该过程。

    【讨论】:

    • 在多大程度上可以确信memcpy(&amp;temp,p,sizeof *p); temp|= 0x80808080; memcpy(p, &amp;temp, sizeof *p); 最终会像禁用严格别名和使用*((uint32_t*)p)|=0x80808080; 一样高效
    • @supercat,我用 msvc 试过这个,它最终变成了一条 or dword ptr [esp+8], 80808080h 指令,没有 memcpy 调用,没有单一动作。现代优化器做得很好。当然,与任何优化一样,无法保证特定编译器会对特定代码段进行特定优化,但 memcpy 函数是如此基础,并且直接或间接使用了如此多次,以至于您可能期望它高度针对所有常见用例进行了优化...
    • @supercat,说到性能,我不会禁用严格别名,因为它存在的原因是允许更多优化,这样做实际上可能会让你的代码变慢。
    • 通过“restrict”无法实现的严格别名可以实现哪些优化?在我看来,基于类型的别名有时会幸运地进行一些优化,但前提是指针的类型和其他对象的类型恰好不同;创建一个指向不会别名的对象的“限制”指针,然后通过该指针访问另一个对象将为编译器提供与基于类型的别名相同的有用语义保证,但仅在实际需要时。
    • @supercat,我没想到restrict。很可能在禁止严格别名规则的同时手动限制指针可以给我们同样的优化。我想不出这两种方法表现不同的例子。这可能是一个很好的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 2017-02-25
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    相关资源
    最近更新 更多