μHAL (v2.7.9)
Part of the IPbus software repository
PerfTester.cxx
Go to the documentation of this file.
1 /*
2 ---------------------------------------------------------------------------
3 
4  This file is part of uHAL.
5 
6  uHAL is a hardware access library and programming framework
7  originally developed for upgrades of the Level-1 trigger of the CMS
8  experiment at CERN.
9 
10  uHAL is free software: you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  uHAL is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with uHAL. If not, see <http://www.gnu.org/licenses/>.
22 
23  Marc Magrans de Abril, CERN
24  email: marc.magrans.de.abril <AT> cern.ch
25 
26  Andrew Rose, Imperial College, London
27  email: awr01 <AT> imperial.ac.uk
28 
29  Tom Williams, Rutherford Appleton Laboratory, Oxfordshire
30  email: tom.williams <AT> cern.ch
31 
32 ---------------------------------------------------------------------------
33 */
34 
36 
37 // C++ headers
38 #include <iostream>
39 #include <iomanip>
40 #include <sstream>
41 #include <cstdlib>
42 #include <unistd.h>
43 
44 // Boost headers
45 #include <boost/program_options.hpp>
46 #include <boost/bind.hpp>
47 #include <boost/foreach.hpp>
48 #include <boost/mem_fn.hpp>
49 
50 // uHAL headers
51 #include "uhal/ClientFactory.hpp"
52 #include "uhal/tests/tools.hpp"
53 
54 // Namespace resolution
55 namespace po = boost::program_options;
56 using namespace std;
57 
58 
59 // PUBLIC METHODS
60 
62  m_testDescMap(),
63  m_testFuncMap(),
64  m_deviceURIs(),
65  m_clients(),
66  m_testName ( "BandwidthRx" ),
67  m_iterations ( 1000 ),
68  m_baseAddrStr ( "0x0" ),
69  m_baseAddr ( 0 ),
70  m_bandwidthTestDepth ( 0 ),
71  m_verbose ( false ),
72  m_perIterationDispatch ( false ),
73  m_includeConnect ( false )
74 {
75  // ***** DECLARE TESTS HERE - descriptions should not be longer than a shortish line. *****:
76  // Receive bandwidth test
78  m_testDescMap["BandwidthRx"] = "Block read test (default depth = 340) to find the receive bandwidth.";
79  // Transmit bandwidth test
81  m_testDescMap["BandwidthTx"] = "Block write test (default depth = 340) to find the transmit bandwidth.";
82  // Validation test
84  m_testDescMap["Validation"] = "For validating downstream subsystems, such as the Control Hub or the IPbus firmware.";
85  // Sandbox test
86  m_testFuncMap["Sandbox"] = &PerfTester::sandbox;
87  m_testDescMap["Sandbox"] = "A user-definable test - modify the sandbox() function to whatever you wish.";
88  // ***** END DECLARATION OF TESTS *****
89  // Initialise random number generator
90  srand ( time ( NULL ) );
91 }
92 
93 
94 int uhal::tests::PerfTester::run ( int argc, char* argv[] )
95 {
96  try
97  {
98  // This just defines and parses the command line parameters that are allowed.
99  po::options_description argDescriptions ( "Allowed options" );
100  argDescriptions.add_options()
101  ( "help,h", "Produce help message" )
102  ( "list,l", "List the names of the different tests that can be performed." )
103  ( "verbose,v", "Make the output more verbose." )
104  ( "test,t", po::value<string> ( &m_testName )->default_value ( "BandwidthRx" ), "Name of the test to be performed." )
105  ( "iterations,i", po::value<uint64_t> ( &m_iterations )->default_value ( 1000 ), "Number of test iterations to run." )
106  ( "devices,d", po::value<StringVec> ( &m_deviceURIs )->multitoken(), "List of device connection URIs, e.g. chtcp-1.3://..., etc" )
107  ( "baseAddr,b", po::value<string> ( &m_baseAddrStr )->default_value ( "0x0" ), "Base address (in hex) of the test location on the target device(s)." )
108  ( "bandwidthTestDepth,w", po::value<boost::uint32_t> ( &m_bandwidthTestDepth )->default_value ( 340 ), "Depth of read/write used in bandwidth tests." )
109  ( "perIterationDispatch,p", "Force a network dispatch every test iteration instead of the default single dispatch call at the end." )
110  ( "includeConnect,c", "Include connect time in reported bandwidths and latencies" );
111  po::variables_map argMap;
112  po::store ( po::parse_command_line ( argc, argv, argDescriptions ), argMap );
113  po::notify ( argMap );
114  // Convert the hexadecimal base addr value into an actual number.
115  // This is just a workaround for boost:program_options not understanding hex input
116  istringstream convert ( m_baseAddrStr );
117  convert >> std::hex >> m_baseAddr;
118 
119  if ( convert.fail() )
120  {
121  cerr << "The specified base address was not valid hexadecimal!" << endl;
122  return 10;
123  }
124 
125  // Display help info and exit
126  if ( argc < 2 || argMap.count ( "help" ) )
127  {
128  ostringstream oss;
129  oss << argDescriptions;
130  outputHelpText ( oss.str() );
131  return 20;
132  }
133 
134  if ( argMap.count ( "list" ) )
135  {
136  outputTestDescriptionsList();
137  return 30;
138  }
139 
140  if ( argMap.count ( "verbose" ) )
141  {
142  m_verbose = true;
143  }
144 
145  if ( argMap.count ( "perIterationDispatch" ) )
146  {
147  m_perIterationDispatch = true;
148  }
149 
150  if ( argMap.count ( "includeConnect" ) )
151  {
152  m_includeConnect = true;
153  }
154 
155  if ( badInput() )
156  {
157  return 40; // Report bad user input and exit if necessary.
158  }
159 
160  outputUserChoices(); // Echo back to the user the settings they have selected.
161  buildClients(); // Build the clients from the device URIs provided by the user
162  ( this->*m_testFuncMap.find ( m_testName )->second ) (); // Calls the test function, based on the test name.
163  }
164  catch ( std::exception& e )
165  {
166  cerr << "Error - exception thrown ..." << endl << e.what() << endl;
167  return 50;
168  }
169  catch ( ... )
170  {
171  cerr << "Caught exception of unknown type!" << endl;
172  return 60;
173  }
174 
175  return 0; // The program ran successfully (even if the test didn't...)
176 }
177 
178 
179 // PRIVATE METHODS - Test infrastructure
180 
181 void uhal::tests::PerfTester::outputHelpText ( const string& argDescriptions ) const
182 {
183  cout << "\n -----------------------------------------\n"
184  " PerfTester.exe - IPbus Performance Tester\n"
185  " -----------------------------------------\n\n"
186  " Generate custom IPbus/uHAL tests from the command line\n\n"
187  << argDescriptions
188  << "Usage examples:\n\n"
189  " PerfTester.exe -t BandwidthTx -b 0xf0 -d ipbusudp-1.3://localhost:50001 ipbusudp-1.3://localhost:50002\n"
190  " PerfTester.exe -t BandwidthTx -w 5 -i 100 chtcp-1.3://localhost:10203?target=127.0.0.1:50001" << endl;
191  outputTestDescriptionsList();
192 }
193 
194 
196 {
197  cout << "\nNames and descriptions of available tests:\n" << endl;
198  cout << " " << setw ( 16 ) << left << "Name" << " " << "Description" << endl;
199  cout << " " << setw ( 16 ) << left << "----" << " " << "-----------" << endl;
200  TestDescMap::const_iterator iTest = m_testDescMap.begin(), iTestEnd = m_testDescMap.end();
201 
202  for ( ; iTest != iTestEnd ; ++iTest )
203  {
204  cout << " " << setw ( 16 ) << left << iTest->first << " " << iTest->second << endl;
205  }
206 
207  cout << endl;
208 }
209 
210 
212 {
213  if ( m_deviceURIs.empty() )
214  {
215  cerr << "You must specify at least one device connection URI by using the -d option!" << endl;
216  return true;
217  }
218 
219  if ( m_testFuncMap.find ( m_testName ) == m_testFuncMap.end() )
220  {
221  cerr << "The test name '" << m_testName
222  << "' is not one of the available tests!\nDo 'PerfTester -l' to see names of available tests!" << endl;
223  return true;
224  }
225 
226  return false;
227 }
228 
229 
231 {
232  cout << "Test settings:\n" << endl
233  << " Test Name --------------> " << m_testName << endl
234  << " Test register addr -----> " << std::hex << showbase << m_baseAddr << noshowbase << std::dec << endl
235  << " Test iterations --------> " << m_iterations << endl
236  << " Per-iteration dispatch --> " << ( m_perIterationDispatch?"Yes":"No" ) << endl
237  << " Device URIs:" << endl;
238  StringVec::const_iterator iDevice = m_deviceURIs.begin(), iDeviceEnd = m_deviceURIs.end();
239 
240  for ( ; iDevice != iDeviceEnd ; ++iDevice )
241  {
242  cout << " " << *iDevice << endl;
243  }
244 
245  cout << "\nRunning test now...\n" << endl;
246 }
247 
248 
250 {
251  if ( m_verbose )
252  {
253  setLogLevelTo ( Debug() );
254  cout << "Building device clients..." << endl;
255  }
256  else
257  {
258  setLogLevelTo ( Warning() );
259  }
260 
261  m_clients.reserve ( m_deviceURIs.size() );
262 
263  for ( unsigned int iURI = 0 ; iURI < m_deviceURIs.size() ; ++iURI )
264  {
265  m_clients.push_back ( ClientFactory::getInstance().getClient ( "MyDevice", m_deviceURIs.at ( iURI ) ) );
266  }
267 
268  if ( m_verbose )
269  {
270  cout << "Device clients built successfully!" << endl;
271  }
272 }
273 
274 
275 void uhal::tests::PerfTester::outputStandardResults ( double totalSeconds ) const
276 {
277  string underline;
278  underline.assign ( m_testName.size() + 14, '-' );
279  cout << m_testName << " Test Results:\n"
280  << underline << "\n\n"
281  << "Number of IPbus hosts in test = " << m_deviceURIs.size() << "\n"
282  << "Total test iterations = " << m_iterations << "\n"
283  << "Total time taken = " << totalSeconds << " s\n"
284  << "Test iteration frequency = " << m_iterations/totalSeconds << " Hz" << endl;
285 }
286 
287 
288 bool uhal::tests::PerfTester::buffersEqual ( const U32Vec& writeBuffer, const U32ValVec& readBuffer ) const
289 {
290  return std::equal ( readBuffer.begin(), readBuffer.end(), writeBuffer.begin() );
291 }
292 
293 
294 // PRIVATE MEMBER FUNCTIONS - IPbus test functions that users can run
295 
297 {
298  if ( ! m_includeConnect )
299  {
300  BOOST_FOREACH ( ClientPtr& iClient, m_clients )
301  {
302  iClient->readBlock ( m_baseAddr, 1, defs::NON_INCREMENTAL );
303  iClient->dispatch();
304  }
305  }
306 
307  std::vector<ClientInterface*> lClients;
308  BOOST_FOREACH ( ClientPtr& iClient, m_clients )
309  {
310  lClients.push_back( &*iClient );
311  }
312 
313  double totalSeconds = measureReadLatency(lClients, m_baseAddr, m_bandwidthTestDepth, m_iterations, m_perIterationDispatch, m_verbose);
314  double totalPayloadKB = m_deviceURIs.size() * m_iterations * m_bandwidthTestDepth * 4. / 1024.;
315  double dataRateKB_s = totalPayloadKB/totalSeconds;
316  outputStandardResults ( totalSeconds );
317  cout << "Read depth used each iteration = " << m_bandwidthTestDepth << " 32-bit words\n"
318  << "Total IPbus payload received = " << totalPayloadKB << " KB\n"
319  << "Average read bandwidth = " << dataRateKB_s << " KB/s" << endl;
320 }
321 
322 
324 {
325  if ( ! m_includeConnect )
326  {
327  BOOST_FOREACH ( ClientPtr& iClient, m_clients )
328  {
329  iClient->writeBlock ( m_baseAddr, std::vector<uint32_t>(1,0x0), defs::NON_INCREMENTAL );
330  iClient->dispatch();
331  }
332  }
333 
334  std::vector<ClientInterface*> lClients;
335  BOOST_FOREACH ( ClientPtr& iClient, m_clients )
336  {
337  lClients.push_back( &*iClient );
338  }
339 
340  double totalSeconds = measureWriteLatency(lClients, m_baseAddr, m_bandwidthTestDepth, m_iterations, m_perIterationDispatch, m_verbose);
341  double totalPayloadKB = m_deviceURIs.size() * m_iterations * m_bandwidthTestDepth * 4. / 1024.;
342  double dataRateKB_s = totalPayloadKB/totalSeconds;
343  outputStandardResults ( totalSeconds );
344  cout << "Write depth used each iteration = " << m_bandwidthTestDepth << " 32-bit words\n"
345  << "Total IPbus payload sent = " << totalPayloadKB << " KB\n"
346  << "Average write bandwidth = " << dataRateKB_s << " KB/s" << endl;
347 }
348 
349 
351 {
352  std::vector<ClientInterface*> lClients;
353  for(ClientVec::iterator lIt = m_clients.begin(); lIt != m_clients.end(); lIt++)
354  lClients.push_back(&**lIt);
355 
356  if (! runValidationTest(lClients, m_baseAddr, m_bandwidthTestDepth, m_iterations, m_perIterationDispatch, m_verbose) )
357  std::exit( 1 );
358 }
359 
360 
362 {
363  try
364  {
365  BOOST_FOREACH ( ClientPtr& iClient, m_clients )
366  {
367  // *** Your code here ***
368  // For example:
369  ValWord<uint32_t> result = iClient->read ( m_baseAddr );
370  iClient->dispatch();
371  cout << "Read: " << std::hex << result.value() << " from address: " << m_baseAddr << std::dec << endl;
372  }
373  }
374  catch ( const std::exception& e )
375  {
376  cout << e.what() << endl;
377  }
378 }
379 
380 // END OF PerfTester MEMBER FUNCTIONS
381 
382 
383 
384 // The main() func...
385 int main ( int argc, char* argv[] )
386 {
387  uhal::tests::PerfTester perfTester;
388  return perfTester.run ( argc, argv );
389 }
tools.hpp
uhal::ValVector
A class which wraps a block of data and marks whether or not it is valid.
Definition: ValMem.hpp:75
uhal::tests::PerfTester::outputHelpText
void outputHelpText(const std::string &argDescriptions) const
Outputs the standard help text to screen.
Definition: PerfTester.cxx:181
uhal::dec
@ dec
Decimal.
Definition: log_inserters.integer.hpp:50
uhal::tests::PerfTester::m_testDescMap
TestDescMap m_testDescMap
Maps test name to test description.
Definition: PerfTester.hxx:109
boost::shared_ptr< uhal::ClientInterface >
uhal::tests::PerfTester::outputStandardResults
void outputStandardResults(double totalSeconds) const
Outputs a standard result set to screen - provide it with the number of seconds the test took.
Definition: PerfTester.cxx:275
uhal::ClientFactory::getInstance
static ClientFactory & getInstance()
Static method to retrieve the single instance of the class.
Definition: ClientFactory.cpp:65
uhal::tests::PerfTester::validationTest
void validationTest()
Historic basic firmware/software validation test.
Definition: PerfTester.cxx:350
uhal::tests::PerfTester::badInput
bool badInput() const
Returns true if the user has entered bad command line arguments.
Definition: PerfTester.cxx:211
uhal::ValVector::begin
const_iterator begin() const
If the memory has previously been marked as valid, return a const iterator to the beginning of the un...
Definition: ValMem.cpp:295
uhal::ValWord::value
T value() const
Return the value of the validated memory with check on validity.
Definition: ValMem.cpp:141
uhal::setLogLevelTo
void setLogLevelTo(const FatalLevel &)
Function to specify, at runtime, that only messages with a severity level above Fatal should be logge...
Definition: log.cpp:88
uhal::tests::PerfTester
Definition: PerfTester.hxx:59
uhal::tests::PerfTester::outputTestDescriptionsList
void outputTestDescriptionsList() const
Outputs the test names and descriptions to screen.
Definition: PerfTester.cxx:195
uhal::tests::measureWriteLatency
double measureWriteLatency(ClientInterface &aClient, uint32_t aBaseAddr, uint32_t aDepth, size_t aNrIterations, bool aDispatchEachIteration, bool aVerbose)
Definition: tools.cpp:111
uhal::tests::PerfTester::PerfTester
PerfTester()
Constructor - takes no arguments, does nothing.
Definition: PerfTester.cxx:61
uhal::tests::PerfTester::U32Vec
std::vector< U32 > U32Vec
A vector of unsigned 32-bit words.
Definition: PerfTester.hxx:83
uhal::hex
@ hex
Hexadecimal.
Definition: log_inserters.integer.hpp:51
uhal::Warning
WarningLevel Warning
Definition: LogLevels.cpp:79
PerfTester.hxx
uhal::tests::PerfTester::sandbox
void sandbox()
An area for a user-definable test.
Definition: PerfTester.cxx:361
uhal::tests::PerfTester::m_testFuncMap
TestFuncMap m_testFuncMap
Maps test name to test function.
Definition: PerfTester.hxx:110
uhal::defs::NON_INCREMENTAL
@ NON_INCREMENTAL
Definition: definitions.hpp:53
uhal::tests::PerfTester::run
int run(int argc, char *argv[])
Pass in the two command-line parameter variables, this will define the test that then gets run.
Definition: PerfTester.cxx:94
uhal::Debug
DebugLevel Debug
Definition: LogLevels.cpp:133
main
int main(int argc, char *argv[])
Definition: PerfTester.cxx:385
uhal::ValWord< uint32_t >
uhal::tests::PerfTester::outputUserChoices
void outputUserChoices() const
Outputs the user's choices to screen.
Definition: PerfTester.cxx:230
uhal::tests::PerfTester::bandwidthRxTest
void bandwidthRxTest()
Read bandwidth test.
Definition: PerfTester.cxx:296
uhal::tests::PerfTester::bandwidthTxTest
void bandwidthTxTest()
Write bandwidth test.
Definition: PerfTester.cxx:323
uhal::tests::PerfTester::buildClients
void buildClients()
Constructs and sets up the appropriate IPbusClient for use in the test.
Definition: PerfTester.cxx:249
uhal::ValVector::end
const_iterator end() const
If the memory has previously been marked as valid, return a const iterator to the end (one past last ...
Definition: ValMem.cpp:311
uhal::tests::PerfTester::buffersEqual
bool buffersEqual(const U32Vec &writeBuffer, const U32ValVec &readBuffer) const
Compares a write buffer with one or more ValVec read responses.
Definition: PerfTester.cxx:288
uhal::tests::measureReadLatency
double measureReadLatency(ClientInterface &aClient, uint32_t aBaseAddr, uint32_t aDepth, size_t aNrIterations, bool aDispatchEachIteration, bool aVerbose)
Definition: tools.cpp:67
ClientFactory.hpp