@@ -303,6 +303,9 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
303303 typename std::vector<next_alloc_data>::const_iterator;
304304 std::vector<std::unique_ptr<next_alloc_data_iterator>> next_iter;
305305 int64_t iterations;
306+ bool log_fragmentation;
307+ multiple_malloc_free_benchmark (bool log_fragmentation = true )
308+ : log_fragmentation(log_fragmentation) {}
306309
307310 public:
308311 void SetUp (::benchmark::State &state) override {
@@ -350,7 +353,7 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
350353 void TearDown (::benchmark::State &state) override {
351354 base::allocator.postBench (state);
352355 auto tid = state.thread_index ();
353- if (tid == 0 ) {
356+ if (tid == 0 && log_fragmentation ) {
354357 size_t current_memory_allocated = 0 ;
355358 for (const auto &allocationsPerThread : allocations) {
356359 for (const auto &allocation : allocationsPerThread) {
@@ -518,87 +521,88 @@ template <
518521class peak_alloc_benchmark
519522 : public multiple_malloc_free_benchmark<Size, Alloc> {
520523 using base = multiple_malloc_free_benchmark<Size, Alloc>;
521- virtual void prepareWorkload (benchmark::State &state) override {
522- // Retrieve the thread index and corresponding operation buffer.
523- auto tid = state.thread_index ();
524- auto &n = this ->next [tid];
525-
526- // Set up the random generators for index selection and decision making.
527- std::default_random_engine generator;
528- std::uniform_int_distribution<size_t > dist_offset (0 ,
529- this ->max_allocs - 1 );
530- std::uniform_real_distribution<double > dist_opt_type (0 , 1 );
531- generator.seed (0 );
532- auto sizeGenerator = this ->alloc_sizes [tid];
533-
534- n.clear ();
535- std::vector<size_t > free;
536- std::vector<size_t > allocated;
537- free.reserve (this ->max_allocs );
538- // Initially, all indices are available.
539- for (size_t i = 0 ; i < this ->max_allocs ; i++) {
540- free.push_back (i);
541- }
542-
543- // Total number of allocation/free operations to simulate.
544- int64_t operations_number =
545- state.max_iterations * this ->allocsPerIterations ;
546- for (int64_t j = 0 ; j < operations_number; j++) {
547- int64_t target_allocation;
548-
549- // Determine the target number of allocations based on the progress of the iterations.
550- // In the first half of the iterations, the target allocation increases linearly.
551- // In the second half, it decreases linearly.
552- if (j < operations_number / 2 ) {
553- target_allocation = 2 * static_cast <int64_t >(this ->max_allocs ) *
554- j / operations_number;
555- } else {
556- target_allocation = -2 *
557- static_cast <int64_t >(this ->max_allocs ) *
558- j / operations_number +
559- 2 * static_cast <int64_t >(this ->max_allocs );
560- }
561-
562- // x represents the gap between the target and current allocations.
563- auto x = static_cast <double >(target_allocation -
564- static_cast <double >(allocated.size ()));
565-
566- // Use a normal CDF with high sigma so that when x is positive,
567- // we are slightly more likely to allocate,
568- // and when x is negative, slightly more likely to free memory,
569- // keeping the overall change gradual.
570-
571- const double sigma = 1000 ;
572- auto cdf = normalCDF (x, sigma);
573-
574- // Decide whether to allocate or free:
575- // - If no allocations exist, allocation is forced.
576- // - If there is maximum number of allocation, free is forced
577- // - Otherwise, Based on the computed probability, choose whether to allocate or free
578- if (allocated.empty () ||
579- (!free.empty () && cdf > dist_opt_type (generator))) {
580- // Allocation
581- std::swap (free[dist_offset (generator) % free.size ()],
582- free.back ());
583- auto offset = free.back ();
584- free.pop_back ();
585- n.push_back ({true , offset, sizeGenerator.nextSize ()});
586- allocated.push_back (offset);
587- } else {
588- // Free
589- std::swap (allocated[dist_offset (generator) % allocated.size ()],
590- allocated.back ());
591- auto offset = allocated.back ();
592- allocated.pop_back ();
593- n.push_back ({false , offset, 0 });
594- free.push_back (offset);
595- }
596- }
597-
598- this ->next_iter [tid] =
599- std::make_unique<std::vector<next_alloc_data>::const_iterator>(
600- n.cbegin ());
601- }
524+ public:
525+ peak_alloc_benchmark () : base(false ) {}
526+ virtual void prepareWorkload (benchmark::State &state) override {
527+ // Retrieve the thread index and corresponding operation buffer.
528+ auto tid = state.thread_index ();
529+ auto &n = this ->next [tid];
530+
531+ // Set up the random generators for index selection and decision making.
532+ std::default_random_engine generator;
533+ std::uniform_int_distribution<size_t > dist_offset (0 ,
534+ this ->max_allocs - 1 );
535+ std::uniform_real_distribution<double > dist_opt_type (0 , 1 );
536+ generator.seed (0 );
537+ auto sizeGenerator = this ->alloc_sizes [tid];
538+
539+ n.clear ();
540+ std::vector<size_t > free;
541+ std::vector<size_t > allocated;
542+ free.reserve (this ->max_allocs );
543+ // Initially, all indices are available.
544+ for (size_t i = 0 ; i < this ->max_allocs ; i++) {
545+ free.push_back (i);
546+ }
547+
548+ // Total number of allocation/free operations to simulate.
549+ int64_t operations_number =
550+ state.max_iterations * this ->allocsPerIterations ;
551+ for (int64_t j = 0 ; j < operations_number; j++) {
552+ int64_t target_allocation;
553+
554+ // Determine the target number of allocations based on the progress of the iterations.
555+ // In the first half of the iterations, the target allocation increases linearly.
556+ // In the second half, it decreases linearly.
557+ if (j < operations_number / 2 ) {
558+ target_allocation = 2 * static_cast <int64_t >(this ->max_allocs ) *
559+ j / operations_number;
560+ } else {
561+ target_allocation = -2 * static_cast <int64_t >(this ->max_allocs ) *
562+ j / operations_number +
563+ 2 * static_cast <int64_t >(this ->max_allocs );
564+ }
565+
566+ // x represents the gap between the target and current allocations.
567+ auto x = static_cast <double >(target_allocation -
568+ static_cast <double >(allocated.size ()));
569+
570+ // Use a normal CDF with high sigma so that when x is positive,
571+ // we are slightly more likely to allocate,
572+ // and when x is negative, slightly more likely to free memory,
573+ // keeping the overall change gradual.
574+
575+ const double sigma = 1000 ;
576+ auto cdf = normalCDF (x, sigma);
577+
578+ // Decide whether to allocate or free:
579+ // - If no allocations exist, allocation is forced.
580+ // - If there is maximum number of allocation, free is forced
581+ // - Otherwise, Based on the computed probability, choose whether to allocate or free
582+ if (allocated.empty () ||
583+ (!free.empty () && cdf > dist_opt_type (generator))) {
584+ // Allocation
585+ std::swap (free[dist_offset (generator) % free.size ()],
586+ free.back ());
587+ auto offset = free.back ();
588+ free.pop_back ();
589+ n.push_back ({true , offset, sizeGenerator.nextSize ()});
590+ allocated.push_back (offset);
591+ } else {
592+ // Free
593+ std::swap (allocated[dist_offset (generator) % allocated.size ()],
594+ allocated.back ());
595+ auto offset = allocated.back ();
596+ allocated.pop_back ();
597+ n.push_back ({false , offset, 0 });
598+ free.push_back (offset);
599+ }
600+ }
601+
602+ this ->next_iter [tid] =
603+ std::make_unique<std::vector<next_alloc_data>::const_iterator>(
604+ n.cbegin ());
605+ }
602606
603607 virtual void prealloc (benchmark::State &state) {
604608 auto tid = state.thread_index ();
0 commit comments