Reputation: 581
We're developing a high frequency trading platform with C++ and we've tried implementing grpc with protobuf but we saw that a single network call tooks approximately 200-300 microseconds which is too long for us. What we are expecting to have as serializing/deserializing data through network socket is approximately 50-60 microseconds. Than we 've tried to use protobuf with native c++ sockets (with using non blocking i/o), we saw that this time performance became approximately 150-200 microseconds which was not enough for us. Than we saw flatbuffers and implemented it as described in below. However during our tests we saw that only serializing (also same in deserializing) tooks approximately 50 microseconds and also transferring the data tooks 30-40 microseconds so totatly it tooks approximately 100-150 microseconds. So I wondered if we are doing something wrong in our implementation of flatbuffers.
In the below example, I've calculated the difference betwen timestamp logs are :
Timestamp 1 -> Timestamp 2 = 16 microseconds
Timestamp 2 -> Timestamp 3 = 24 microseconds
Total serialization = 40 microseconds
Do you know any other way to increase the performance
Example code for serializing data with flatbuffers in C++:
const char* MAHelper::getRequest(BaseRequest *request,int& size) {
const char *result;
flatbuffers::FlatBufferBuilder builder(10240);
if (request->orderType == OrderTypes::TYPE_LoginRequest){
std::cout<<"Timestamp 1: "<<getCurrentTimestamp()<<std::endl;
LoginRequest *loginRequest = (LoginRequest*) request;
std::cout<<"Converting Login Request 1: "<<getCurrentTimestamp()<<std::endl;
auto username = builder.CreateString(loginRequest->userName);
auto password = builder.CreateString(loginRequest->password);
auto application = getApplication(loginRequest->applicationType);
std::cout<<"Timestamp 2: "<<getCurrentTimestamp()<<std::endl;
auto loginReq = piramit::orders::fb::CreateLoginRequest(builder,username,password,application);
auto loginOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_LoginRequest,loginReq.Union());
builder.Finish(loginOrderBase);
std::cout<<"Timestamp 3:"<<getCurrentTimestamp()<<std::endl;
} else if (request->orderType == OrderTypes::TYPE_EnterOrderRequest) {
EnterOrderRequest *enterOrderRequest = (EnterOrderRequest*) request;
auto strategyIdentifier = builder.CreateString(enterOrderRequest->strategyIdentifier);
auto passThrough = builder.CreateString(enterOrderRequest->passThrough);
auto account = builder.CreateString(enterOrderRequest->account);
auto authToken = builder.CreateString(enterOrderRequest->baseRequest.authToken);
auto enterOrderReq = piramit::orders::fb::CreateEnterOrder(builder,enterOrderRequest->orderbookId,enterOrderRequest->quantity,enterOrderRequest->price,account,
getStrategyType(enterOrderRequest->strategyType),strategyIdentifier,getSide(enterOrderRequest->side),getTimeInForce(enterOrderRequest->timeInForce),passThrough,getOrderType(enterOrderRequest->orderType));
auto enterOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_EnterOrder,enterOrderReq.Union(),authToken);
builder.Finish(enterOrderBase);
} else if (request->orderType == OrderTypes::TYPE_ReplaceOrderRequest) {
ReplaceOrderRequest *replaceOrderRequest = (ReplaceOrderRequest*) request;
auto orderToken = builder.CreateString(replaceOrderRequest->orderToken);
auto authToken = builder.CreateString(replaceOrderRequest->baseRequest.authToken);
auto replaceOrderReq = piramit::orders::fb::CreateReplaceOrder(builder,orderToken,replaceOrderRequest->quantity,replaceOrderRequest->price);
auto replaceOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_ReplaceOrder,replaceOrderReq.Union(),authToken);
builder.Finish(replaceOrderBase);
} else if (request->orderType == OrderTypes::TYPE_CancelOrderRequest) {
CancelOrderRequest *cancelOrderRequest = (CancelOrderRequest*) request;
auto orderToken = builder.CreateString(cancelOrderRequest->orderToken);
auto authToken = builder.CreateString(cancelOrderRequest->baseRequest.authToken);
auto cancelOrderReq = piramit::orders::fb::CreateCancelOrder(builder,orderToken);
auto cancelOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_CancelOrder,cancelOrderReq.Union(),authToken);
builder.Finish(cancelOrderBase);
} else if (request->orderType == OrderTypes::TYPE_BasicOrderRequest) {
BasicOrderRequest *basicOrderRequest = (BasicOrderRequest*) request;
auto authToken = builder.CreateString(basicOrderRequest->baseRequest.authToken);
auto basicOrderReq = piramit::orders::fb::CreateOrderRequest(builder,getOperationType(basicOrderRequest->operation),basicOrderRequest->orderId,getOrderType(basicOrderRequest->orderTypes));
auto basicOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_OrderRequest,basicOrderReq.Union(),authToken);
builder.Finish(basicOrderBase);
} else if (request->orderType == OrderTypes::TYPE_AccountStrategyRequest) {
AccountStrategyRequest *accountStrategyRequest = (AccountStrategyRequest*) request;
flatbuffers::Offset<flatbuffers::String> account = 0;
flatbuffers::Offset<flatbuffers::String> strategyIdentifier = 0;
auto authToken = builder.CreateString(accountStrategyRequest->baseRequest.authToken);
if (accountStrategyRequest->operation == OPERATION_SET) {
account = builder.CreateString(accountStrategyRequest->accountStrategy.account);
strategyIdentifier = builder.CreateString(accountStrategyRequest->accountStrategy.strategyIdentifier);
}
flatbuffers::Offset<piramit::orders::fb::AccountStrategy> accountStrategy = piramit::orders::fb::CreateAccountStrategy(builder,accountStrategyRequest->accountStrategy.orderBookId,account,getStrategyType(accountStrategyRequest->accountStrategy.strategyType),strategyIdentifier);
auto accountStrategyReq = piramit::orders::fb::CreateAccountStrategyRequest(builder,getOperationType(accountStrategyRequest->operation),accountStrategy);
auto accountStrategyBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_AccountStrategyRequest,accountStrategyReq.Union(),authToken);
builder.Finish(accountStrategyBase);
} else if (request->orderType == OrderTypes::TYPE_OrderBookStateRequest) {
OrderBookStateRequest *orderBookStateRequest = (OrderBookStateRequest*) request;
auto stateName = builder.CreateString(orderBookStateRequest->stateName);
auto orderBookStateReq = piramit::orders::fb::CreateOrderBookStateRequest(builder,stateName,orderBookStateRequest->orderBookId,orderBookStateRequest->timestamp);
auto orderBookStateBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_OrderBookStateRequest,orderBookStateReq.Union());
builder.Finish(orderBookStateBase);
}
uint8_t *requestBuffer = builder.GetBufferPointer();
result = (const char*) requestBuffer;
size = builder.GetSize();
return result;
}
And also this is part of our schema in flatbuffers
union BaseRequest { LoginRequest,EnterOrder,CancelOrder,ReplaceOrder,OrderRequest,AccountStrategyRequest,OrderBookStateRequest }
table RequestHolder {
request:BaseRequest;
authToken:string;
}
table LoginRequest {
username:string;
password:string;
application:Application = APP_UNKNOWN;
}
table EnterOrder{
order_book_id:uint;
quantity:ulong;
price:int;
account:string;
strategy:StrategyType;
strategy_identifier:string;
side:Side;
time_in_force:TimeInForce;
pass_through:string;
order_type:OrderType;
}
root_type RequestHolder;
Upvotes: 0
Views: 871
Reputation: 6074
For serializing:
For deserializing:
Upvotes: 2