如果使用32位整型会溢出,那么是否可以使用一个40位结构体代替64位长整型?
2015-02-19 23:10
459 查看
问题:
假如说,使用32位的整型会溢出,在不考虑使用长整型的情况下,如果我们只需要表示2的40次方范围内的数,是否可以利用某些40位长的数据类型来表示呢?这样的话,每个整型数就可以节省24位的空间。如果可以,该怎么做?
需求是:我现在必须处理数以亿计的数字,所以在存储空间上受到了很大的限制。
回答:
可以是可以,但是……这种方法的确可行,但这么做通常没什么意义(因为几乎没有程序需要处理多达十亿的数字):
#include <stdint.h> // 不要考虑使用long long类型 struct bad_idea { uint64_t var : 40; };
在这里,变量var占据40位大小,但是这是以生成代码时拥有非常低的运行效率来换取的(事实证明“非常”二字言过其实了——测试中程序开销仅仅增加了1%到2%,正如下面的测试时间所示),而且这么做通常没什么用。除非你还需要保存一个24位的值(或者是8位、16位的值),这样你皆可以它们放到同一个结构中。不然的话,因为对齐内存地址产生的开销会抵消这么做带来的好处。
在任何情况下,除非你是真的需要保存数以亿计的数字,否则这样做给内存消耗带来的好处是可以忽略不计的(但是为了处理这些位字段的额外代码量是不可忽略的!)。
说明:
在此期间,这个问题已经被更新了,是为了说明实际上确实有需要处理数以亿计数字的情况。假设,采取某些措施来防止因为结构体对齐和填充抵消好处(比如在后24位中存储其它的内容,或者使用多个8位来存储40位),那么这么做就变得有意义了。如果有十亿个数,每个数都节省三个字节的空间,那么这么做就非常有用了。因为使用更小的空间存储要求更少的内存页,也就会产生更少的cache和TLB不命中和内存缺页(单个缺页会产生数以千万计的指令 [译者注:直译是这样,但语义说不通!])。
尽管上面提到的情况不足以充分利用到剩余的24位(它仅仅使用了40位部分),如果确实在剩余位中放入了有用的数据,那么使用类似下面的方法会使得这种思路就管理内存而言显得非常有用。
struct using_gaps { uint64_t var : 40; uint64_t useful_uint16 : 16; uint64_t char_or_bool : 8; };
结构体大小和对齐长度等于64位整型的大小,所以只要使用得当就不会浪费空间,比如对一个保存10亿个数的数组使用这个结构(不考虑使用指定编译器的扩展)。如果你不会用到一个8位的值,那么你可以使用一个48位和16位的值(giving a bigger overflow margin)。
或者以牺牲可用性为代价,把8个64位的值放入这样的结构体中(或者使用40和64的组合使得其和满足320)。当然,在这种情况下,通过代码去访问数组结构体中的元素会变得非常麻烦(尽管一种方法是实现一个operator[]在功能上还原线性数组,隐藏结构体的复杂性)。
更新:
我写了一个快速测试工具,只是为了获得位字段的开销(以及伴随位字段引用的重载操作)。由于长度限制将代码发布在gcc.godbolt.org上,在本人64位Win7上的测试结果如下:运行测试的数组大小为1048576 what alloc seq(w) seq(r) rand(w) rand(r) free ----------------------------------------------------------- uint32_t 0 2 1 35 35 1 uint64_t 0 3 3 35 35 1 bad40_t 0 5 3 35 35 1 packed40_t 0 7 4 48 49 1 运行测试的数组大小为16777216 what alloc seq(w) seq(r) rand(w) rand(r) free ----------------------------------------------------------- uint32_t 0 38 14 560 555 8 uint64_t 0 81 22 565 554 17 bad40_t 0 85 25 565 561 16 packed40_t 0 151 75 765 774 16 运行测试的数组大小为134177228 what alloc seq(w) seq(r) rand(w) rand(r) free ----------------------------------------------------------- uint32_t 0 312 100 4480 4441 65 uint64_t 0 648 172 4482 4490 130 bad40_t 0 682 193 4573 4492 130 packed40_t 0 1164 552 6181 6176 130
我们看到,位字段的额外开销是微不足道的,但是当以友好的方式线性访问数据时伴随位字段引用的操作符重载产生的开销则相当显著(大概有3倍)。在另一方面,随机访问产生的开销则无足轻重。
这些时间表明简单的使用64位整型会更好,因为它们在整体性能上要比位字段好(尽管占用更多的内存),但是显然它们并没有考虑随着数据集增大带来的缺页开销。一旦程序内存超过RAM大小,结果可能就不一样了(未亲自考证)。
via:stackoverflow
作者:DamonMichael Kohne 译者:KayGuoWhu 校对:wxy
相关文章推荐
- 如果系统能够保证不在0x000000007fffffff以上的地址分配内存,那么应用程序就能够正常运行。把一个高33位都为0的64位地址截断为32位地址,无论如何都不会产生问题。系统可以提供这一保证,
- 时钟频率,最大不会超过3000MHz;字长,可以是32位或64位;核数,可以是单核、双核、或四核;是否支持超线路。各项信息要求使用位域来表示。通过输出sizeof(CPU)来观察该类所占的字节数
- 我这 cpu 可以装win7吗?如果可以装32位,还是64位?
- 单位里有个软件,必须要32位的win7系统下才可以使用,为什么64位下就不能用?请高手指点
- 听说此文又是一篇找砖头的文章, 善良,可爱型的人请速速离开。 1: 模板引擎。 模板引擎是否得继续使用及发挥作用, 成为phper们在争论的第一话题。 其实我们可以换个立场来思考, 如果d
- Adapter 如果客户需要使用某个类的服务,而这项服务是这个类用一个不同的接口提供的,那么,可以使用适配器模式为客户提供一个期望的接口
- windows 编译libxml2 64位及32位都可以使用
- ExtTag,如果你不想写extjs那么麻烦的代码可以尝试使用html方式的方法来创建extjs控件.
- 每日刷题(2015/6/21):对比哈希表和STL map。哈希表是怎么实现的?如果输入数据规模不大, 可以使用什么数据结构来代替哈希表。
- “希望可以帮到你”“尝试加载Oracle客户端库时引发BadImageFormatException。如果在安装32位Oracle客户端组件的情况下以64位模式运行,将出现此问题。”解决方案
- 16位平台、32位平台、64位平台下char、short、int、long、指针 分别占多少字节,操作系统可以使用的最大内存空间
- 32位Eclipse,可以使用64位的jre吗?
- 有如下CAT_s结构体定义,回答: 1) 在一台64位的机器上,使用32位编译,Garfield变量占用多少内存空间?64位编译又是如何?
- 要特别注意数据是否溢出,另外,int是16位,long是32位,而long long才是64位int
- C语言中用函数实现整型数据位宽,如16,32位,不可以使用sizeof
- 如果对象值相同(x.equals(y)==true),那么是否可以有不同的hash code?
- 转载:用oralce连接.net客户端出现问题:“数据连接不成功,请检查该数据库是否已启动尝试加载oracle客户端时引发BadImageFormatException.如果在安装32位Oracle客户端组件的情况下以64位模式运行,”的解
- 用oralce连接.net客户端出现问题:“数据连接不成功,请检查该数据库是否已启动尝试加载oracle客户端时引发BadImageFormatException.如果在安装32位Oracle客户端组件的情况下以64位模式运行,”的解决办法
- 小易邀请你玩一个数字游戏,小易给你一系列的整数。你们俩使用这些整数玩游戏。每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字。 例如: 如果{2,1,2,7}是你有的一系列数,小易说的数字是11.你可以得到方案2+2+7 = 11.如果顽皮的小易想坑你,他说的数字是6,那么你没有办法拼凑出和为6 现在小易给你n个数,让你找出无法从n个数中选取部分求和
- 如果判断图片是否存在,可以使用javascript,省资源并且方便简单.