V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
iqoo
V2EX  ›  C

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

  •  
  •   iqoo · Mar 26, 2023 · 3182 views
    This topic created in 1137 days ago, the information mentioned may be changed or developed.

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

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

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

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

    优先考虑优雅的话,效率就往后放放
    HaroldFinchNYC
        2
    HaroldFinchNYC  
       Mar 26, 2023
    define "优雅"
    xuanbg
        3
    xuanbg  
       Mar 26, 2023
    为什么不对齐呢?都 2023 年了,还缺这么点内存?
    leonshaw
        4
    leonshaw  
       Mar 26, 2023 via Android
    用位运算
    duke807
        5
    duke807  
       Mar 26, 2023 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  
       Mar 26, 2023 via Android
    我建议直接用对应数据类型的指针,别管其他的。怎么解引用没对齐的指针那是编译器的问题,只有给我实现了 c 语言标准定义的行为,编译器爱怎么优化怎么优化。
    wudicgi
        7
    wudicgi  
       Mar 26, 2023
    @favourstreet 这个还真不是编译器能完全负责的,如果所有地址都视为未对齐的,生成机器码的效率会非常低
    其实一般只有处理外来的数据流时可能会遇到这种情况,特殊处理一下就好了
    leonshaw
        8
    leonshaw  
       Mar 26, 2023 via Android
    @favourstreet 不对齐的指针是 UB
    duke807
        9
    duke807  
       Mar 26, 2023 via Android
    @favourstreet
    no no no ,在不支持非对齐访问的 cpu 上,你这样搞 cpu 会进异常的
    icyalala
        10
    icyalala  
       Mar 26, 2023
    在支持非对齐内存访问的架构上,无论用 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  
       Mar 26, 2023 via Android
    @wudicgi
    @duke807
    @duke807
    对不起我看了一下还真是 UB ,让各位大佬见笑了
    DeltaC
        12
    DeltaC  
       Mar 26, 2023
    “用指针类型转换代替 memcpy ”,可能违反 strict aliasing rule ,用的时候要注意。
    这里不止存在 unaligned access 问题,你还进行了 type punning 。
    符合标准的 type punning 方式有 4 个:1.符合 strict aliasing rule 的指针访问 2.union 3.memcpy 4.std::bit_cast
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   6085 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 60ms · UTC 03:22 · PVG 11:22 · LAX 20:22 · JFK 23:22
    ♥ Do have faith in what you're doing.