Measures the latency from the network socket read to the user callback. Logging is turned off to avoid affecting the results, a FeedListener timestamps each multicast message block, and the collected latencies are reported (min / median) and saved to a CSV file. It also shows where to apply CPU affinity for the market data processing threads.
#include "../Common/Defaults.h"
#include "../Common/ServiceEventTracer.h"
#include "../Common/Utils.h"
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <stdexcept>
namespace {
{
static const unsigned MAX_SAMPLES_COUNT = 100000;
public:
MyListener(Handler& handler)
: handler_(handler)
{
subscribe(this);
latencies_.reserve(MAX_SAMPLES_COUNT);
}
virtual ~MyListener()
{
try
{
if (handler_.active())
{
handler_.stop();
}
subscribe(0);
}
catch (...)
{
std::cerr << "Exception while unsubscribing." << std::endl;
}
}
void onFeedStarted(FeedId , const MarketIds& ) {}
void onFeedStopped(FeedId ) {}
void onSessionNumberChange(FeedId , short , short ) {}
void onMulticastMessageBlockEnd(FeedId , const MarketIds& ) {}
void onMulticastMessageBlockBegin(FeedId , const MessageInfo& msgInfo)
{
{
return;
}
latencies_.push_back(Timestamp::utcNow() - msgInfo.
receiveTime);
}
void processLatencies()
{
if (latencies_.empty())
{
std::cout << "Nothing to process (latencies list is empty)." << std::endl;
return;
}
std::size_t medianPosition = latencies_.size() / 2;
std::nth_element(latencies_.begin(), latencies_.begin() + medianPosition, latencies_.end());
TimeSpan medianLatency = latencies_.at(medianPosition);
TimeSpan minLatency = *std::min_element(latencies_.begin(), latencies_.end());
std::cout << "Latency from network socket read to user callback (nanoseconds):\n"
"Min: "
<< " ns\n"
"Median: "
<< " ns\n"
"Count: "
<< latencies_.size() << " multicast message blocks" << std::endl;
}
void saveLatencies(const std::string& path)
{
std::ofstream csvFile;
csvFile.open(path.c_str());
if (csvFile.good())
{
for (Latencies::const_iterator it = latencies_.begin(); it != latencies_.end(); ++it)
{
csvFile << it->nanoseconds() << ";\n";
}
}
else
{
std::string errorReason;
errorReason += "Cannot open file to output benchmarking statistics [filename=";
errorReason += path;
errorReason += "]. ";
throw std::domain_error(errorReason);
}
csvFile.close();
}
private:
void subscribe(MyListener* listener)
{
handler_.registerFeedListener(listener);
}
private:
typedef std::vector<TimeSpan> Latencies;
Handler& handler_;
Latencies latencies_;
};
}
int main(int , char** )
{
try
{
introduceMyself("Benchmark");
ServiceEventTracer serviceEventTracer;
serviceEventTracer.attachTo(handler);
MyListener myListener(handler);
);
subscriptions.insert(brentFutures);
handler.
start(subscriptions);
std::cout << "\nPress ENTER to stop Handler, analyze statistics and exit Sample." << std::endl;
readUserResponse();
std::cout << "Stopping Handler..." << std::endl;
std::cout << "Handler is stopped." << std::endl;
myListener.processLatencies();
myListener.saveLatencies("results.csv");
std::cout << "Done." << std::endl;
}
catch (const std::exception& ex)
{
std::cout << "Error: " << ex.what() << std::endl;
return EXIT_FAILURE;
}
catch (...)
{
std::cout << "Unknown exception." << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}