Using protobuf in GO, performance comparison with java, rust, c++

(Applicable to 2022)
Download the protoc program:
    tips: Since the centos version is too low, only protoc-3.0 version can be used, and protoc-3.17.0 version cannot be used

Install the protobuf third-party library (which includes proto-go-gen)
tips: If you do not add the goroot path to bash after installing go, the installation will fail. How to add:
    Run the command to open the file: vi ~/.bashrc
    Add the following line, save it, log in to ssh again or open the terminal.
    export GOPATH=/home/testgo/go
    
The command used in 2022 to install protobuf-go, the previous get command is not suitable (pay attention to adjust the system time to the correct time, you may need to surf the Internet scientifically, etc.): go install
   google.golang.org /protobuf/cmd/protoc-gen-go@latest
   

Run the command to generate go code from the proto file:
../protoc3.0/bin/protoc --go_out=golang ./*.proto


When the proto file generates go code, an error is reported: protoc-gen-go: invalid Go import path "." for
solution ( where pb is the package name ):
option go_package = "./;pb";

Specify the protobuf package version (this command will modify the go.mod file):
go mod edit -require google.golang.org/[email protected]

If the package is not in goroot error occurs, you need to enable the module switch (it is not set to off, it is an old version) The
code to introduce the module in the go code is as follows:
import(
pb "hello/pb" (here hello can not be without , otherwise an error will be reported: package pb is not in GOROOT (/home/testgo/go/src/pb))
proto "google.golang.org/protobuf/proto" (Why is hello not needed here?)
)

The reason is that the module switch (GO111MODULE) is not enabled, and the following directory structure is required:
hello/main.go
hello/pb/*.go
hello/go.mod

Which needs to run go mod init hello in the hello directory

The introduction in the import command requires a go code file in the specified directory, otherwise it cannot be imported normally.

During the entire test process, using go version 1.19, the performance is compared with java, rust, and c++ as follows (the cpu of windows is almost worse, and the cpu of linux is better):

Parsing a single protobuf takes time:

          go(linux) needs 0-2ms;

           java(linux) needs 48ms for the first time, and 5-9ms for the second time;

           Rust needs 8ms in debug (windows), 2ms for the first time in release mode, and 0ms for the second time.

           Rust takes 1ms under linux (different machines).

           In c++ (linux), it takes 1ms.

Parsing 10000 protobuf time-consuming:

           go(linux) takes 4762ms (when gc is turned off);

          java(linux) needs 2451ms;

         Rust needs 38294ms in debug (windows) mode, 4031ms for the first time in release mode, and 7730ms for the second time.

         Rust takes 3394ms under linux (unlike windows).

         It takes 3369ms in c++(linux).

According to observations, java seems to use multi-core performance, and it may also be caused by problems with the garbage collector of java. The cpu usage rate of java will reach 140%, and the usage rate of go will be up to 100%.

c++ code:

Attach the rust code:

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(); // This is an iterator, not an array

    let mut ba = [0u8;34483]; // RUST cannot use dynamic arrays, here we can only write dead length.

    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 configuration 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 code:

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!");
}

Guess you like

Origin blog.csdn.net/cention168/article/details/126399854