Skip to content

gufeijun/hgen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

github Build Status

Related Libraries

This project is meant to be used with the following RPC libraries:

Introduction

hgen is the compiler for the Interface Definition Language (IDL) of a cross-language RPC framework, similar in concept to gRPC.

Instead of using a third-party IDL like Protocol Buffers, hgen uses its own simple, custom-designed syntax. The purpose of this compiler is to parse our custom syntax and generate stub code for target languages, which then interfaces with the RPC frameworks listed above. Currently, it supports mutual RPC calls between Go, C, and Node.js.

The compiler's frontend is a hand-written recursive descent parser for an LL(1) grammar, and the backend uses Go's template engine for code generation. The grammar can be found in bnf.txt.

For details on the RPC framework's protocol design, please see the rpch-go

IDL Syntax

Create a math.gfj file:

service Math{
    // Add two numbers
    uint32 Add(uint32,uint32)	// Multiple input parameters of basic types are allowed
       // Subtract two numbers
    int32 Sub(int32,int32)
    // Multiply two numbers
    int32 Multiply(TwoNum)
    // Divide two numbers
    Quotient Divide(uint64,uint64)
    void NoReturnFunc(void)		// Services can have no return value or request parameters
}

message Quotient{
    uint64 Quo	// Quotient
    uint64 Rem	// Remainder
}

message TwoNum{
    int32 A
    int32 B
}

// A message can contain other messages as members
message ComplexStruct {
    TwoNum Nums
    Quotient noConcern
}

There are only two keywords:

  • message defines composite structures.
  • service defines a collection of services.

Basic Types:int8、uint8、int16、uint16、int32、uint32、int64、uint64、float32、float64、string、void、stream、istream、ostream。stream is a streaming type, currently only supported by rpch-go. See rpch-go for more details。

Benchmarking

In addition to the three language implementations of rpch, we introduced Go's standard RPC library and the gRPC framework for a comparative analysis.

The test case is a simple Add service (adding two numbers). Our framework can implement this service in two ways via the IDL:

Implementation 1

service Math{
	int32 Add(int32,int32)
}

No JSON serialization is needed; data is transferred directly in little-endian format, resulting in higher efficiency.

Implementation 2

service Math{
	Response Add(Request)
}
message Response{
	int32 Result
}
message Request{
	int32 A
	int32 B
}

This requires serialization to transfer composite message structures, leading to lower efficiency but better representing typical application scenarios. Since gRPC can only use this method, this implementation is more representative of real-world business cases. Therefore, in addition to the first method, we also benchmarked our framework with this second implementation.

We measured throughput and latency with a varying number of concurrent clients. When the number of concurrent clients was 500 or less, each client sent 10,000 RPC requests. When it exceeded 500, each client sent 1,000 requests to save time.

The tests used a synchronous request-response model, where a new request is not sent until the response to the previous one is received. All tests were run multiple times, and the average values were taken.

Raw Data

rpch-c represents the framework implemented in C. rpch-c(json) represents the C implementation using the second method (JSON serialization).

Throughput (requests/sec) under different numbers of concurrent clients:

rpch-c rpch-c(json) rpch-go rpch-go(json) rpch-node stdrpc grpc-go
1 9980 9320 11792 9355 1039 6993 3686
10 74239 72727 91491 78740 8702 71994 29420
100 178922 144928 148456 115380 12649 110803 37926
200 167940 130387 139860 111988 15759 107718 39118
300 158587 125697 134735 109601 17549 104102 39425
500 150299 119927 131144 107794 17370 101161 39538
1000 151604 120460 130371 111089 16420 102162 39757
2000 145041 1209575 129514 109922 15709 99789 37434
5000 143661 113254 123066 102303 14772 90283 36342

Latency per request (ms) under different numbers of concurrent clients:

rpch-c rpch-c(json) rpch-go rpch-go(json) rpch-node stdrpc grpc-go
1 0.1002 0.1073 0.0848 0.1069 0.9628 0.143 0.2713
10 0.1347 0.1375 0.1093 0.127 1.1491 0.1389 0.3399
100 0.5589 0.69 0.6736 0.8667 7.9056 0.9025 2.6367
200 1.1909 1.5339 1.43 1.7859 12.691 1.8567 5.1127
300 1.8917 2.3867 2.2266 2.7372 17.0947 2.8818 7.6093
500 3.3267 4.1692 3.8126 4.6385 28.785 4.9426 12.6462
1000 6.5961 8.3015 7.6704 9.0018 60.903 9.7884 25.1527
2000 13.7892 16.5323 15.4424 18.1948 127.3164 20.0422 53.427
5000 34.8042 44.1485 40.6287 48.8744 338.4807 55.3812 137.5815

Throughput

(The horizontal axis represents the number of concurrent clients, and the vertical axis represents throughput in RPC requests per second. Higher is better.) image.png

Latency

(The horizontal axis represents the number of concurrent clients, and the vertical axis represents the latency per request in milliseconds. Lower is better.) image.png

Conclusion

Based on the benchmark results, we can draw the following conclusions in order of performance: rpch-c > rpch-go > rpch-c(json) > rpch-go(json) > stdrpc >> grpc-go > rpch-node.

Overall Performance:

  1. The custom rpch framework developed in C (rpch-c) achieves the highest performance, followed closely by the Go implementation (rpch-go).
  2. Serialization Overhead: Employing JSON serialization results in a noticeable performance degradation. However, even with this overhead, both C and Go implementations still outperform Go's standard RPC library (stdrpc).
  3. Framework Comparison: The performance of grpc-go is significantly lower, only about twice that of rpch-node. Stability: All frameworks exhibit stable performance, with latency increasing linearly as the number of concurrent clients grows. The performance rankings for latency are consistent with those for throughput.
  4. Language Impact: As expected, the C implementation delivers the best performance, while the Node.js implementation is the least performant.

Installation and Usage

There are two ways to install hgen:

  1. Build from Source
# for linux
git clone github.com/gufeijun/hgen
cd hgen
go build -o hgen main.go

This will generate an executable file named hgen.

  1. Download Binaries

    Pre-compiled binaries are available on the Releases page.

Usage:

Usage of hgen:
	hgen [options] <IDLfiles...>
options:
  -dir string
    	the dirpath where the generated source code files will be placed (default "gfj")
  -lang string
    	the target languege the IDL will be compliled to (default "c")

About

compiler for rpch

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors