Golang利用位运算实现为程序加速

目录
  • 前言
  • 用位运算优化
  • 其他奇淫巧技
  • 总结

前言

最近在持续优化之前编写的 JSON 解析库 xjson,主要是两个方面的优化。

第一个是支持将一个 JSONObject 对象输出为 JSON 字符串。

这点在上个版本中只是利用自带的 Print 函数打印数据:

func TestJson4(t *testing.T)  {
	str := `{"people":{"name":{"first":"bob"}}}`
	first := xjson.Get(str, "people.name.first")
	assert.Equal(t, first.String(), "bob")
	get := xjson.Get(str, "people")
	fmt.Println(get.String())
	//assert.Equal(t, get.String(),`{"name":{"first":"bob"}}`)
}

Output:

map[name:map[first:bob]]

本次优化之后便能直接输出 JSON 字符串了:

实现过程也很简单,只需要递归遍历 object 中的数据,然后拼接字符串即可,核心代码如下:

func (r Result) String() string {
	switch r.Token {
	case String:
		return fmt.Sprint(r.object)
	case Bool:
		return fmt.Sprint(r.object)
	case Number:
		i, _ := strconv.Atoi(fmt.Sprint(r.object))
		return fmt.Sprintf("%d", i)
	case Float:
		i, _ := strconv.ParseFloat(fmt.Sprint(r.object), 64)
		return fmt.Sprintf("%f", i)
	case JSONObject:
		return object2JSONString(r.object)
	case ArrayObject:
		return object2JSONString(r.Array())
	default:
		return ""
	}
}

用位运算优化

第二个优化主要是提高了性能,查询一个复杂 JSON 数据的时候性能提高了大约 16%.

# 优化前
BenchmarkDecode-12         90013             66905 ns/op           42512 B/op       1446 allocs/op

# 优化后
BenchmarkDecode-12        104746             59766 ns/op           37749 B/op       1141 allocs/op

这里截取了一些重点改动的部分:

在 JSON 解析过程中会有一个有限状态机状态迁移的过程,而迁移的时候可能会出现多个状态。

比如当前解析到的 token 值为 {,那它接下来的 token 可能会为 ObjectKey:"name",也可能会是 BeginObject:{,当然也可能会是 EndObject:}
所以在优化之前我是将状态全部存放在一个集合中的,在解析过程中如果发现状态不满足预期的列表时则会抛出语法异常的错误。

所以优化之前是遍历这个集合来进行判断的,这样的时间复杂度为 O(N),但当我们换成位运算就不一样了,时间复杂度直接就变为O(1)了,同时还节省了一个切片的存储空间。

我们简单来分析下这个位运算为什么会达到判断一个数据是否在一个集合中同样的效果。

首先以这两个状态为例:

	StatusObjectKey   status = 0x0002
	StatusColon       status = 0x0004

他们分别对应的二进制数据为:

	StatusObjectKey   status = 0x0002 //0010
	StatusColon       status = 0x0004 //0100

当我们对这两个数据求 | 运算得到的数据是 0110

A:0010
B:0100

C:0110

这时候如何我们如果用这两个原始数据与 C:0110 做 & 运算时就会还原为刚才的两个数据。

// input:
A:0010
C:0110

// output:
A:0010

----------
// input:
B:0100
C:0110

// output:
B:0100

但我们换一个 D 与 C 求 & 时:

D: 1000 // 0x0008 对应的二进制为 1000
C: 0110
D':0000

将会得到一个 0 值,只要得出的数据大于 0 我们就能判断一个数据是否在给定的集合中了。

当然这里有一个前提条件就是,我们输入的数据高位永远都是是 1 才行,也就是2的幂。

同样的优化在解析查询语法时也有使用:

其他奇淫巧技

当然位运算还有一些其他技巧,比如判断奇偶数:

// 偶数
a & 1 == 0

// 奇数
a & 1 == 1

乘法和除法,右移1一位是除以2,左移一位是乘以2.

x := 2
fmt.Println(x>>1) //1
fmt.Println(x<<1) //4

总结

位运算在带来程序性能提升的同时也降低代码可读性,所以我们得按需选择是否使用;

再一些底层库、框架代码对性能有极致追求的场景推荐使用,但在业务代码中对数据做加减乘除就没必要用位运算了,只会让后续的维护者一脸懵逼。

到此这篇关于Golang利用位运算实现为程序加速的文章就介绍到这了,更多相关Golang位运算内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Golang 运算符及位运算详解

    什么是运算符? 运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.例如:2+3,其操作数是2和3,而运算符则是"+". 在vb2005中运算符大致可以分为5种类型:算术运算符.位运算符. 关系运算符.赋值运算符.逻辑运算符. 算数运算符 运算符 描述 + 相加 - 相减 * 相乘 / 相除 % 求余 注意: ++(自增)和--(自减)在Go语言中是单独的语句,并不是运算符. func main() { a, b := 3,4 fmt.Printf("a 加 b

  • golang简单位运算示例

    本文实例讲述了golang简单位运算.分享给大家供大家参考,具体如下: 复制代码 代码如下: // http://play.golang.org/p/idG7Ri_krU package main import "fmt" func main() {     var n uint8 = 6     fmt.Printf("%s\n%08b\n%08b\n\n", "6<<1 左移1位", n, n<<1)     fmt.

  • Golang利用位运算实现为程序加速

    目录 前言 用位运算优化 其他奇淫巧技 总结 前言 最近在持续优化之前编写的 JSON 解析库 xjson,主要是两个方面的优化. 第一个是支持将一个 JSONObject 对象输出为 JSON 字符串. 这点在上个版本中只是利用自带的 Print 函数打印数据: func TestJson4(t *testing.T) { str := `{"people":{"name":{"first":"bob"}}}` first

  • PHP巧妙利用位运算实现网站权限管理的方法

    首先我们先定义4个常量来设定四种权限: ===================================== define(ADD,1);//增加数据库记录的权限 define(UPD,2);//修改数据库记录的权限 define(SEL,4);//查找数据库记录的权限 define(DEL,8);//删除数据库记录的权限 ===================================== 接下来假设有3个用户: A用户拥有ADD-UPD-SEL-DEL四个权限,用位或运算计算A的

  • Java利用位运算实现加减乘除的方法详解

    目录 前言 一.常见位运算 1. &运算 2. |运算 3. ^运算 4. ~运算 二.位运算实现加法 三.位运算实现减法 四.位运算实现乘法 五.位运算实现除法 前言 我们经常使用的加减乘除,我们所看到的只是表面的效果,那么加减乘除在底层究竟是怎么实现的?今天就让我们一探究竟.今天用位运算实现的加减乘除不使用任何的加减乘除符号. 一.常见位运算 1. &运算 &运算二进制每一位全1为1,否则为0 public static void main(String[] args) { i

  • Java利用位运算实现比较两个数的大小

    目录 题目要求 主要思路 方法1(不考虑溢出) 方法2(考虑溢出情况) 题目要求 如何不要用任何比较判断符(>,==,<),返回两个数( 32 位整数)中较大的数. 主要思路 方法1(不考虑溢出) 要比较 a 和 b 的大小,因为不能用比较符号,我们可以通过 a - b 的符号位来判断,如果 a - b 的符号位是 1,说明 a - b < 0,则 a 小,否则 a 大或者 a 和 b 相等. 如何判断一个数的符号位是 0 还是 1 ? 由于是 32 位整数,所以如果将一个数右移 31

  • Java利用位运算实现加减运算详解

    目录 前言 思路分析 示例 位运算进位 初步结果 去除加号 整体思路 加法代码实现 减法实现 减法分析 减法代码实现 总结 前言 本文主要介绍如何使用位运算来实现加减功能,也就是在整个运算过程中不能出现加减符号. 加减乘除运算在计算机中,实际上都是用位运算实现的,今天就用位运算来模拟下加法和减法的运算功能. 思路分析 先分析如何用位运算实现加法运算. 示例 假设a=23,b=36,使用位运算实现加法得到结果59. 首先来看下23.36.59的二进制信息. 从上面的图中可以看到,两个数相加的结果与

  • Java利用位运算实现乘法运算详解

    目录 前言 正文 十进制相乘 二进制相乘 思路分析 代码实现 总结 前言 在上一篇中,我们介绍了使用位运算实现加法和减法运算,接下来本文主要介绍如何用位运算实现乘法运算,在实现乘法时要用位运算实现,并且不能出现加减乘除任何符号. 之前介绍过一篇如何用位运算实现加法和减法: 如何用位运算实现加减运算? 正文 在用位运算实现之前,我们先来回忆一下小学时,学乘法时用的十字相乘法. 十进制相乘 例如,26 * 15,在进行乘法操作时,我们一般这样算,先用5乘以6得到30,把0写下把3记在一边,再用5乘以

  • JAVA位运算的知识点总结

    一.在计算机中数据是如何进行计算的? 1.1:java中的byte型数据取值范围 我们最开始学习java的时候知道,byte类型的数据占了8个bit位,每个位上或0或1,左边第一位表示符号位,符号位如果为1表示负数,为0则表示正数,因此要推算byte的取值范围,只需要让数值位每一位上都等于1即可. 我们来用我们的常规思维来分析下byte类型的取值范围: 图1 如果按照这种思路来推算,七个1的二进制数转换为十进制是127,算上符号位,取值范围应为:-127~+127,但事实上我们知道,byte的取

  • 如何利用C语言位运算解决只出现一次的数字

    解题所需要的C语言基础知识 hello!从现在开始就进入本题解的正式内容了.首先给大家用图解的方式介绍3个C语言位运算的基本操作符 & | ^ 这些知识对下面的解题都非常重要,一定要熟练掌握,不然等会会有一种"我在哪,我是谁我在干什么"的感觉. 只出现一次的数字I 题目描述 只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 示例 1:

随机推荐