1.应用场景对于高精度的采样结果,最大值可能需要3个字节,最小值需要1个字节。使用标准C基本数据类型,U16太小满足不了需求,U32浪费内存。当样本量很大时,其占用空间的问题就变得突出。变长数据类型可以用来存储吗?小数据使用U8,大数据使用U32,根据值的大小动态分配存储空间,这是本文的重点。2、数据去冗余的U32空间的最大值范围接近2^32,很大,实际取值范围远小于它,高位必须为0。例如U32用0x00000001表示1,前面的数字都是0,表示的值和U8的0x01一样。前面重复的一串0属于冗余数据区,可以去掉。假设5个数据D0..4,每个数据原本固定为U32类型,去掉其高位冗余0,再拼接成U8的一维数组,占用空间会大大减少。其思想核心是将U32或U64数组裁剪拼接成U8数组,同时保证在使用时能根据U8数组中存储的信息还原出对应的值。假设有0x00000001、0x00000101、0x00000001三个数据,其有效部分分别为0x01、0x0101、0x01。如果直接拼接在一起,无法区分0x01010101的含义。因此,去除高位0后,需要对数据进行编码标记,方便后续分析还原。3、数据编码数据编码的主要作用是标记当前数据占用了多少个连续字节。有两种选择:1.固定位定义字节长度(2位可以代表4个字节)一个字节:00******两个字节:01******,00******三字节:10******,01******,00******四字节:11******,10******,01******,00******五字节:每字节高2位使用2位不支持该位表示第几位属于原始数据(从0开始)。前面例子中的3个字节可以表示为:0x01编码二进制为00-000001,最高2位为0,表示当前为编码数据的最后一个字节;0x0101编码后的二进制是01-000001--00-000001。解析时,每个字节取2位进行判断。如果为00,表示一个编码值结束。因为前2位是固定标记字节数的,所以每个字节实际可用的范围只有6位。如果原来的数据位是10000001,那么10的最高两位需要另外占用一个字节来表示,最终编码为01-000010--00-000001。在这种编码方式中,所有字节的有效位都是固定的,很容易实现编解码。缺点是4个字节只有24位有效数据。如果原始数据高达25位,那么每个字节分配3位来表示,但是这种大数据一般很少用于嵌入式。2、字节的最高位表示还有剩余数据,参考UTF8的编码方式。一个字节:0*********两个字节:110******、10******三字节:1110******、10******、10******四个字节:11110***、10******、10******、10******五个字节:111110**、10******、10******,10******,10******六个字节:1111110*,10******,10******,10******、10******、10******七个字节:不支持这种编码方式,最高字节有效位可变,其他字节有效位为6位。两种编码方式的选择主要是根据原始数据的分布概率。如果原始数据范围在24位以内,则以前面的固定位方式为主。如果超过32位,动态的比较合适。如果数据范围在16位以内,那就没必要这么挑剔了。源码或更多交流,请关注微信公众号嵌入式系统。4.数据访问原始数据的每个值占用一个固定的字节长度,使用数组下标可以方便的遍历,即地址偏移量为(单个数占用的字节数)*(数),编码asvariable-lengthdata最后,如果要得到某个原始数据的编码值,从数组开头遍历是相当低效的。有没有更好的办法?将之前的一维数组转换为二维数组,数组的每一行按前面的编码实现,数据中预留4个字节,每行满时,尾标示结束当前行指示包含多少原始数据,下一个编码值存储在下一行,依此类推。如上图所示,二维数组的一行退化为一维数组,每一行标记固定位置存储的数量。如果需要查找C10,首先根据标号的字节地址遍历,可以发现第二行(从0开始)是13,也就是说要查找的数据就在这一行,你只要需要遍历这一行,从C9查询。5.总结通过选择合适的数据类型来减少存储空间,对于大规模数据使用变长类型的拼接存储,牺牲了一部分时间,但是节省了ram或者flash空间,对于嵌入式设备具有一定的优势稀缺资源价值。本文转载自微信公众号“嵌入式系统”,可通过以下二维码关注。转载本文请联系EmbeddedSystems公众号。
