GO中使用protobuf,性能和java、rust、c++对比

(适用2022年度)
下载protoc程序:
    tips:由于centos版本过低,只能用protoc-3.0版本,protoc-3.17.0版本用不了

安装protobuf第三方库(这里面包含了proto-go-gen)
tips:这里如果安装go之后没有添加goroot路径到bash中的话,那么安装会失败。添加方法:
    运行指令打开文件:vi ~/.bashrc
    添加下面一行,之后保存,重新登录ssh或者打开终端。
    export GOPATH=/home/testgo/go
    
2022年使用的命令安装protobuf-go,之前的get命令不合适了(注意调整系统时间到正确时间,有可能需要科学上网等):
   go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
   

运行命令将proto文件生成go代码:
../protoc3.0/bin/protoc --go_out=golang ./*.proto


proto文件生成go代码时候报错:protoc-gen-go: invalid Go import path "." for
解决办法(其中pb为包名):
option go_package = "./;pb";

指定protobuf包版本(此命令会修改go.mod文件):
go mod edit -require google.golang.org/[email protected]

如果发生package is not in goroot错误,需要启用module开关(并非是设置成关闭状态,那是老版本了)
go代码中引入module的代码如下:
import(
pb "hello/pb"  (此处hello不能没有,否则会报错:package pb is not in GOROOT (/home/testgo/go/src/pb))
proto "google.golang.org/protobuf/proto" (为什么此处不需要hello?)
)

原因是没有启用module开关(GO111MODULE),需要如下目录结构:
hello/main.go
hello/pb/*.go
hello/go.mod

其中需要在hello目录中运行go mod init hello

import指令中的引入,需要指定的目录下有go代码文件,否则不能正常引入。

整个测试过程中,使用go版本1.19,性能和java,rust,c++对比如下(其中windows的cpu差点,linux的cpu好点):

解析单个protobuf耗时:

          go(linux)需要0-2ms;

           java(linux)需要首次需要48ms,二次需要5-9ms;

           rust在debug下(windows)需要8ms,在release模式下需要第一次2ms,第二次0ms。

           rust在linux下(机器不同),耗时1ms。

           c++中(linux)需要1ms。

解析10000个protobuf耗时:

           go(linux)需要4762ms(关闭gc情况下);

          java(linux)需要2451ms;

         rust在debug(windows)模式下需要38294ms,在release模式下需要第一次4031ms,第二次7730ms。

         rust在linux下(跟windows不一样)耗时3394ms。

         c++(linux)中需要3369ms。

据观察java好像会运用多核性能,也可能是java的垃圾回收器的问题导致,java的cpu使用率会达到140%,go最多100%。

c++代码:

GO中使用protobuf,性能和java、rust、c++对比_第1张图片

附上rust代码:

use std::fs;

use protobuf::Message;

mod login;

mod goods;

mod common;

mod hero;

use crate::login::RoleInfo;

extern crate chrono;

use chrono::prelude::*;

fn main() {

    println!("Hello, world!");

    let bytes = fs::read("e:\\roleinfo.bs").unwrap(); // 这个是迭代器,不是数组

    let mut ba = [0u8;34483]; // RUST没法使用动态数组,这里只能写死长度。

    for i in 0..bytes.len(){

        ba[i] = bytes[i];

    }

    let start = Local::now().timestamp_millis();

    let ri = RoleInfo::parse_from_bytes(&ba).unwrap();

    print!("name={}, time={}ms\n", ri.get_name(), Local::now().timestamp_millis() - start);

    let start = Local::now().timestamp_millis();

    let ri = RoleInfo::parse_from_bytes(&ba).unwrap();

    print!("name={}, time={}ms\n", ri.get_name(), Local::now().timestamp_millis() - start);

    let start = Local::now().timestamp_millis();

    let mut count = 0;

    while count < 10000{

        let ri = RoleInfo::parse_from_bytes(&ba).unwrap();

        count = count + 1;

    }

    print!("name={}, time={}ms\n", ri.get_name(), Local::now().timestamp_millis() - start);

    let mut count = 0;

    while count < 10000{

        let ri = RoleInfo::parse_from_bytes(&ba).unwrap();

        count = count + 1;

    }

    print!("name={}, time={}ms\n", ri.get_name(), Local::now().timestamp_millis() - start);

        /* protobuf_codegen_pure::Codegen::new()

        .out_dir("src/protos")

        .inputs(&["src/protos/common.proto", "src/protos/goods.proto", "src/protos/hero.proto", "src/protos/login.proto"])

        .include("src/protos")

        .run()

        .expect("Codegen failed."); */

}

rust配置Cargo.toml:

[package]
name = "greeting"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
protobuf-codegen-pure = "2"
protobuf="2.5.1"
chrono = "0.4"

GO代码:

package main

import "fmt"
import "time"
import "io/ioutil"
import(
pb "hello/pb"
proto "google.golang.org/protobuf/proto"
)

func main() {
    fmt.Println("Hello, World!")
    var bs,err=ioutil.ReadFile("./roleinfo.bs")
    if(err == nil){
       fmt.Printf("read success!len=%v \n", len(bs));
    }
    startTime:=time.Now().UnixNano() / 1e6;
    roleInfo := &pb.RoleInfo{}
    if err := proto.Unmarshal(bs, roleInfo); err != nil {
        fmt.Println("Failed to parse address book:", err)
    }
    fmt.Println(roleInfo.GetName());
    endTime:=time.Now().UnixNano() / 1e6;
    fmt.Printf("need time: %vms,start=%v end=%v \n", endTime - startTime, startTime, endTime);

    startTime=time.Now().UnixNano() / 1e6;
    roleInfo= &pb.RoleInfo{}
    if err= proto.Unmarshal(bs, roleInfo); err != nil {
        fmt.Println("Failed to parse address book:", err)
    }
    fmt.Println(roleInfo.GetName());
    endTime=time.Now().UnixNano() / 1e6;
    fmt.Printf("need time: %vms,start=%v end=%v \n", endTime - startTime, startTime, endTime);


    startTime=time.Now().UnixNano() / 1e6;
    for i:=0; i < 10000; i++{
    roleInfo= &pb.RoleInfo{}
    if err= proto.Unmarshal(bs, roleInfo); err != nil {
        fmt.Println("Failed to parse address book:", err)
    }
    }
    fmt.Printf("loop end:%s \n", roleInfo.GetName());
    endTime=time.Now().UnixNano() / 1e6;
    fmt.Printf("need time: %vms,start=%v end=%v \n", endTime - startTime, startTime, endTime);


    startTime=time.Now().UnixNano() / 1e6;
    for i:=0; i < 10000; i++{
    roleInfo= &pb.RoleInfo{}
    if err= proto.Unmarshal(bs, roleInfo); err != nil {
        fmt.Println("Failed to parse address book:", err)
    }
    }
    fmt.Printf("loop end 2:%s \n", roleInfo.GetName());
    endTime=time.Now().UnixNano() / 1e6;
    fmt.Printf("need time: %vms,start=%v end=%v \n", endTime - startTime, startTime, endTime);


    fmt.Println("end!");
}

你可能感兴趣的:(golang,开发语言,后端)