一尘不染

在Go中加速JSON解析

go

我们有事务日志文件,其中每个事务都是JSON格式的一行。我们经常需要获取数据的选定部分,执行一次时间转换,然后以特定格式将结果馈送到另一个系统中。我编写了一个Python脚本,可以根据需要执行此操作,但是我希望Go可以更快,并且可以给我一个开始学习Go的机会。因此,我写了以下内容:

package main
import "encoding/json"
import "fmt"
import "time"
import "bufio"
import "os"

func main() {

    sep := ","

    reader := bufio.NewReader(os.Stdin)

    for {
        data, _ := reader.ReadString('\n')
        byt := []byte(data)

        var dat map[string]interface{}

        if err := json.Unmarshal(byt, &dat); err != nil {
            break
        }

        status := dat["status"].(string)
        a_status := dat["a_status"].(string)
        method := dat["method"].(string)
        path := dat["path"].(string)
        element_uid := dat["element_uid"].(string)
        time_local := dat["time_local"].(string)
        etime, _ := time.Parse("[02/Jan/2006:15:04:05 -0700]", time_local)
        fmt.Print(status, sep, a_status, sep, method, sep, path, sep, element_uid, sep, etime.Unix(), "\n")
    }
}

编译时没有任何抱怨,但是令我惊讶的是缺乏性能改进。为了进行测试,我将2,000,000行日志放入tmpfs(以确保磁盘I /
O不受限制),并比较了脚本的两个版本。我的结果:

$ time cat /mnt/ramdisk/logfile | ./stdin_conv > /dev/null 
real    0m51.995s

$ time cat /mnt/ramdisk/logfile | ./stdin_conv.py > /dev/null 
real    0m52.471s

$ time cat /mnt/ramdisk/logfile > /dev/null 
real    0m0.149s

如何使其更快?我做了一些初步的努力。例如,ffjson项目提议创建静态函数,这些函数不需要反射。但是,到目前为止,我无法使它正常工作,并出现以下错误:

Error: Go Run Failed for: /tmp/ffjson-inception810284909.go
STDOUT:

STDERR:
/tmp/ffjson-inception810284909.go:9:2: import "json_parse" is a program, not an importable package

:

此外,我上面所说的不是静态类型吗?可能不是-
我正积极地向Go滴下耳朵。我尝试有选择地禁用Go代码中的不同属性,以查看是否特别有问题。没有一个对性能有明显的影响。关于提高性能的任何建议,还是仅仅是编译后的语言没有比其他语言有实质性好处的情况?


阅读 272

收藏
2020-07-02

共1个答案

一尘不染

尝试使用类型来删除所有这些不必要的分配和类型断言。

type RenameMe struct {
     Status string `json:"status"`
     Astatus string `json:"a_status"`
     Method string `json:"method"`
     Path string `json:"path"`
     ElementUid string `json:"element_uid"`
     TimeLocal time.Time `json:"time_local"`
     Etime time.Time // deal with this after the fact
}

data := &RenameMe{}
if err := json.Unmarshal(byt, data); err != nil {
            break
        }

data.Etime,  _ := time.Parse("[02/Jan/2006:15:04:05 -0700]", time_local)

我不会对其进行测试以确保其性能优于您的代码,但我敢肯定它会在很大程度上提高性能。试试看,让我知道。

2020-07-02