来源:不言 发布时间:2019-03-02 17:17:54 阅读量:908
本篇文章给大家带来的内容是关于php中small内存规格的计算(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
small内存分配计算bin_num
在PHP源码中,有一段对small内存规格的计算,具体在Zend/zend_alloc.c的zend_mm_small_size_to_bin函数中,其目的是传入一个size,计算对应的规格。见代码:
1 2 3 4 5 6 7 8 9 10 11 |
|
可以看出,这段代码中分为两种情况进行讨论:
1、size小于等于64的情况;
2、size大于64的情况;
下面我们对这两种情况详细分析下。
看ZEND_MM_BINS_INFO
这个宏知道当size小于等于64的情况是一个等差数列,递增8,所以使用size除以8就行(源码中是右移3位)size >> 3
但是要考虑到size等于8、16等的情况,所以为 (size - 1) >> 3
然后要考虑到为0的情况,所以源码中对于-1
的处理是!!size
,当size为0的情况!!0 = 0
。所以当size为0的情况就把-1
转换成了-0
,最终有了源码中的表达式 (size - !!size) >> 3
1 2 3 4 5 6 |
|
初看这个代码,容易一脸懵逼,这些t1 t2 都是啥啊
不过不用怕,我们一点点来分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
size = size - 1;
这个是边界情况,跟前面一样,后面出现的size暂且都认为已近减一了
假设不看这个源码,我们要实现在ZEND_MM_BINS_INFO
中找到对应的bin_num
由ZEND_MM_BINS_INFO
得知后续的增加4个为一组,分别为
1 |
|
有了这个分组信息的话,我们要找siez对应的bin_num
找到这个size属于哪一组
并且size在组内的偏移是多少
计算组的起始位置
那现在问题转换成了上面3个小问题,我们一个一个来解决
最简单的办法就是比大小是吧,可以使用if...else 来一个一个比,但是显然php源码不是这样干的,那我们还有什么其它的办法呢?
我们看十进制看不出来什么名堂,就把这些值转成二进制看看吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
我们看下上面的二进制,会发现每组的内的二进制长度相等,并且后面每个都比前面多一位
那就是说我们可以计算二进制的长度来决定它的分组,那么二进制的长度又是啥呢,其实就是当前二进制的最高位为1
的位数
那么问题又转换成了求二进制中最高位的1
的位数
下面给出php源码的解法,这里暂时不对其解析,只要知道它返回的是二进制中最高位的1
的位数
1 2 3 4 5 6 |
|
假设我们申请的size为65,那么这里的n返回7
这个简单,直接用size减去每组的起始siez大小然后除以当前组内的差值(16、32、64...)即可,也就是(size-64)/16 (size-128)/32 (size-256)/64
现在来看看上一步中的返回的值,每个组分别是7、8、9...
,那么我们现在来看看这样的数据怎么计算组内的偏移量
1 2 3 4 5 |
|
那是不是可以用7、8、9
减去3
得到4、5、6
,这样我们就可以根据它在哪一组的信息得到当前组的差值(16、32、64...)
当size为65时,偏移量是不是就是
1 |
|
现在我们有了偏移量的信息,假定我们分组是1、2、3
那是不是就是用最高位的1
的位数减去6
就可以得到分组信息了
得到分组信息之后,怎么知道每组的起始位置呢
我们知道起始位置分别是8、12、16...
它也是一个等差数列,就是4n+4
我们在看看size=65的那个例子
计算的偏移量是0
计算的起始位置是4*1 + 4 = 8
所以当size=65的bin_num就是起始位置加上偏移量 8 + 0 = 8
我们再看一个size=129的例子
二进制中最高位的1
的位数为8
然后用8减去3得到5
(129 - 1 - 32 * 4) / 64 = 0
偏移量是
计算起始位置是 4 * 2 + 4 = 12
两者相加就是 12 + 0 = 0
size=193
二进制中最高位的1
的位数为8
(193 - 1 - 32 * 4) / 64 = 2
偏移量是
计算起始位置是 4 * 2 + 4 = 12
两者相加就是 12 + 2 = 14
size=1793
二进制中最高位的1
的位数为11
(1793 - 1 - 256 * 4) / 256 = 3
偏移量是
计算起始位置是 4 * 5 + 4 = 24
两者相加就是 24 + 3 = 27
1 2 3 4 5 6 |
|
t1 = size - 1;
是为了考虑size为64、128...这些边界情况
t2 = zend_mm_small_size_to_bit(t1) - 3;
这里调用了zend_mm_small_size_to_bit
这个函数,我们看看这个函数
1 2 3 4 5 6 7 8 |
|
看注释我们就知道这个函数是用来返回当前size二进制中最高位1的位数,具体的做法呢其实就是二分法
我们通过zend_mm_small_size_to_bit
这个函数获取了size二进制中最高位1的位数,那么这个 -3
是什么神奇的操作呢
1 2 3 4 5 |
|
这里获取二进制的位数是7、8、9...通过 -3
的操作来获取相应的 4、5、6...
上问的分析中提到,我们计算size在组内的偏移量的公式
t1 = t1 >> t2;
把t1右移t2位,这又是什么神奇的操作?
这里我们把最后计算bin_num的数学公式给写出来,它是等于每组的起始位置加上组内的偏移量
1 2 3 |
|
所以第三行的意思我们就知道了,就是size右移2^n次方为
t2 = t2 - 3;
这个好理解,可以参照上文得到每组的起始位置的方法
t2 = t2 << 2;
我们再看看bin_num的计算公式
1 2 3 |
|
那么这行就好理解了,就是计算每组的起始位置4n
对吧,左移两位就是乘以4
return (int)(t1 + t2);
这行没啥说的,就是返回了一个int类型的bin_num