BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslmt_throughputbenchmark.h
Go to the documentation of this file.
1/// @file bslmt_throughputbenchmark.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_throughputbenchmark.h -*-C++-*-
8
9#ifndef INCLUDED_BSLMT_THROUGHPUTBENCHMARK
10#define INCLUDED_BSLMT_THROUGHPUTBENCHMARK
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bslmt_throughputbenchmark bslmt_throughputbenchmark
16/// @brief Provide a performance test harness for multi-threaded components.
17/// @addtogroup bsl
18/// @{
19/// @addtogroup bslmt
20/// @{
21/// @addtogroup bslmt_throughputbenchmark
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bslmt_throughputbenchmark-purpose"> Purpose</a>
26/// * <a href="#bslmt_throughputbenchmark-classes"> Classes </a>
27/// * <a href="#bslmt_throughputbenchmark-description"> Description </a>
28/// * <a href="#bslmt_throughputbenchmark-structure-of-a-test"> Structure of a Test </a>
29/// * <a href="#bslmt_throughputbenchmark-usage"> Usage </a>
30/// * <a href="#bslmt_throughputbenchmark-example-1-test-performance-of-bsl-queue-int"> Example 1: Test Performance of bsl::queue<int> </a>
31///
32/// # Purpose {#bslmt_throughputbenchmark-purpose}
33/// Provide a performance test harness for multi-threaded components.
34///
35/// # Classes {#bslmt_throughputbenchmark-classes}
36///
37/// - bslmt::ThroughputBenchmark: multi-threaded performance test harness
38///
39/// # Description {#bslmt_throughputbenchmark-description}
40/// This component defines a mechanism,
41/// `bslmt::ThroughputBenchmark`, that provides performance testing for multi-
42/// threaded components. The results are loaded into a
43/// `bslmt::ThroughputBenchmarkResult` object, which provides access to counts
44/// of the work done by each thread, thread group, and sample, divided by the
45/// number of actual seconds of execution.
46///
47/// ## Structure of a Test {#bslmt_throughputbenchmark-structure-of-a-test}
48///
49///
50/// A test is composed from one or more thread groups, each running one or more
51/// threads. Each thread in a thread group executes a thread function, with a
52/// simulated work load executing between subsequent calls to the thread
53/// function. To provide reliability, the test is executed multiple times. A
54/// single execution of a test is referred to as a "sample execution" and its
55/// result referred to as a "sample". To support fine tuning of the test, it is
56/// possible to provide initialize and cleanup functions for a sample and / or a
57/// thread.
58///
59/// ## Usage {#bslmt_throughputbenchmark-usage}
60///
61///
62/// This section illustrates intended use of this component.
63///
64/// ### Example 1: Test Performance of bsl::queue<int> {#bslmt_throughputbenchmark-example-1-test-performance-of-bsl-queue-int}
65///
66///
67/// In the following example we test the throughput of a `bsl::queue<int>` in a
68/// multi-threaded environment, where multiple "producer" threads are pushing
69/// elements, and multiple "consumer" threads are popping these elements.
70///
71/// First, we define a global queue, a mutex to protect this queue, and a
72/// semaphore for a "pop" operation to block on:
73/// @code
74/// bsl::queue<int> myQueue;
75/// bslmt::Mutex myMutex;
76/// bslmt::Semaphore mySem;
77/// @endcode
78/// Next, we define a counter value we push in:
79/// @code
80/// int counterValue = 0;
81/// @endcode
82/// Then, we define simple push and pop functions that manipulate this queue:
83/// @code
84/// /// Push an element into `myQueue`, using the specified `threadIndex`.
85/// void myPush(int threadIndex)
86/// {
87/// bslmt::LockGuard<bslmt::Mutex> guard(&myMutex);
88/// myQueue.push(1000000 * threadIndex + counterValue++);
89/// mySem.post();
90/// }
91///
92/// /// Pop an element from `myQueue`.
93/// void myPop(int)
94/// {
95/// mySem.wait();
96/// bslmt::LockGuard<bslmt::Mutex> guard(&myMutex);
97/// myQueue.pop();
98/// }
99/// @endcode
100/// Next, we define a thread "cleanup" function for the push thread group, which
101/// pushes a couple of extra elements to make sure that the pop thread group
102/// will not hang on an empty queue:
103/// @code
104/// /// Cleanup function.
105/// void myCleanup()
106/// {
107/// bslmt::LockGuard<bslmt::Mutex> guard(&myMutex);
108/// for (int i = 0; i < 10; ++i) {
109/// myQueue.push(counterValue++);
110/// mySem.post();
111/// }
112/// }
113/// @endcode
114/// Then, we create a `bslmt::ThroughputBenchmark` object and add push and pop
115/// thread groups, each with 2 threads and a work load (arithmetic operations to
116/// consume an amount of time) of 100:
117/// @code
118/// bslmt::ThroughputBenchmark myBench;
119/// myBench.addThreadGroup(
120/// myPush,
121/// 2,
122/// 100,
123/// bslmt::ThroughputBenchmark::InitializeThreadFunction(),
124/// myCleanup);
125/// const int consumerGroupIdx = myBench.addThreadGroup(myPop, 2, 100);
126/// @endcode
127/// Now, we create a `bslmt::ThroughputBenchmarkResult` object to contain the
128/// result, and call `execute` to run the benchmark for 500 millseconds 10
129/// times:
130/// @code
131/// bslmt::ThroughputBenchmarkResult myResult;
132/// myBench.execute(&myResult, 500, 10);
133/// @endcode
134/// Finally, we print the median of the throughput of the consumer thread group.
135/// @code
136/// double median;
137/// myResult.getMedian(&median, consumerGroupIdx);
138/// bsl::cout << "Throughput:" << median << "\n";
139/// @endcode
140/// @}
141/** @} */
142/** @} */
143
144/** @addtogroup bsl
145 * @{
146 */
147/** @addtogroup bslmt
148 * @{
149 */
150/** @addtogroup bslmt_throughputbenchmark
151 * @{
152 */
153
154#include <bslscm_version.h>
155
156#include <bslmt_barrier.h>
158
159#include <bslma_allocator.h>
160
162
163#include <bsls_assert.h>
164#include <bsls_atomic.h>
165#include <bsls_timeinterval.h>
166#include <bsls_types.h>
167
168#include <bsl_functional.h>
169#include <bsl_vector.h>
170
171
172namespace bslmt {
173
174class ThroughputBenchmark_TestUtil;
175
176 // =========================
177 // class ThroughputBenchmark
178 // =========================
179
180/// This class is a mechanism that provides performance testing for multi-
181/// threaded components. It allows running different thread functions at
182/// the same time, and simulates a work load between subsequent calls to the
183/// tested thread functions. The results are loaded into a
184/// `bslmt::ThroughputBenchmarkResult` object, which provides access to
185/// counts of the work done by each thread, thread group, and sample,
186/// divided by the number of actual seconds of execution.
187///
188/// See @ref bslmt_throughputbenchmark
190
191 public:
192 // PUBLIC TYPES
193
194 /// An alias to a function meeting the following contract:
195 /// @code
196 /// /// Run the main part of the benchmark having the specified /// `threadIndex`.
197 ///
198 /// \pre The behavior is undefined unless `threadIndex` is
199 /// /// in the range `[0, numThreadsInGroup)`, where `numThreadsInGroup` is
200 /// /// the number of threads in a thread group for the associated
201 /// /// throughput benchmark.
202 /// void runTest(int threadIndex);
203 /// @endcode
204 typedef bsl::function<void(int)> RunFunction;
205
206 /// An alias to a function meeting the following contract:
207 /// @code
208 /// /// Initialize the sample run. If the specified `isFirst` is `true`,
209 /// /// this is the first sample run.
210 /// void initializeSample(bool isFirst);
211 /// @endcode
213
214 /// An alias to a function meeting the following contract:
215 /// @code
216 /// /// Clean up at the end of the sample run, before threads have been
217 /// /// joined. If the specified `isLast` is `true`, this is the last
218 /// /// sample run.
219 /// void shutdownSample(bool isLast);
220 /// @endcode
222
223 /// An alias to a function meeting the following contract:
224 /// @code
225 /// /// Clean up after the sample run. If the specified `isLast` is
226 /// /// `true`, this is the last sample run.
227 /// void cleanupSample(bool isLast);
228 /// @endcode
230
231 /// An alias to a function meeting the following contract:
232 /// @code
233 /// /// Initialize each thread in a sample run.
234 /// void initializeThread();
235 /// @endcode
237
238 /// An alias to a function meeting the following contract:
239 /// @code
240 /// /// Clean up after each thread in a sample run.
241 /// void cleanupThread();
242 /// @endcode
244
245 /// Data used by a thread group
246 ///
247 /// See @ref bslmt_throughputbenchmark
248 struct ThreadGroup {
249
250 // PUBLIC DATA
251 RunFunction d_func; // test function to run
252
253 int d_numThreads; // number of threads in the
254 // thread group
255
256 bsls::Types::Int64 d_amount; // amount of busy work to
257 // perform between calls to
258 // 'd_func'
259
260 InitializeThreadFunction d_initialize; // initialize function per
261 // thread
262
263 CleanupThreadFunction d_cleanup; // cleanup function per
264 // thread
265 };
266
267 private:
268 // CLASS DATA
269 static bsls::AtomicUint s_antiOptimization; // Used by 'busyWork' to
270 // prevent optimization.
271
272 // DATA
273 bsl::vector<ThreadGroup> d_threadGroups; // Data kept for each thread
274 // group added.
275
276 bsls::AtomicInt d_state; // This is how a test thread
277 // knows it has to exit. It
278 // starts as 0, and exits
279 // when is set to 1.
280
281 // FRIENDS
284
285 private:
286 // NOT IMPLEMENTED
288 ThroughputBenchmark& operator=(const ThroughputBenchmark&);
289
290 // PRIVATE ACCESSORS
291
292 /// Return `true` if the test should continue to run, and `false`
293 /// otherwise.
294 bool isRunState() const;
295
296 public:
297 // TRAITS
300
301 // CLASS METHODS
302
303 /// Return the value calculated by `busyWork`.
304 /// \note Note that this method is
305 /// provided to prevent the compiler from optimizing the simulated workload
306 /// away.
307 static unsigned int antiOptimization();
308
309 /// Perform arithmetic operations to consume an amount of time in linear relation to the specified `busyWorkAmount`.
310 ///
311 /// \note Note that the duration of
312 /// `busyWork` invoked with a particular `busyWorkAmount` will vary with
313 /// system load.
314 static void busyWork(bsls::Types::Int64 busyWorkAmount);
315
316 /// Return an estimate of the work amount so that `busyWork` invoked with
317 /// the returned work amount executes, approximately, for the specified `duration`.
318 ///
319 /// \note Note that this estimate varies with system load.
321 bsls::TimeInterval duration);
322
323 // CREATORS
324
325 /// Create an empty `ThroughputBenchmark` object. Optionally specify a
326 /// `basicAllocator` used to supply memory. If `basicAllocator` is 0, the
327 /// currently installed default allocator is used.
328 explicit ThroughputBenchmark(bslma::Allocator *basicAllocator = 0);
329
330 // MANIPULATORS
331
332 /// Create a set of threads, with cardinality the specified `numThreads`,
333 /// that will repeatedly execute the specified `runFunction` followed by
334 /// the specified `busyWork`, with the specified `busyWorkAmount` as its
335 /// argument. Return the index for the thread group. Optionally specify
336 /// `initializeFunctor`, which is run at the beginning of each thread of
337 /// the sample and accepts a boolean flag `isFirst`, that is set to `true`
338 /// on the first sample, and `false` otherwise. Optionally specify
339 /// `cleanupFunctor`, which is run at the end of each thread of the sample
340 /// and accepts a boolean flag `isLast`, that is set to `true` on the last
341 /// sample, and `false` otherwise. Return an id for the added thread group.
342 ///
343 /// \pre The behavior is undefined unless `0 < numThreads` and
344 /// `0 <= busyWorkAmount`.
345 int addThreadGroup(const RunFunction& runFunction,
346 int numThreads,
347 bsls::Types::Int64 busyWorkAmount);
348 int addThreadGroup(const RunFunction& runFunction,
349 int numThreads,
350 bsls::Types::Int64 busyWorkAmount,
351 const InitializeThreadFunction& initializeFunctor,
352 const CleanupThreadFunction& cleanupFunctor);
353
354 /// Run the tests previously added with calls to the `addThreadGroup`
355 /// method. The tests are run for the specified `numSamples` times. Each
356 /// sample is run for the specified `millisecondsPerSample` duration. The
357 /// results are stored in the specified `result` object. Optionally
358 /// specify `initializeFunctor`, which is run at the beginning of the
359 /// sample and accepts a boolean flag `isFirst`, that is set to `true` on
360 /// the first sample, and `false` otherwise. Optionally specify
361 /// `shutdownFunctor`, which is run at the end of each sample before
362 /// threads have been joined, and accepts a boolean flag `isLast`, that is
363 /// set to `true` on the last sample, and `false` otherwise. Optionally
364 /// specify `cleanupFunctor`, which is run at the end of each sample after
365 /// threads have been joined, and accepts a boolean flag `isLast`, that is
366 /// set to `true` on the last sample, and `false` otherwise.
367 ///
368 /// \pre The behavior is undefined unless `0 < millisecondsPerSample`, `0 < numSamples`, and
369 /// `0 < numThreadGroups()`. Also see @ref bslmt_throughputbenchmark-structure-of-a-test .
371 int millisecondsPerSample,
372 int numSamples);
374 int millisecondsPerSample,
375 int numSamples,
376 const InitializeSampleFunction& initializeFunctor,
377 const ShutdownSampleFunction& shutdownFunctor,
378 const CleanupSampleFunction& cleanupFunctor);
379
380 // ACCESSORS
381
382 /// Return the total number of threads.
383 int numThreads() const;
384
385 /// Return the number of thread groups.
386 int numThreadGroups() const;
387
388 /// Return the number of threads in the specified `threadGroupIndex`.
389 ///
390 /// \pre The behavior is undefined unless
391 /// `0 <= threadGroupIndex < numThreadGroups()`.
392 int numThreadsInGroup(int threadGroupIndex) const;
393
394 // Aspects
395
396 /// Return the allocator used by this object.
398
399};
400
401 // ===================================
402 // struct ThroughputBenchmark_WorkData
403 // ===================================
404
405/// Data transferred to ThroughputBenchmark_WorkFunction.
406///
407/// See @ref bslmt_throughputbenchmark
409
410 // PUBLIC DATA
412 // test function to run
413
415 // busy work amount
416
418 // initialize function per
419 // thread
420
422 // cleanup function per
423 // thread
424
426 // exposes the "this"
427 // pointer of the benchmark
428 // to the work thread
429
431 // thread index 0, 1, 2,
432 // ... that is provided to
433 // the thread to
434 // differentiate it if so
435 // desired
436
438 // trigger start for
439 // threads to start
440 // processing at the same
441 // time
442
444 // number of nanoseconds
445 // that the thread actually
446 // ran
447
449 // number of items
450 // processed by this thread
451};
452
453 // ======================================
454 // class ThroughputBenchmark_WorkFunction
455 // ======================================
456
457/// This class is the work function functor, being called for each work
458/// thread.
459///
460/// See @ref bslmt_throughputbenchmark
462
463 private:
464 // DATA
466
467 public:
468 // CREATORS
469
470 /// Create a `ThroughputBenchmark_WorkFunction` object with the
471 /// specified `data` argument.
474
475 /// Destroy this object.
477
478 // MANIPULATORS
479
480 /// Work function being run on the thread.
482};
483
484 // ==================================
485 // class ThroughputBenchmark_TestUtil
486 // ==================================
487
488/// This class implements a test utility that gives the test driver access
489/// to the unexposed data members of `ThroughputBenchmark`.
490///
491/// See @ref bslmt_throughputbenchmark
493
494 // DATA
495 ThroughputBenchmark& d_data;
496
497 public:
498 // CREATORS
499
500 /// Create a `ThroughputBenchmark_TestUtil` object to test contents of
501 /// the specified `data`.
503
504 /// Destroy this object.
506
507 // MANIPULATORS
508
509 /// Return a reference providing modifiable access to the `d_state` data
510 /// member of `ThroughputBenchmark`.
512
513 /// Return a reference providing modifiable access to the
514 /// `d_threadGroups` data member of `ThroughputBenchmark`.
516};
517
518// ============================================================================
519// INLINE DEFINITIONS
520// ============================================================================
521
522 // -------------------------
523 // class ThroughputBenchmark
524 // -------------------------
525
526// PRIVATE ACCESSORS
527inline
528bool ThroughputBenchmark::isRunState() const
529{
530 return d_state.loadAcquire() == 0;
531}
532
533// ACCESSORS
534inline
536{
537 if (0 == d_threadGroups.size()) {
538 return 0; // RETURN
539 }
540
541 int numThreads = d_threadGroups[0].d_numThreads;
542 for (int i = 1; i < numThreadGroups(); ++i) {
543 numThreads += d_threadGroups[i].d_numThreads;
544 }
545 return numThreads;
546}
547
548inline
550{
551 return static_cast<int>(d_threadGroups.size());
552}
553
554inline
555int ThroughputBenchmark::numThreadsInGroup(int threadGroupIndex) const
556{
557 BSLS_ASSERT(0 <= threadGroupIndex);
558 BSLS_ASSERT(numThreadGroups() > threadGroupIndex);
559
560 return d_threadGroups[threadGroupIndex].d_numThreads;
561}
562
563 // Aspects
564
565inline
567{
568 return d_threadGroups.get_allocator().mechanism();
569}
570
571 // --------------------------------------
572 // class ThroughputBenchmark_WorkFunction
573 // --------------------------------------
574
575// CREATORS
576inline
582
583 // ----------------------------------
584 // class ThroughputBenchmark_TestUtil
585 // ----------------------------------
586
587// CREATORS
588inline
594
595// MANIPULATORS
596inline
598{
599 return d_data.d_state;
600}
601
602inline
605{
606 return d_data.d_threadGroups;
607}
608
609} // close package namespace
610
611
612#endif
613
614// ----------------------------------------------------------------------------
615// Copyright 2019 Bloomberg Finance L.P.
616//
617// Licensed under the Apache License, Version 2.0 (the "License");
618// you may not use this file except in compliance with the License.
619// You may obtain a copy of the License at
620//
621// http://www.apache.org/licenses/LICENSE-2.0
622//
623// Unless required by applicable law or agreed to in writing, software
624// distributed under the License is distributed on an "AS IS" BASIS,
625// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
626// See the License for the specific language governing permissions and
627// limitations under the License.
628// ----------------------------- END-OF-FILE ----------------------------------
629
630/** @} */
631/** @} */
632/** @} */
Forward declaration.
Definition bslstl_function.h:946
Definition bslstl_vector.h:1120
Definition bslma_allocator.h:545
Definition bslmt_barrier.h:353
Definition bslmt_throughputbenchmarkresult.h:140
Definition bslmt_throughputbenchmark.h:492
~ThroughputBenchmark_TestUtil()=default
Destroy this object.
ThroughputBenchmark_TestUtil(ThroughputBenchmark &data)
Definition bslmt_throughputbenchmark.h:589
bsl::vector< ThroughputBenchmark::ThreadGroup > & threadGroups()
Definition bslmt_throughputbenchmark.h:604
bsls::AtomicInt & state()
Definition bslmt_throughputbenchmark.h:597
Definition bslmt_throughputbenchmark.h:461
void operator()()
Work function being run on the thread.
ThroughputBenchmark_WorkFunction(ThroughputBenchmark_WorkData &data)
Definition bslmt_throughputbenchmark.h:577
~ThroughputBenchmark_WorkFunction()=default
Destroy this object.
Definition bslmt_throughputbenchmark.h:189
bsl::function< void(bool)> ShutdownSampleFunction
Definition bslmt_throughputbenchmark.h:221
void execute(ThroughputBenchmarkResult *result, int millisecondsPerSample, int numSamples)
bsl::function< void()> InitializeThreadFunction
Definition bslmt_throughputbenchmark.h:236
bsl::function< void(bool)> CleanupSampleFunction
Definition bslmt_throughputbenchmark.h:229
int numThreadsInGroup(int threadGroupIndex) const
Definition bslmt_throughputbenchmark.h:555
int numThreadGroups() const
Return the number of thread groups.
Definition bslmt_throughputbenchmark.h:549
bsl::function< void()> CleanupThreadFunction
Definition bslmt_throughputbenchmark.h:243
bsl::function< void(bool)> InitializeSampleFunction
Definition bslmt_throughputbenchmark.h:212
static unsigned int antiOptimization()
ThroughputBenchmark(bslma::Allocator *basicAllocator=0)
void execute(ThroughputBenchmarkResult *result, int millisecondsPerSample, int numSamples, const InitializeSampleFunction &initializeFunctor, const ShutdownSampleFunction &shutdownFunctor, const CleanupSampleFunction &cleanupFunctor)
static bsls::Types::Int64 estimateBusyWorkAmount(bsls::TimeInterval duration)
BSLMF_NESTED_TRAIT_DECLARATION(ThroughputBenchmark, bslma::UsesBslmaAllocator)
int addThreadGroup(const RunFunction &runFunction, int numThreads, bsls::Types::Int64 busyWorkAmount, const InitializeThreadFunction &initializeFunctor, const CleanupThreadFunction &cleanupFunctor)
bslma::Allocator * allocator() const
Return the allocator used by this object.
Definition bslmt_throughputbenchmark.h:566
int addThreadGroup(const RunFunction &runFunction, int numThreads, bsls::Types::Int64 busyWorkAmount)
bsl::function< void(int)> RunFunction
Definition bslmt_throughputbenchmark.h:204
int numThreads() const
Return the total number of threads.
Definition bslmt_throughputbenchmark.h:535
static void busyWork(bsls::Types::Int64 busyWorkAmount)
Definition bsls_atomic.h:744
int loadAcquire() const
Definition bsls_atomic.h:1753
Definition bsls_atomic.h:1050
Definition bsls_timeinterval.h:307
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bslmt_barrier.h:344
Definition bslma_usesbslmaallocator.h:344
Definition bslmt_throughputbenchmark.h:248
InitializeThreadFunction d_initialize
Definition bslmt_throughputbenchmark.h:260
CleanupThreadFunction d_cleanup
Definition bslmt_throughputbenchmark.h:263
int d_numThreads
Definition bslmt_throughputbenchmark.h:253
RunFunction d_func
Definition bslmt_throughputbenchmark.h:251
bsls::Types::Int64 d_amount
Definition bslmt_throughputbenchmark.h:256
Definition bslmt_throughputbenchmark.h:408
ThroughputBenchmark::InitializeThreadFunction d_initialize
Definition bslmt_throughputbenchmark.h:417
bsls::Types::Int64 d_amount
Definition bslmt_throughputbenchmark.h:414
bslmt::Barrier * d_barrier_p
Definition bslmt_throughputbenchmark.h:437
bsls::Types::Int64 d_actualNanos
Definition bslmt_throughputbenchmark.h:443
int d_threadIndex
Definition bslmt_throughputbenchmark.h:430
ThroughputBenchmark::CleanupThreadFunction d_cleanup
Definition bslmt_throughputbenchmark.h:421
ThroughputBenchmark * d_bench_p
Definition bslmt_throughputbenchmark.h:425
bsls::Types::Int64 d_count
Definition bslmt_throughputbenchmark.h:448
ThroughputBenchmark::RunFunction d_func
Definition bslmt_throughputbenchmark.h:411
long long Int64
Definition bsls_types.h:134