libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
mit_exchanges_performance.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2015 by J.M.McGuiness, isimud@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 #include "stdafx.h"
20 
21 #define BOOST_TEST_MODULE isimud_tests
22 #include <boost/test/included/unit_test.hpp>
23 
24 #include <boost/mpl/list.hpp>
25 
26 #include "../exchanges/FIX/v5.0sp2/fix_client.hpp"
27 #include "../exchanges/MIT/BIT/bit.hpp"
28 #include "../exchanges/MIT/BIT/bit_sim.hpp"
29 #ifndef JMMCG_PERFORMANCE_TESTS
30 # include "../exchanges/MIT/JSE/jse.hpp"
31 # include "../exchanges/MIT/JSE/jse_sim.hpp"
32 # include "../exchanges/MIT/LSE/lse.hpp"
33 # include "../exchanges/MIT/LSE/lse_sim.hpp"
34 # include "../exchanges/MIT/OSLO/oslo.hpp"
35 # include "../exchanges/MIT/OSLO/oslo_sim.hpp"
36 # include "../exchanges/MIT/TRQ/trq.hpp"
37 # include "../exchanges/MIT/TRQ/trq_sim.hpp"
38 #endif
39 #include "../exchanges/conversions/fix_to_mit_conversions.hpp"
40 #include "../exchanges/conversions/mit_to_fix_conversions.hpp"
41 
42 #include "core/ave_deviation_meter.hpp"
43 #include "core/latency_timestamps.hpp"
44 #include "core/jthread.hpp"
45 #include "core/stats_output.hpp"
46 
47 #include <ratio>
48 
49 using namespace libjmmcg;
50 using namespace libisimud;
51 
52 using api_thread_traits=ppd::thread_params<ppd::platform_api>;
53 using timed_results_t=ave_deviation_meter<double>;
54 
55 const boost::asio::ip::address localhost(boost::asio::ip::address_v4::loopback());
56 const unsigned short client_port=12377u;
57 const boost::asio::ip::address primary_gw(boost::asio::ip::address_v4::loopback());
58 const unsigned short unused_primary_port=client_port+1;
59 const boost::asio::ip::address secondary_gw(boost::asio::ip::address_v4::loopback());
61 const exchanges::MIT::common::ClientOrderID_t clientOrderId1{"00000000000000test1"};
62 const exchanges::MIT::common::SeqNum_t sequenceNumber(1);
63 
64 using exchg_t_types=boost::mpl::list<
65  std::pair<exchanges::MIT::BIT::link_t<exchanges::FIX::v5_0sp2::MsgTypes>, exchanges::MIT::BIT::simulator_t>
66 #ifndef JMMCG_PERFORMANCE_TESTS
67  ,
68  std::pair<exchanges::MIT::JSE::link_t<exchanges::FIX::v5_0sp2::MsgTypes>, exchanges::MIT::JSE::simulator_t>,
69  std::pair<exchanges::MIT::LSE::link_t<exchanges::FIX::v5_0sp2::MsgTypes>, exchanges::MIT::LSE::simulator_t>,
70  std::pair<exchanges::MIT::OSLO::link_t<exchanges::FIX::v5_0sp2::MsgTypes>, exchanges::MIT::OSLO::simulator_t>,
71  std::pair<exchanges::MIT::TRQ::link_t<exchanges::FIX::v5_0sp2::MsgTypes>, exchanges::MIT::TRQ::simulator_t>
72 #endif
73 >;
74 
75 template<class exchg_t>
76 struct only_sim {
77  using link_t=typename exchg_t::first_type;
78  using simulator_t=typename exchg_t::second_type;
79  using conn_pol_t=typename link_t::exchg_link_t::conn_pol_t;
80  using connection_t=exchanges::common::connection<
81  typename simulator_t::msg_processor_t::msg_details_t,
82  conn_pol_t
83  >;
84  using ref_data_t=typename link_t::client_link_t::proc_rules_t::ref_data;
85 
86  static ref_data_t make_ref_data() noexcept(false) {
87  const std::string ref_data_file(
88  "133215;FTSE100;SET0;PT_T;TP_1;GB00BH4HKS39;;20060731;0;1;10000;42467000;1;;1;DE;VOD;VODAFONE GRP.;BH4HKS3;15225662730;GBX;1;Y;0023;VOVOD;VODAFONE GROUP PLC;0;;;15000;ORD USD0.20 20/21;;1;1;5;GB;;;FE00;1;;;;1;A;;;;;;\n"
89  "2926;FTSE100;SET1;PT_T;TP_12;GB0000595859;;20000419;0;1;3000;32438040;1;;1;DE;ARM;ARM HLDGS.;0059585;3861344694;GBX;1;Y;0023;ARARM;ARM HOLDINGS PLC;0;;;7500;ORD 0.05P;;1;1;5;GB;;;FS10;4;;;;2;B;;;;;;"
90  );
91  std::stringstream ss;
92  ss<<ref_data_file;
93  return std::move(ref_data_t(ss));
94  }
95 
96  const typename conn_pol_t::gateways_t gateways{
99  };
101  const conn_pol_t conn_pol{
102  gateways,
107  },
110  }
111  };
112  no_latency_timestamps ts{0};
113  simulator_t svr{
114  primary_gw,
116  simulator_t::socket_t::socket_priority::low,
117  api_thread_traits::processor_mask_type(exchanges::common::thread_traits::exchange_simulator_thread.core),
118  exchanges::common::thread_traits::exchange_simulator_thread.priority,
119  typename simulator_t::proc_rules_t(),
120  ts,
121  "sim" LIBJMMCG_ENQUOTE(__LINE__)
122  };
123 };
124 
125 template<class exchg_t>
126 struct simulator_and_link : public only_sim<exchg_t> {
127  using base_t=only_sim<exchg_t>;
128  using link_t=typename base_t::link_t;
129 
130 #ifdef JMMCG_PERFORMANCE_TESTS
132 #else
133  no_latency_timestamps ts;
134 #endif
135  link_t link{
136  typename link_t::client_link_t::ctor_args{
137  localhost,
138  client_port,
139  typename link_t::client_link_t::proc_rules_t(this->ref_data)
140  },
141  this->conn_pol,
142  link_t::socket_t::socket_priority::high,
143  link_t::socket_t::socket_priority::low,
144  typename link_t::exchg_to_client_proc_rules_t(this->ref_data),
145  ts,
146  "link" LIBJMMCG_ENQUOTE(__LINE__)
147  };
148 
149  explicit simulator_and_link(std::size_t num_ts)
150  : ts(num_ts) {}
151 };
152 
153 template<class exchg_t>
154 struct simulator_and_link_client_too : public simulator_and_link<exchg_t> {
155  using base_t=simulator_and_link<exchg_t>;
156 
157 #ifdef JMMCG_PERFORMANCE_TESTS
159 #else
160  no_latency_timestamps client_ts;
161 #endif
162  exchanges::FIX::v5_0sp2::fix_client client{
163  localhost,
164  client_port,
167  client_ts
168  };
169 
170  explicit simulator_and_link_client_too(std::size_t num_ts)
171  : base_t(num_ts), client_ts(num_ts) {}
172 };
173 
174 BOOST_AUTO_TEST_SUITE(performance, *stats_to_csv::make_fixture("mit_exchanges_performance.csv"))
175 
176 BOOST_AUTO_TEST_SUITE(simulator)
177 
178 /**
179  \test <a href="./examples/mit_exchanges_performance.svg">Graph</a> of performance results for the MIT-based simulator-
180  ================================
181  Results for 5000 repetitions.
182  The timings include the full-round-trip: including time to create the MIT order, send it, time in the simulator, time to receive, all of which includes the "wire-time".
183 */
184 BOOST_AUTO_TEST_CASE_TEMPLATE(reject, exchg_t, exchg_t_types) {
185 #ifdef JMMCG_PERFORMANCE_TESTS
186  const unsigned long num_loops=10000;
187  const unsigned short loops_for_conv=1000;
188 #else
189  const unsigned long num_loops=1;
190  const unsigned short loops_for_conv=1;
191 #endif
192  const double perc_conv_estimate=2.0;
193 
194  using fixture_t=only_sim<exchg_t>;
195 
196  const fixture_t f{};
197 
198  typename fixture_t::connection_t link(
199  f.conn_pol,
201  exchanges::common::thread_traits::client_to_exchange_thread.core
202  );
203 
204  auto send_and_receive=[&link]() {
205  using msg_details_t=typename fixture_t::simulator_t::msg_processor_t::msg_details_t;
206  const typename msg_details_t::NewOrder_t msg(
207  sequenceNumber,
208  clientOrderId1,
209  msg_details_t::OrderType::Market,
210  exchanges::MIT::common::TIF::Day,
211  msg_details_t::Side::Buy,
212  exchg_t::second_type::proc_rules_t::invalidInstrumentID,
213  exchg_t::second_type::proc_rules_t::quantity_limit-1,
214  exchg_t::second_type::proc_rules_t::price
215  );
216  BOOST_CHECK_NO_THROW(link.send(msg));
217  typename msg_details_t::BusinessReject reply;
218  BOOST_CHECK_NO_THROW(link.receive(reply));
219  };
220 
221  const std::pair<timed_results_t, bool> timed_results(compute_average_deviation<timed_results_t::value_type>(
222  perc_conv_estimate,
223  loops_for_conv,
224  [&send_and_receive]() {
225  const auto t1=std::chrono::high_resolution_clock::now();
226  for (unsigned long i=0; i<num_loops; ++i) {
227  send_and_receive();
228  }
229  const auto t2=std::chrono::high_resolution_clock::now();
230  return timed_results_t::value_type(static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count())/num_loops);
231  }
232  ));
233  std::cout<<fixture_t::link_t::thread_traits::demangle_name(typeid(typename exchg_t::first_type))<<"\n\tSimulator round-trip time (microseconds)="<<timed_results.first<<std::endl;
234 #ifdef JMMCG_PERFORMANCE_TESTS
235  stats_to_csv::handle->stats<<timed_results.first.to_csv()<<std::flush;
236  BOOST_CHECK(!timed_results.second);
237 #endif
238 }
239 
240 BOOST_AUTO_TEST_SUITE_END()
241 
242 BOOST_AUTO_TEST_SUITE(exchange_gateways)
243 
244 /**
245  \test <a href="./examples/mit_exchanges_in_order_performance.svg">Graph</a> of performance results for the MIT-based exchange-links, sending an order and receiving the reject one-at-a-time. The histograms generated by using <a href="./examples/mit_exchanges_in_order_performance_latencies_GNU.svg">GNU</a> or <a href="./examples/mit_exchanges_in_order_performance_latencies_Clang.svg">Clang</a> to compile & link the code of the latest performance results, pinned and with numactl.
246  ================================
247  Results for 20000 repetitions, repeated up to 500 times.
248  The timings include the full-round-trip: including time to create the FIX order, convert it, send it, time in the simulator, time to receive, time to convert back to FIX, all of which includes the "wire-time".
249  If numactl is to be used, the command "numactl --cpunodebind=NODE_INDEX --membind=NODE_INDEX ../../build/release/isimud/tests/test_mit_exchanges_performance" should be executed, Where NODE_INDEX should be the same value as that in thread_traits::numa_index.
250 
251  \see thread_traits::numa_index
252 */
253 BOOST_AUTO_TEST_CASE_TEMPLATE(order_rejected_at_a_time, exchg_t, exchg_t_types) {
254 #ifdef JMMCG_PERFORMANCE_TESTS
255  const unsigned long num_loops=20000;
256  const unsigned short loops_for_conv=500;
257 #else
258  const unsigned long num_loops=1;
259  const unsigned short loops_for_conv=1;
260 #endif
261  const double perc_conv_estimate=0.1;
262 
263  using fixture_t=simulator_and_link_client_too<exchg_t>;
264 
265  fixture_t f(2*3*num_loops*loops_for_conv);
266 
267  auto const &timed_results=f.client.in_order_tx_rx_stats(
268  num_loops,
269  loops_for_conv,
270  perc_conv_estimate
271  );
272  std::cout<<fixture_t::link_t::thread_traits::demangle_name(typeid(typename exchg_t::first_type))<<"\n\tExchange round-trip in-order time (microseconds)="<<timed_results.first<<std::endl;
273 #ifdef JMMCG_PERFORMANCE_TESTS
274  stats_to_csv::handle->stats<<timed_results.first.to_csv()<<std::flush;
275  BOOST_CHECK(!timed_results.second);
276  f.ts.write_to_named_csv_file(std::cout, "mit_exchanges_in_order_performance_latencies");
277 #endif
278 }
279 
280 /**
281  \test <a href="./examples/mit_exchanges_out_of_order_performance.svg">Graph</a> of performance results for the MIT-based exchange-links, sending the orders and receiving the rejects in parallel. The histograms generated by using <a href="./examples/mit_exchanges_out_of_order_performance_latencies_GNU.svg">GNU</a> or <a href="./examples/mit_exchanges_out_of_order_performance_latencies_Clang.svg">Clang</a> to compile & link the code of the latest performance results, pinned and with numactl.
282  ================================
283  Results for 20000 orders sent & rejects received, repeated up to 500 times.
284  The timings include the full-round-trip: including time to create the FIX order, convert it, send it, time in the simulator, time to receive, time to convert back to FIX, all of which includes the "wire-time".
285  If numactl is to be used, the command "numactl --cpunodebind=NODE_INDEX --membind=NODE_INDEX ../../build/release/isimud/tests/test_mit_exchanges_performance" should be executed, Where NODE_INDEX should be the same value as that in thread_traits::numa_index.
286 
287  \see thread_traits::numa_index
288 */
289 BOOST_AUTO_TEST_CASE_TEMPLATE(order_rejects_parallel, exchg_t, exchg_t_types) {
290 #ifdef JMMCG_PERFORMANCE_TESTS
291  const unsigned long num_loops=20000;
292  const unsigned short loops_for_conv=500;
293 #else
294  const unsigned long num_loops=1;
295  const unsigned short loops_for_conv=1;
296 #endif
297  const double perc_conv_estimate=0.1;
298 
299  using fixture_t=simulator_and_link_client_too<exchg_t>;
300 
301  fixture_t f(2*3*num_loops*loops_for_conv);
302 
303  auto send_and_receive=[&f]() {
304  std::chrono::high_resolution_clock::time_point send_time{};
305  std::chrono::high_resolution_clock::time_point receive_time{};
306 
307  auto send_batch_of_orders=[&f, &send_time]() {
308  BOOST_CHECK(f.client.send_fix_msg.is_valid());
309  // This farting around is just to throttle sending messages so that localhost is not flooded. (If it got flooded it would simply stop sending more messages and the test would break.)
310  double accrued_delay_in_msec{};
311  send_time=std::chrono::high_resolution_clock::now();
312  for (unsigned long i=0; i<num_loops; ++i) {
313  f.client.send();
314  accrued_delay_in_msec+=cpu_timer::pause_for_usec(400);
315  }
316  send_time=std::chrono::time_point_cast<std::chrono::high_resolution_clock::duration>(send_time+std::chrono::duration<double, std::micro>(accrued_delay_in_msec));
317  };
318 
319  auto receive_batch_of_rejects=[&f, &receive_time]() {
320  for (unsigned long i=0; i<num_loops; ++i) {
321  f.client.receive();
322  }
323  receive_time=std::chrono::high_resolution_clock::now();
324  BOOST_CHECK(f.client.receive_fix_msg.is_valid());
325  };
326 
327  {
328  ppd::jthread send(std::move(send_batch_of_orders));
329  ppd::jthread receive(std::move(receive_batch_of_rejects));
330  }
331  return timed_results_t::value_type(static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(receive_time - send_time).count())/num_loops);
332  };
333 
334  const std::pair<timed_results_t, bool> timed_results(compute_average_deviation<timed_results_t::value_type>(
335  perc_conv_estimate,
336  loops_for_conv,
337  std::move(send_and_receive)
338  ));
339  std::cout<<fixture_t::link_t::thread_traits::demangle_name(typeid(typename exchg_t::first_type))<<"\n\tExchange round-trip out-of-order time (microseconds)="<<timed_results.first<<std::endl;
340 #ifdef JMMCG_PERFORMANCE_TESTS
341  stats_to_csv::handle->stats<<timed_results.first.to_csv()<<std::flush;
342  BOOST_CHECK(!timed_results.second);
343  f.ts.write_to_named_csv_file(std::cout, "mit_exchanges_out_of_order_performance_latencies");
344 #endif
345 }
346 
347 BOOST_AUTO_TEST_SUITE_END()
348 
349 BOOST_AUTO_TEST_SUITE_END()