详解varint,zigzag编码, 以及在Go标准库中的实现

文章目录

  • 为啥需要varint编码
  • 为啥需要zigzag编码
  • varint
    • 编码
    • 解码
  • zigzag
    • 编码
    • 解码
  • 局限性

为啥需要varint编码

当我们用定长数字类型int32来表示整数时,为了传输一个整数1,我们需要传输00000000 00000000 00000000 00000001 32 个 bits,而有价值的数据只有 1 位。这就导致了大量的空间浪费,因为大部分字节并没有实际存储有效的信息

varint编码通过使用可变长度的字节序列来表示整数,根据数字的大小灵活占用的空间。这样使得小的整数可以用更少的字节表示,提高空间效率

下面是varint编码中,正数的大小和需要的字节数的关系

数字大小uvarint编码需要的字节数
<=1271
<=163832
<=20971513
<=2684354554

我们业务中大部分数据的size都 <= 16383,也就只用1或2个字节。相比于定长的int32,int64能节省不少空间

其设计原理为:

  • 每7bit 为一组:将整数的二进制按照每7个bit划分到一个byte中
  • 最高位表示是否还有下个字节:划分好的byte中,如果最高位为1,表示还有下个byte,否则当前byte是最后一个。最后一个字节的最高位为1,其他字节的最高位为0

在这里插入图片描述

例如:对于一个整数 500,它的二进制表示是 111110100。将其分为2组,即 111110100。然后在每组前面添加标志位,得到两个字节 1000001101110100,这两个字节就是 500 的 varint 编码。相比于用 int32 的 4 字节表示,节省了 50% 的存储空间


为啥需要zigzag编码

但如果是负数,那么继续采用Varint编码就没有任何压缩效果,甚至占用更多字节。因为负数的符号位最高位为1,也就是一定会用满最大的字节

ZigZag编码解决了varint对负数编码效率低的问题,其原理为:

  • 对于正数 n,会将其映射为 2 * n。例如整数 2,经过 zigzag 编码之后变成 4
  • 对于负数 -n 来说,会将其映射为 2 * n-1。例如负数 -3,经过 zigzag 编码之后变成了 2 * 3 - 1 = 5
nzigzag编码后
00
-11
12
-23
24
-35
36

例如:举个极端的例子-1,如果不用zigzag编码,直接用varint,那么会用10个字节。如果先zigzag变成1,再varint,只会用1个字节

接下来阅读golang标准库中如何对varint和zagzig进行编码和解码的


varint

编码

将x以varint的形式写入buf中,返回写了多少个字节

由于是每7位用一个字节存储,那么只要大于等于10000000,也就是需要超过7位,就需要先把低7位存到buf[i]中

for循环中:buf[i] = byte(x) | 10000000,这行是保留低7位,并且把buf[i]的第8位强制置为1

最后一个字节的最高位为0

func PutUvarint(buf []byte, x uint64) int {i := 0// Ox80 = 10000000for x >= 0x80 {// buf[i] = x的低8位 | 10000000buf[i] = byte(x) | 0x80// 移除低7位x >>= 7// 需要用到的字节数 + 1i++}// 最后一个字节buf[i] = byte(x)return i + 1
}

解码

传入需要解码的字节序列,返回解码后的数字,以及其占用了字节序列中前多少字节

func Uvarint(buf []byte) (uint64, int) {var x uint64var s uintfor i, b := range buf {if i == MaxVarintLen64 {return 0, -(i + 1) // overflow}// b < 10000000,也最高位为0,代表是就是最后一个字节if b < 0x80 {if i == MaxVarintLen64-1 && b > 1 {return 0, -(i + 1) // overflow}// x | 最高的7位,返回return x | uint64(b)<<s, i + 1}// 最高位为1,表示后面还有字节// 提取这个字节的前7位:b & 01111111x |= uint64(b&0x7f) << ss += 7}return 0, 0
}

注意:如果要解码成uint64,一共64位,按7位一组,最多10组且第10组最大为1(第64位)

对应到源码中判断,如果超过10组或第10组大于1了,就返回溢出


zigzag

编码

我们知道在zigzag编码中:

  • 如果x是正数,按照2 * x的varint编码

  • 如果x是负数,假设其值为-n,就按照2 * n - 1的varint编码

对照源码看看:

func PutVarint(buf []byte, x int64) int {ux := uint64(x) << 1if x < 0 {ux = ^ux}return PutUvarint(buf, ux)
}

如果x是正数,等于x << 1的varint编码,没问题

如果x是负数,这里的操作是 x << 1,再按位取反

go中x = ^x代表对x按位取反

这就有些难以理解了,为啥 ^(x << 1)等于 2 * n - 1

我们先看看负数的二进制怎么表示

要计算-n的二进制表示,先计算n-1,再按位取反

那么反过来,给定一个负数的二进制x,怎么得到n是多少(也就是负多少)?就是把上面的操作反过来,先按位取反,再+1,也就是n = ^x + 1

我们目的是要得到 n * 2 - 1 ,把上面的式子带进去,得到 2 * (^x + 1) - 1 = 2 * ^x + 1

而对一个数先取反,再乘2,再加1,等于对这个数先乘2,再取反

2 * ^x + 1 = ^(2 * x)

举个例子,假设x = 10010:
在这里插入图片描述

为啥这个等式成立呢?因右边2 * x后,最低位变成0了,此时再取反的到的值,相比于先对x取反再*2得到的值来说,最低位多了个1。也就是后取反的话,比先取反多把末尾的0翻转成1了

于是得到n * 2 - 1 = ^(2 * x),对应了源码中对负数的编码


解码

如果varint中存的是偶数,那么原始值就是正数,值为ux / 2

如果varint中存的是奇数,那么原始值就是负数,那么值是多少呢?

ux / 2得到的值是n-1,最终要得到-n

我们先看n怎么得到-n?n-1再按位取反

而现在已经有n-1了,直接按位取反即可

func Varint(buf []byte) (int64, int) {ux, n := Uvarint(buf) x := int64(ux >> 1)// 负数 x = n-1,要得到-n,按位取反即可if ux&1 != 0 {x = ^x}return x, n
}

局限性

注意不是所有场景都适合用varint编码:

  1. 当数值比较大时:例如用满了int64的64位,那么在varint中会用到10位,反而比定长编码多了用了20%的空间
  2. 需要随机访问时:例如一个varint数组,要随机访问下标i的值。此时就不适合用任何变长编码的数据了。因为要随机访问的前提是每个元素的长度是定长的,这样才能根据公式 i * 定长,随机访问到特定的内存空间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458546.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】STL初识

【C】STL初识 文章目录 【C】STL初识前言一、STL基本概念二、STL六大组件简介三、STL三大组件四、初识STL总结 前言 本篇文章将讲到STL基本概念&#xff0c;STL六大组件简介&#xff0c;STL三大组件&#xff0c;初识STL。 一、STL基本概念 STL(Standard Template Library,标准…

QT建立工程时出现了:Reading Project

QT建立工程时出现了&#xff1a;Reading Project 打开建立的工程发现&#xff0c;缺少build文件 从别的工程中复制一个build&#xff0c;点击.pro就可以打开了

【CSS3】css开篇基础(4)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

Spring Boot实现的动态化酒店住宿管理系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理酒店客房管理系统的相关信息成为必然。开发…

图文详解ChatGPT-o1完成论文写作的全流程

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 本月中旬OpenAI发布了OpenAI o1系列新的AI模型。 据OpenAI介绍&#xff0c;这些模型旨在花更多时间思考后再做出反应&#xff0c;就像人一样。通过训练&#xff0c;它们学会改进思维过…

深度学习(六)CNN:图像处理的强大工具(6/10)

一、CNN 的概述 卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;是深度学习的代表算法之一&#xff0c;在深度学习中占据着重要地位。 CNN 的发展历程可追溯至 20 世纪 80 至 90 年代&#xff0c;时间延迟网络和 LeNet - 5 是最早出现的卷…

conda虚拟环境中安装cuda方法、遇到的问题

conda虚拟环境中安装cuda方法、遇到的问题 文章目录 conda虚拟环境中安装cuda方法、遇到的问题conda虚拟环境中安装cudacuda.h和cuda_runtime.hpytorch运行时的CUDA版本其他问题检查包冲突nvcc -V和nvidia-smi显示的版本不一致cuda路径 conda虚拟环境中安装cuda 参考文章&…

【AIGC】从CoT到BoT:AGI推理能力提升24%的技术变革如何驱动ChatGPT未来发展

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;迈向AGI的新跨越&#x1f4af;BoT与CoT的技术对比技术原理差异推理性能提升应用范围和通用性从错误中学习的能力总结 &#x1f4af;BoT的工作流程和机制初始化过程生成推…

layaair获取组件里的脚本

获取脚本用getComponents方法&#xff0c;但是这个方法里的参数不是脚本的名称。而是组件类型。如果你需要获取脚本&#xff0c;则类型为Laya.Script。挺坑的。我在官网找都没找到这个是这么用的。我猜测的。没想到试了一下成功了。 property(Laya.Node)public img1: Laya.Node…

碰一碰支付系统搭建怎么做?头部公司源码大测评!

随着碰一碰支付dai li骗局的曝光&#xff0c;越来越多的人开始选择将目光转向碰一碰支付系统搭建这一入局方式&#xff0c;连带着与之相关的多个话题&#xff0c;如碰一碰支付系统搭建怎么做等也成为了当前的一大热点。 毕竟&#xff0c;相较于dai li 模式的与第三方公司合作、…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-26

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-26 前言 本期相关论文可以从“下载” 资源中获取&#xff0c;如果有感兴趣的问题&#xff0c;欢迎交流探讨&#xff01; 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-26前言目…

【C++进阶】C++11(上)

【C进阶】C11(上) &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;C&#x1f96d; &#x1f33c;文章目录&#x1f33c; 1. C11的发展史 2. 列表初始化 2.1 C98的传统{} 2.2 C11中的{} 2.3 C11中的std::initializer_list 3. 右值引用…

Kaggle竞赛——灾难推文分类(Disaster Tweets)

目录 1. 准备工作2. 资源导入3. 数据处理4. 绘制词云图5. 数据可视化5.1 词数和字符数可视化5.2 元特征可视化5.3 类别可视化 6. 词元分析6.1 一元语法统计6.2 多元语法统计 7. 命名实体识别8. 推文主题提取9. 构建模型9.1 数据划分与封装9.2 模型训练与验证 10. 模型评估11. 测…

jvm虚拟机介绍

Java虚拟机&#xff08;JVM&#xff09;是Java语言的运行环境&#xff0c;它基于栈式架构&#xff0c;通过加载、验证、准备、解析、初始化等类加载过程&#xff0c;将Java类文件转换成平台无关的字节码&#xff0c;并在运行时动态地将其翻译成特定平台的机器码执行。 JVM的核心…

App测试环境部署

一.JDK安装 参考以下AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载 二.SDK安装 安装地址&#xff1a;https://www.androiddevtools.cn/ 解压 环境变量配置 变量名&#xff1a;ANDROID_SDK_HOME 参考步骤&#xff1a; A…

K8s中TSL证书如何续期

TSL是什么 K8s中的作用是什么&#xff1f; 在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;TSL 指的是 Transport Layer Security&#xff0c;也就是传输层安全协议。它是用来保护在网络上传输的数据的安全性和隐私性。 TSL 在 Kubernetes 中的作用包括&#xff1a;…

铜业机器人剥片 - SNK施努卡

SNK施努卡有色行业电解车间铜业机器人剥片 铜业机器人剥片技术是针对传统人工剥片效率低下、工作环境恶劣及生产质量不稳定的痛点而发展起来的自动化解决方案。 面临人工剥片的诸多挑战&#xff0c;包括低效率、工作环境差、人员流动大以及产品质量控制不精确等问题。 人工剥片…

OSPF特殊区域及其他特性

不用的链路这状态信息没必要一直保存&#xff0c;要不路由器承受不了。用OSPF 特殊区域解决 1. Stub区域和Totally Stub区域 R1作为ASBR引入多个外部网段&#xff0c;如果Area 2是普通区域&#xff0c;则R3将向该区域注入5类和4类LSA。 当把Area 2配置为Stub区域后&#xff1a…

51单片机之蜂鸣器驱动

1.简介 蜂鸣器是一种一体化结构的电子讯响器&#xff0c;采用直流电压供电&#xff0c;广泛应用于计算机、打印机、 复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。   压电式蜂鸣器主要…