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

2023-03-26 12:42:06 +08:00
 iqoo

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

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

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

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

优先考虑优雅的话,效率就往后放放
HaroldFinchNYC
2023-03-26 13:43:19 +08:00
define "优雅"
xuanbg
2023-03-26 13:46:09 +08:00
为什么不对齐呢?都 2023 年了,还缺这么点内存?
leonshaw
2023-03-26 13:52:41 +08:00
用位运算
duke807
2023-03-26 13:54:01 +08:00
参考 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
2023-03-26 14:19:08 +08:00
我建议直接用对应数据类型的指针,别管其他的。怎么解引用没对齐的指针那是编译器的问题,只有给我实现了 c 语言标准定义的行为,编译器爱怎么优化怎么优化。
wudicgi
2023-03-26 14:28:05 +08:00
@favourstreet 这个还真不是编译器能完全负责的,如果所有地址都视为未对齐的,生成机器码的效率会非常低
其实一般只有处理外来的数据流时可能会遇到这种情况,特殊处理一下就好了
leonshaw
2023-03-26 14:29:17 +08:00
@favourstreet 不对齐的指针是 UB
duke807
2023-03-26 14:29:22 +08:00
@favourstreet
no no no ,在不支持非对齐访问的 cpu 上,你这样搞 cpu 会进异常的
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
2023-03-26 15:22:00 +08:00
@wudicgi
@duke807
@duke807
对不起我看了一下还真是 UB ,让各位大佬见笑了
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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/927247

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX