一: 背景
在编程语言中各种数据类型底层存储的时候都是使用二进制的编码方式进行存储的,那么存储方式的不同必然会导致性能的不同。不同的数据类型使用的字节也是不同的,比如int32占用4个字节,int64占用8个字节等。
在二进制编码中正数的高位存储0,负数的高位存储1。
那么如int32数值1,使用的是4个字节,每个字节8bit,所以需要32bit。二进制表示为0000 0000 0000 0000 0000 0000 0000 0001。可以看到除了最低位是1,其他位均为0。从字节的角度来说,除了最后一个字节有效表示外,前3个字节都是无效存储。而且在实际的使用场景中,我们用int类型来定义数值的时候很多部分是用来表示小数值的,那么就更加有必要进行一些优化存储
二:概述
Varints算法:将二进制表示进行7位分组,然后针对每一组进行小端排序,即倒序。然后在每一组的高位进行补数,补数的原则是如果下一个字节有效,高位补充1,如果下一个字节无效,高位补充0。
三:示例
举例1:int32数值1的Varints编码方式:
1. 将定长位图按7位分组
0000
000 0000
000 0000
000 0000
000 0001
2.小端排序
000 0001
000 0000
000 0000
000 0000
0000
3.每一个字节补充高位,如果下一个字节有效,高位补充1,如果下一个字节无效,高位补充0.有效的判断取决于7位中是否有1
0 000 0001
0 000 0000
0 000 0000
0 000 0000
0 0000
4.从前往后,高位为1的时候观察下一个字节的高位,高位为0的时候,保留至当前高位,之后不在观察
0 000 0001
5.转换结束,可以看到,这里我们只用一个字节就可以表示数值1,节省了3个字节的空间
优劣:上面可以看到在原来的定长字节表示中8个字节一位可以表示0-127,那么在varints中是7位分组的方式,8位高位用来表示后续是否还有有效字节,所以在定长表示中4个字节中有32位可以用来表示数值大小,在varints中4个字节只有28位可以用来表示数值大小,所有需要5个字节才能满足int32的最大值
举例2:定长二进制表示是 0000 0000 0000 0000 0110 1011 0110 0011:
1. 7位分组
0000
000 0000
000 0001
101 0110
110 0011
2.小端排序
110 0011
101 0110
000 0001
000 0000
0000
3,高位补位
1 110 0011
1 101 0110
0 000 0001
0 000 0000
0 0000
4,结果
1110 0011 1101 0110 0000 0001
注:上面的例子表示都是基于正数的基础上分析的,如果是负数的化如下
int类型占4个字节,32位,其中最高位是符号位,为0表示的是正数,为1表示的是负数,所以实际可以存储的数据范围是-232---->2(32-1)
如果负整数使用varints来进行编码,那么因为高位是1,所以在小端反转之后,每一个字节都需要保留,原来定长4个字节使用varints之后转成5个字节,没有节省反而增加了字节占用
示例:-300 的二进制表示:1000 0000 0000 0000 0000 0001 00101100
1.7位分组
1000
000 0000
000 0000
000 0010
010 1100
2.小端排序
010 1100
000 0010
000 0000
000 0000
1000
3,高位补位
1 010 1100
1 000 0010
1 000 0000
1 000 0000
0000 1000
4,结果
1010 1100 1000 0010 1000 0000 1000 0000 0000 01000
如上转换完之后需要5个字节,如果是int64,那么就需要10个字节
所以在实际中varints只会应用于正整数,对于负数会使用 Zigzag 算法