[工作积累] bitfield
ISO/IEC 14882:2003: 9.6 Bit-fields [class.bit]
- A member-declarator of the form identifieropt : constant-expression specifies a bit-field; its length is set off from the bit-field name by a colon. The bit-field attribute is not part of the type of the class member. The constant-expression shall be an integral constant-expression with a value greater than or equal to zero. The constant-expression may be larger than the number of bits in the object representation (3.9) of the bit-field’s type; in such cases the extra bits are used as padding bits and do not participate in the value representation (3.9) of the bit-field. Allocation of bit-fields within a class object is implementation-defined. Alignment of bit-fields is implementation-defined. Bit-fields are packed into some addressable allocation unit. [Note: bit-fields straddle allocation units on some machines and not on others. Bit-fields are assigned right-to-left on some machines, left-to-right on others. ]
- A declaration for a bit-field that omits the identifier declares an unnamed bit-field. Unnamed bit-fields are not members and cannot be initialized. [Note: an unnamed bit-field is useful for padding to conform to externally-imposed layouts. ] As a special case, an unnamed bit-field with a width of zero specifies alignment of the next bit-field at an allocation unit boundary. Only when declaring an unnamed bit-field may the constant-expression be a value equal to zero.
- A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (3.9.1). It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned. A bool value can successfully be stored in a bit-field of any nonzero size. The address-of operator & shall not be applied to a bit-field, so there are no pointers to bit-fields. A non-const reference shall not be bound to a bit-field (8.5.3). [Note: if the initializer for a reference of type const T& is an lvalue that refers to a bit-field, the reference is bound to a temporary initialized to hold the value of the bit-field; the reference is not bound to the bit-field directly. See 8.5.3. ]
- If the value true or false is stored into a bit-field of type bool of any size (including a one bit bitfield), the original bool value and the value of the bit-field shall compare equal. If the value of an enumerator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type, the original enumerator value and the value of the bit-field shall compare equal.
最近在64bit IOS上遇到一个问题, 发现bit field的数据不对. 这个bit field是一个data struct, 在host(win32)上生成. 运行时候发现IOS取的值不对.
比如
//IOS 32
struct
{
//allocation unit 1
int a : ;
int b : ;//12bit
int c : ; //28bit
int d : ; //4 bit allocation uint 1, 4 bit allocation unit 2
int e : ; //20 bit in allocation unit 2
}; //IOS 64
struct
{
//allocation unit 1
int a : ;
int b : ;//12bit
int c : ; //28bit
//allocation unit 2
int d : ; //8 bit
int e : ; //24 bit
};
在IOS的32位上, d会横跨两个allocation uint, 而在IOS的64位上, d直接从第二个allocation unit上从新开始. clang在编译32位跟64位时的处理方式不一样,不知道是不是bug. 就算它是clang的bug, 但仍然是遵循标准的.因为C++标准说了可以这样.
根据C++标准, 这个是implementation defined behavior, 编译器可以选择横跨也可以不横跨, 都可以.
在host(win32)上的打包程序, MSVC也是横跨方式, 与clang处理32 bit IOS的方式一致. 所以(凑巧)host生成的数据可以在IOS32上面使用, 可能大多数编译器都以横跨方式处理bitfields吧.
解决方法是:将a,b,c对齐到32bit的整数, 这样强制d从新的allocation unit开始, 没有横跨的问题, 对于IOS32位和IOS64位都有效.
//shared disk data //IOS 32
struct
{
//allocation unit 1
int a : ; //4bit
int b : ; //12bit
int c : ; //32bit
//allocation unit 2
int d : ; //8 bit
int e : ; //24 bit
}; //IOS 64
struct
{
//allocation unit 1
int a : ; //4bit
int b : ; //12bit
int c : ; //32bit
//allocation unit 2
int d : ; //8 bit
int e : ; //24 bit
};
另外, IOS 64bit的额外参考: 对齐等等其他因素
bitfield用在game data里面, 如果多平台共享的同一份数据话, 就有类似上面的的问题. 甚至顺序也有可能不一样(left-to-right/right-to-left), 所以blade里面没有用过. 而公司的代码用的地方非常多.
公司的代码虽然是跨平台的, 但是之前是每个平台都有不同的数据, 现在IOS 32/64要共享同一份数据, 才会有上面的问题. 这个是历史的原因.毕竟积累了近20年的代码. 注重代码和技术积累, 这样的公司才靠谱.
另外公司有自己的编译器, 最好的方式是在编译器上直接解决bitfield的straddle和left-to-right/right-to-left的问题. 这样上面的代码就不用关心这些琐碎的细节了.
个人的观点是每个平台使用通用的数据最好.
以下内容摘自我的另一博客(http://hi.baidu.com/crazii_chn/item/62705798f8a76bd91b49dfd8):
一般来说, 需要根据每一种目标平台, 各生成一份对应的数据.
这样做的好处是每个平台的数据可以根据目标特性做调整.比如移动平台的场景规模/质量和数据量一般都相对要小.
由于每种CPU的数据处理特性(cache line, alignment, SIMD指令集)可能都不一样,所以需要做特别的处理.对于数据的格式,有两种做法, 第一是每个平台的数据包格式不一样, 第二是种是使用统一的格式, 也就是说尽管每个目标平台都有不同的一个数据包(因目标规模而定), 但理论上某个平台可以正常加载其他所有平台的数据包.
而第一种则不同, 各个平台的数据格式不一样, 比如一个struct的成员对齐不同, 那么序列化后的数据会有大小和偏移量的差别.第一种方式不用特别考虑数据对齐等等问题, 但是要生成和维护不同目标平台下的数据包和工具, 虽然维护成本相对较低,但是如果工具很多的时候也很繁琐.
第二种方式需要在代码中考虑对齐等等因素, 保证一个数据包可以被所有的平台加载, 这么做可能也很繁琐,需要特别小心,并经过完整的数据测试.这里有一个android下x86和arm的结构对齐的处理 (4.1 Forced Memory Alignment):
http://software.intel.com/en-us/articles/android-application-development-and-optimization-on-the-intel-atom-platform
参考http://software.intel.com/en-us/blogs/2011/08/18/understanding-x86-vs-arm-memory-alignment-on-android/
比如上面第一个链接中使用的"-malign-double" GCC参数, 强制8字节的数据对齐的8字节边界.保证同一份数据可以被不同的CPU正常加载. 第二个链接使用的是__attribute__((aligned(n))), 来强制数据成员的对齐.我所在的公司使用的是第一种方式. 第一种方式的问题在于同一个OS也有不同的target CPU(platform), 这样会出现对于android平台, x86有一份数据, 而arm有另外一份数据. 所以个人倾向于第二种方式, 除了上文链接中的方式以外, 也可以强制不读写struct, 只读写原子数据, 把一个struct的成员逐个单独读写.
如何保证数据能够通用呢? 严格按照C/C++标准来做. 如果是implementation defined或者unspecified的定义, 那么就规避, 不要使用, 比如上面的那个例子. undefined behavior更不能用. 这样在host编译出的data-tools, 处理数据的方式与target上的executables处理数据的方式一致, 能够保证host上打包生成的同一份数据, 在所有平台上都可以用.
[工作积累] bitfield的更多相关文章
- [工作积累] Google/Amazon平台的各种坑
所谓坑, 就是文档中没有标明的特别需要处理的细节, 工作中会被无故的卡住各种令人恼火的问题. 包括系统级的bug和没有文档化的限制. 继Android的各种坑后, 现在做Amazon平台, 遇到的坑很 ...
- [工作积累] 32bit to 64bit: array index underflow
先贴一段C++标准(ISO/IEC 14882:2003): 5.2.1 Subscripting: 1 A postfix expression followed by an expression ...
- [工作积累] GCC 4.6 new[] operator内存对齐的BUG
对于用户没有定义dctor(包括其所有成员)的类来说, new CLASS[n] 可能会直接请求sizeof(CLASS)*n的空间. 而带有dctor的 类, 因为delete[]的时候要逐个调用析 ...
- [工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls
UE4 的渲染分为两个模式1.编辑器是同步绘制的 2.游戏里是FParallelCommandListSet并行派发的. mesh渲染也分两类,static mesh 使用TStaticMeshDra ...
- [工作积累] D3D10+ 中 Pixel Shader 的input semantic和参数顺序
由于semantic的使用,我们有理由相信 vertex shader的output 和 pixel shader的input是按照semantic来匹配的,而跟传入顺序无关.印象dx9时代是这样. ...
- [工作积累] shadow map问题汇总
1.基本问题和相关 Common Techniques to Improve Shadow Depth Maps: https://msdn.microsoft.com/en-us/library/w ...
- linux-日常工作积累
Linux常用命令之envsubst https://blog.csdn.net/banche163/article/details/101369495 Linux中的EAGAIN含义 https:/ ...
- HAL层Camera模块Dump图片--工作积累
Camera的raw data一般都是YUV420的格式,数据的特点是: YUV 4:2:0采样,每四个Y共用一组UV分量 YUV420格式: 先Y,后V,中间是U.其中的Y是w * h,U和V是w/ ...
- JS工作积累
/* * YYYY-MM-DD类型的字符串日期比较 * */ String.prototype.compareAsDate=function(dateStr){//prototype原型方法 var ...
随机推荐
- (转)PHP的语言结构和函数的区别
相信大家经常看到对比一些PHP应用中,说用isset() 替换 strlen(),isset比strlen执行速度快等. 例子: if ( isset($user) ) { //do some thi ...
- Oracle中建表空间以及用户
第一步:创建临时表空间 --创建临时表空间-- CREATE TEMPORARY TABLESPACE JSYCCS_TEMP ---'JSYCCS_TEMP'临时表空间名 TEMPFILE 'E ...
- 问题记录-Activity跳转后显示空白界面
前两天写一个简易安卓记事本,从主界面跳转到添加内容界面总是显示空白. 明明有setContentView xml文件在可视化开发环境下也正常显示.后经前辈指点,原来是复写onCreate函数时出现了问 ...
- opengl基础学习专题 (二) 点直线和多边形
题外话 随着学习的增长,越来越觉得自己很水.关于上一篇博文中推荐用一个 学习opengl的 基于VS2015的 simplec框架.存在 一些问题. 1.这个框架基于VS 的Debug 模式下,没有考 ...
- SQL基础篇---函数及其函数配套使用的关键字
一.数值函数 知识点1 SUM 求总和 SELECT breakfast,sum(price) FROM my_foods GROUP BY breakfast ORDER BY SUM(price) ...
- java遍历Map的几种方式
1.遍历map的几种方式:private Hashtable<String, String> emails = new Hashtable<String, String>(); ...
- [译] Swift 的响应式编程
原文 https://github.com/bboyfeiyu/iOS-tech-frontier/blob/master/issue-3/Swift的响应式编程.md 原文链接 : Reactiv ...
- debain上安装mono3.4.0和jexus5.5.2
今天折腾了四个小时才把这个正确安装上,特此记录下.特别感谢群友的支持. 在VMware上新安装了Debain7.5,具体细节不复述了. 一.更新系统 #apt-get update #apt-get ...
- python time,string 转换
1. 将字符串转换成时间,字符串格式为05/16/2015 datetime.datetime.strptime(STRING,"%m/%d/%Y") 2. 将时间转换成字符串:格 ...
- iOS 进阶 第十九天(0423)
0427 深复制/浅复制 浅复制:是址引用 深复制:是值拷贝 下面是解释,如下图: 运行时机制runtime 1.表象的就是MPMoviePlayer 2.深层的是 clang -rewrite-ob ...