zoukankan      html  css  js  c++  java
  • 48 【golang】json的效率

    本文将主要做如下几方面的测试:

    1,构造一个[100]struct的数组,然后来测试它的json编码后的字符串 或者([]byte),首先关心它的功能是否正常;

    2,在很早之前,我们在使用golang版本的json编解码时,发现:同PHP的json编解码相比,golang的效率似乎要低,而且要低不少;

    3,基于2的背景,我们希望测试新的golang版本中json编码方面是否有所提升,比较的版本是:Go1.3.3,Go1.9;

    4,我们可以用程序的一次性执行来测试它的功能,但是如果多次测试,我们需要构造一个多次测试的环境;

    关于3,我们需要做点功课,在同一台机器上部署两个版本的go:https://blog.csdn.net/min2015/article/details/77913910,这个博客或许能有所帮助。

    另附:go的各个版本下载地址:https://golang.org/dl/

    关于4,我们也需要做点功课,如何能用go test工具来辅助我们处理。这篇文章或许能帮助到我们:http://www.flysnow.org/2017/05/21/go-in-action-go-benchmark-test.html

    实验环境准备:

    1,创建一个目录,随便一个目录就行,比如我们叫json:mkdir json;

    2,构造两个go文件:json.go, json_test.go

    json.go代码如下:

    package myjson
    
    import "fmt"
    import "math/rand"
    import "time"
    //import "os"
    import "strconv"
    import "encoding/json"
    
    type user struct {
        Name string `json:"nameabcd"`
        Age  int    `json:"age"`
        Desc string `json:"desc"`
    }
    
    func generate() {
        var userRows []user = []user{}
        
        var u user
        for i := 1; i < 100; i++ {
            u.Age = 10
    
            is := strconv.Itoa(i)
            u.Name = "name" + is
            
            u.Desc = GetText(300)
            
            userRows = append(userRows, u)
            
            u = user{}
        }
        
        _, _ = json.Marshal(userRows)
        //fmt.Println(string(o))
        //os.Exit(1)
        
        //构造100个struct
        //构造每一个struct: name, age, desc
        // name = name + index
        // age = rand(10, 40)
        // desc = getRandomText(300)    
        // json.Encode(struct100)
    }
    
    
    var _ = fmt.Sprintf("")
    
    func GetText(slen int) string {
        character := "abcdefghjkmnpqrstuvwxyABCDEFGHJKLMNPQRSTUVWXYZ023456789,:.;-+";
        maxlen := int32(len(character))
    
        var result string
        rand.Seed(time.Now().UnixNano())
        for i:= 0; i < slen; i++ {
            idx := rand.Int31n(maxlen)
            result += (string)(character[idx])
        }
        return result
    }

    json_test.go代码如下:

    package myjson
    
    import "testing"
    
    func BenchmarkJson(b *testing.B) {
        for i := 0; i < b.N; i++ {
            generate()
        }
    }

    测试1:直接用go benchmark 来测试BenchmarkJson方法。

    $ go test -bench=. -run=none
    goos: linux
    goarch: amd64
    BenchmarkJson          300       4361737 ns/op
    PASS
    ok      _/home/luwenwei/go/maps/1    1.761s

    测试2:使用go benchmark的mem监控工具来测试BenchmarkJson方法。

    $ go test -bench=. -benchmem -run=none
    goos: linux
    goarch: amd64
    BenchmarkJson          300       4466717 ns/op     4957588 B/op       59417 allocs/op
    PASS
    ok      _/home/luwenwei/go/maps/1    1.802s

    可以看到带有-benchmem的选项时,在benchmark测试时能够知道内存分配的次数和内存大小。

    测试3:作为和测试2的对比组,用go1.3.3来做测试。

    1$ ~/download/go1.3.3/go/bin/go test -bench=. -benchmem -run=none
    warning: GOPATH set to GOROOT (/home/luwenwei/download/go1.3.3/go) has no effect
    testing: warning: no tests to run
    PASS
    BenchmarkJson         200       8994506 ns/op     5078644 B/op       44185 allocs/op
    ok      _/home/luwenwei/go/maps/1    2.645s

    测试对比结果如下:

      测试2(Go1.9) 测试3(Go1.3.3)
    内存分配次数 59147 44184
    内存分配大小(B) 4957588 5078644
    每次操作耗时 4.4ms 8.99ms

    测试结果对比情况说明:go1.9的内存分配次数更多,但是每次操作耗时更短,依赖于go的版本升级(猜测:对json处理的优化,split hot stack的问题,由分配栈做link改为连续的内存)

    而且这个对比结果还说明了,我们的程序有优化空间,100次的struct构建,却要花费44万次内存分配我们觉得有点扯。

    基于这个有点扯的直觉,我们对程序进行了优化。

    优化的重点放在了,GetText函数。

    优化后的代码:

    func GetText(slen int) string {
        character := "abcdefghjkmnpqrstuvwxyABCDEFGHJKLMNPQRSTUVWXYZ023456789,:.;-+";
        maxlen := int32(len(character))
    
        var r []byte = make([]byte, slen)
        //var result string
        rand.Seed(time.Now().UnixNano())
        for i:= 0; i < slen; i++ {
            idx := rand.Int31n(maxlen)
            r[i] = character[idx]
            //result += (string)(character[idx])
        }
        return (string)(r)
        //return result
    }

    优化后的测试结果。

    测试4:go1.9对优化再次benchmark。

    $ go test -bench=. -benchmem -run=none
    goos: linux
    goarch: amd64
    BenchmarkJson         1000       1836617 ns/op      171504 B/op         314 allocs/op
    PASS
    ok      _/home/luwenwei/go/maps/1    2.028s

    测试5:go1.3.3.对优化再次benchmark

    $ ~/download/go1.3.3/go/bin/go test -bench=. -benchmem -run=none
    warning: GOPATH set to GOROOT (/home/luwenwei/download/go1.3.3/go) has no effect
    testing: warning: no tests to run
    PASS
    BenchmarkJson        1000       2221624 ns/op      171772 B/op         268 allocs/op
    ok      _/home/luwenwei/go/maps/1    2.455s

    测试5,测试4,和之前的测试3,测试2,对比,每个op耗时更短,从测试2的4.4ms,降低到测试4的1.8ms。这个优化带来的效果是显著的,甚至比版本的升级带来的速度提升更明显。

      测试2(Go1.9) 测试3(Go1.3.3) 测试4-优化(Go1.9) 测试5-优化(Go1.3.3)
    内存分配次数 59K 44K 314 268
    内存分配大小 4.9MB 5MB 171KB 171KB
    每次操作耗时 4.4ms 8.99ms 1.8ms 2.2ms

    从程序的维度来看,这次优化带来的提升是显著的,在动态内存分配上[]byte要优于string的附加;

    从Go版本的维度来看,Go1.9比Go1.3.3对json的编码速度而言,更有优势;

    从工具的维度来看,go test更偏向于对功能的测试,go test benchmark更偏向于对程序的性能测试,而且benchmark是个很好的工具,它能够胜任较简单的模块测试,帮助我们发现模块中的性能问题。

    如果对整个project做性能测试,pprof工具要更好,如果怀疑某个模块有问题,再在这个模块上使用benchmark来精准定位,是个合理的性能瓶颈定位方法。

    附录:

    关于golang的json,我们可能想了解更多:https://gobyexample.com/json

  • 相关阅读:
    mysql在CentOS6.3上安装
    hdfs高可用性(HDFS High Availability)
    如何做个好员工
    lock(3)——更新锁(U)、排它锁(X)、死锁及如何避免死锁
    锁(1)—— 锁粒度和层次结构
    lock(2)——创建及更新表过程中SQL SERVER锁资源分配情况
    HBase体系结构
    HDFS的shell操作
    Windows 使用 net use 命令
    Windows 使用 net 命令简介
  • 原文地址:https://www.cnblogs.com/helww/p/10191877.html
Copyright © 2011-2022 走看看