V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iqoo
V2EX  ›  C

C 中访问不对齐的数据有什么优雅的方案?

  •  
  •   iqoo · 2023-03-26 12:42:06 +08:00 · 2419 次点击
    这是一个创建于 607 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如从数据流当前位置读取一个 u64 ,内存大概率是不对齐的,一般做法是用 memcpy 把数据复制到变量上。

    但有些 CPU 允许访问不对齐的内存,只是效率较低。如果程序只运行在这类 CPU 上,并且该变量读取次数极少(可能就读取一次),那么直接用指针类型转换代替 memcpy ,少一行复制代码,是不是更简洁一些?

    至于运行效率,用 memcpy 复制 u64 编译时会自动展开。但内存对齐不可知的访问,编译时是否也会自动展开?如果是的话,最终效率应该都一样。

    12 条回复    2023-03-26 15:57:37 +08:00
    wudicgi
        1
    wudicgi  
       2023-03-26 13:39:09 +08:00
    如果字节序和本机字节序一样的话,用 memcpy() 就挺好
    要是还要转换字节序的话,写个函数按字节读取再位运算合并成 uint64_t 数值也挺好,看着很清晰

    优先考虑优雅的话,效率就往后放放
    HaroldFinchNYC
        2
    HaroldFinchNYC  
       2023-03-26 13:43:19 +08:00
    define "优雅"
    xuanbg
        3
    xuanbg  
       2023-03-26 13:46:09 +08:00
    为什么不对齐呢?都 2023 年了,还缺这么点内存?
    leonshaw
        4
    leonshaw  
       2023-03-26 13:52:41 +08:00 via Android
    用位运算
    duke807
        5
    duke807  
       2023-03-26 13:54:01 +08:00 via Android   ❤️ 4
    参考 linux 内核代码,用 put_unaligned 和 get_unaligned 函数

    我自己经常把 linux 代码搬到 mcu 用,看这个文件最后面:
    https://github.com/dukelec/cdnet/blob/master/utils/cd_utils.h

    使用举例:
    https://github.com/dukelec/msgpackel/blob/master/msgpackel_c/msgpackel.c
    favourstreet
        6
    favourstreet  
       2023-03-26 14:19:08 +08:00 via Android
    我建议直接用对应数据类型的指针,别管其他的。怎么解引用没对齐的指针那是编译器的问题,只有给我实现了 c 语言标准定义的行为,编译器爱怎么优化怎么优化。
    wudicgi
        7
    wudicgi  
       2023-03-26 14:28:05 +08:00
    @favourstreet 这个还真不是编译器能完全负责的,如果所有地址都视为未对齐的,生成机器码的效率会非常低
    其实一般只有处理外来的数据流时可能会遇到这种情况,特殊处理一下就好了
    leonshaw
        8
    leonshaw  
       2023-03-26 14:29:17 +08:00 via Android
    @favourstreet 不对齐的指针是 UB
    duke807
        9
    duke807  
       2023-03-26 14:29:22 +08:00 via Android
    @favourstreet
    no no no ,在不支持非对齐访问的 cpu 上,你这样搞 cpu 会进异常的
    icyalala
        10
    icyalala  
       2023-03-26 15:15:10 +08:00
    在支持非对齐内存访问的架构上,无论用 memcpy 还是自己按字节复制,
    大部分编译器都会直接优化成 mov 之类的单个指令:
    https://godbolt.org/z/355GzW1eb (clang/x64)
    https://godbolt.org/z/ahhbWrGGb (gcc/x64)
    https://godbolt.org/z/97bav5YMz (clang/arm64)

    在不支持的架构上,memcpy 有的会转为一次函数调用 call memcpy ,有的会展开:
    https://godbolt.org/z/3TxG7eonY (clang/arm32) 展开了
    https://godbolt.org/z/hafqdcqTr (gcc/arm32) 没展开

    所以如果你实际 benchmark 下来 call memcpy 性能比较差,那直接都按字节复制就行
    favourstreet
        11
    favourstreet  
       2023-03-26 15:22:00 +08:00 via Android
    @wudicgi
    @duke807
    @duke807
    对不起我看了一下还真是 UB ,让各位大佬见笑了
    DeltaC
        12
    DeltaC  
       2023-03-26 15:57:37 +08:00
    “用指针类型转换代替 memcpy ”,可能违反 strict aliasing rule ,用的时候要注意。
    这里不止存在 unaligned access 问题,你还进行了 type punning 。
    符合标准的 type punning 方式有 4 个:1.符合 strict aliasing rule 的指针访问 2.union 3.memcpy 4.std::bit_cast
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2802 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 00:05 · PVG 08:05 · LAX 16:05 · JFK 19:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.