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