foreword
The parsing library xjson , which was recently written before continuous optimization JSON
is mainly optimized in two aspects.
The first is to support outputting a JSONObject
object as a JSON
string.
In the previous version, only the built-in Print
function was used to print data:
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]]
<!--more-->
After this optimization, the JSON string can be output directly:
The implementation process is also very simple. You only need to recursively traverse the data in the object, and then splicing strings. The core code is as follows:
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 ""
}
}
Optimizing with bitwise operations
The second optimization is mainly to improve performance. When querying a complex JSON data, the performance is improved by about ⏫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
Here are some of the key changes:
During the JSON parsing process, there will be a finite state machine state migration process, and there may be multiple states during the migration.
For example, the currently parsed token value is {
, then its next token may be ObjectKey:"name"
, or BeginObject:{
, of course, it may be EndObject:}
,
So before optimization, I stored all the states in a set. During the parsing process, if the state does not meet the expected list, a syntax exception will be thrown.
So before optimization, we traverse this set to make judgments. This time complexity is O(N)
, but when we change to bit operation, it is different, and the time complexity directly becomes O(1)
, while saving a slice of storage space.
Let's simply analyze why this bit operation achieves the same effect of judging whether a data is in a set.
First, take these two states as an example:
StatusObjectKey status = 0x0002
StatusColon status = 0x0004
Their corresponding binary data are:
StatusObjectKey status = 0x0002 //0010
StatusColon status = 0x0004 //0100
When we calculate these two data |
, the obtained data is 0110
:
A:0010
B:0100
C:0110
At this time, if we use these two original data and C:0110
to do &
operation, it will be restored to the two data just now.
// input:
A:0010
C:0110
// output:
A:0010
----------
// input:
B:0100
C:0110
// output:
B:0100
But when we change D and C to find &
:
D: 1000 // 0x0008 对应的二进制为 1000
C: 0110
D':0000
A 0 value will be obtained, as long as the resulting data is greater than 0, we can judge whether a data is in the given set.
Of course, there is a precondition here that the high bit of the data we input is always 1, which is a power of 2.
The same optimization is used when parsing query syntax:
Other kinky tricks
Of course, there are some other tricks for bit operations, such as judging parity:
// 偶数
a & 1 == 0
// 奇数
a & 1 == 1
Multiplication and division, right shift by 1 is divided by 2, left shifted by one is multiplied by 2.
x := 2
fmt.Println(x>>1) //1
fmt.Println(x<<1) //4
Summarize
Bit operations not only improve program performance, but also reduce code readability, so we have to choose whether to use it or not;
Some low-level libraries and framework codes are recommended for use in scenarios with extreme pursuit of performance. However, it is not necessary to use bit operations to add, subtract, multiply and divide data in business code, which will only make subsequent maintainers confused.
Relevant code: https://github.com/crossoverJie/xjson
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。