xref: /aosp_15_r20/bionic/benchmarks/malloc_benchmark.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker  * Copyright (C) 2019 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker  * All rights reserved.
4*8d67ca89SAndroid Build Coastguard Worker  *
5*8d67ca89SAndroid Build Coastguard Worker  * Redistribution and use in source and binary forms, with or without
6*8d67ca89SAndroid Build Coastguard Worker  * modification, are permitted provided that the following conditions
7*8d67ca89SAndroid Build Coastguard Worker  * are met:
8*8d67ca89SAndroid Build Coastguard Worker  *  * Redistributions of source code must retain the above copyright
9*8d67ca89SAndroid Build Coastguard Worker  *    notice, this list of conditions and the following disclaimer.
10*8d67ca89SAndroid Build Coastguard Worker  *  * Redistributions in binary form must reproduce the above copyright
11*8d67ca89SAndroid Build Coastguard Worker  *    notice, this list of conditions and the following disclaimer in
12*8d67ca89SAndroid Build Coastguard Worker  *    the documentation and/or other materials provided with the
13*8d67ca89SAndroid Build Coastguard Worker  *    distribution.
14*8d67ca89SAndroid Build Coastguard Worker  *
15*8d67ca89SAndroid Build Coastguard Worker  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16*8d67ca89SAndroid Build Coastguard Worker  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17*8d67ca89SAndroid Build Coastguard Worker  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18*8d67ca89SAndroid Build Coastguard Worker  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19*8d67ca89SAndroid Build Coastguard Worker  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20*8d67ca89SAndroid Build Coastguard Worker  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*8d67ca89SAndroid Build Coastguard Worker  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22*8d67ca89SAndroid Build Coastguard Worker  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*8d67ca89SAndroid Build Coastguard Worker  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*8d67ca89SAndroid Build Coastguard Worker  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25*8d67ca89SAndroid Build Coastguard Worker  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*8d67ca89SAndroid Build Coastguard Worker  * SUCH DAMAGE.
27*8d67ca89SAndroid Build Coastguard Worker  */
28*8d67ca89SAndroid Build Coastguard Worker 
29*8d67ca89SAndroid Build Coastguard Worker #include <malloc.h>
30*8d67ca89SAndroid Build Coastguard Worker #include <unistd.h>
31*8d67ca89SAndroid Build Coastguard Worker 
32*8d67ca89SAndroid Build Coastguard Worker #include <condition_variable>
33*8d67ca89SAndroid Build Coastguard Worker #include <mutex>
34*8d67ca89SAndroid Build Coastguard Worker #include <random>
35*8d67ca89SAndroid Build Coastguard Worker #include <thread>
36*8d67ca89SAndroid Build Coastguard Worker #include <vector>
37*8d67ca89SAndroid Build Coastguard Worker 
38*8d67ca89SAndroid Build Coastguard Worker #include <benchmark/benchmark.h>
39*8d67ca89SAndroid Build Coastguard Worker #include "ScopedDecayTimeRestorer.h"
40*8d67ca89SAndroid Build Coastguard Worker #include "util.h"
41*8d67ca89SAndroid Build Coastguard Worker 
42*8d67ca89SAndroid Build Coastguard Worker #if defined(__BIONIC__)
43*8d67ca89SAndroid Build Coastguard Worker 
RunMalloptPurge(benchmark::State & state,int purge_value)44*8d67ca89SAndroid Build Coastguard Worker static void RunMalloptPurge(benchmark::State& state, int purge_value) {
45*8d67ca89SAndroid Build Coastguard Worker   ScopedDecayTimeRestorer restorer;
46*8d67ca89SAndroid Build Coastguard Worker 
47*8d67ca89SAndroid Build Coastguard Worker   static size_t sizes[] = {8, 16, 32, 64, 128, 1024, 4096, 16384, 65536, 131072, 1048576};
48*8d67ca89SAndroid Build Coastguard Worker   static int pagesize = getpagesize();
49*8d67ca89SAndroid Build Coastguard Worker   mallopt(M_DECAY_TIME, 1);
50*8d67ca89SAndroid Build Coastguard Worker   mallopt(M_PURGE_ALL, 0);
51*8d67ca89SAndroid Build Coastguard Worker   for (auto _ : state) {
52*8d67ca89SAndroid Build Coastguard Worker     state.PauseTiming();
53*8d67ca89SAndroid Build Coastguard Worker     std::vector<void*> ptrs;
54*8d67ca89SAndroid Build Coastguard Worker     for (auto size : sizes) {
55*8d67ca89SAndroid Build Coastguard Worker       // Allocate at least two pages worth of the allocations.
56*8d67ca89SAndroid Build Coastguard Worker       for (size_t allocated = 0; allocated < 2 * static_cast<size_t>(pagesize); allocated += size) {
57*8d67ca89SAndroid Build Coastguard Worker         void* ptr = malloc(size);
58*8d67ca89SAndroid Build Coastguard Worker         if (ptr == nullptr) {
59*8d67ca89SAndroid Build Coastguard Worker           state.SkipWithError("Failed to allocate memory");
60*8d67ca89SAndroid Build Coastguard Worker         }
61*8d67ca89SAndroid Build Coastguard Worker         MakeAllocationResident(ptr, size, pagesize);
62*8d67ca89SAndroid Build Coastguard Worker         ptrs.push_back(ptr);
63*8d67ca89SAndroid Build Coastguard Worker       }
64*8d67ca89SAndroid Build Coastguard Worker     }
65*8d67ca89SAndroid Build Coastguard Worker     // Free the memory, which should leave many of the pages resident until
66*8d67ca89SAndroid Build Coastguard Worker     // the purge call.
67*8d67ca89SAndroid Build Coastguard Worker     for (auto ptr : ptrs) {
68*8d67ca89SAndroid Build Coastguard Worker       free(ptr);
69*8d67ca89SAndroid Build Coastguard Worker     }
70*8d67ca89SAndroid Build Coastguard Worker     ptrs.clear();
71*8d67ca89SAndroid Build Coastguard Worker     state.ResumeTiming();
72*8d67ca89SAndroid Build Coastguard Worker 
73*8d67ca89SAndroid Build Coastguard Worker     mallopt(purge_value, 0);
74*8d67ca89SAndroid Build Coastguard Worker   }
75*8d67ca89SAndroid Build Coastguard Worker }
76*8d67ca89SAndroid Build Coastguard Worker 
RunThreadsThroughput(benchmark::State & state,size_t size,size_t num_threads)77*8d67ca89SAndroid Build Coastguard Worker static void RunThreadsThroughput(benchmark::State& state, size_t size, size_t num_threads) {
78*8d67ca89SAndroid Build Coastguard Worker   constexpr size_t kMaxBytes = 1 << 24;
79*8d67ca89SAndroid Build Coastguard Worker   constexpr size_t kMaxThreads = 8;
80*8d67ca89SAndroid Build Coastguard Worker   constexpr size_t kMinRounds = 4;
81*8d67ca89SAndroid Build Coastguard Worker   const size_t MaxAllocCounts = kMaxBytes / size;
82*8d67ca89SAndroid Build Coastguard Worker   std::mutex m;
83*8d67ca89SAndroid Build Coastguard Worker   bool ready = false;
84*8d67ca89SAndroid Build Coastguard Worker   std::condition_variable cv;
85*8d67ca89SAndroid Build Coastguard Worker   std::thread* threads[kMaxThreads];
86*8d67ca89SAndroid Build Coastguard Worker 
87*8d67ca89SAndroid Build Coastguard Worker   // The goal is to create malloc/free interleaving patterns across threads.
88*8d67ca89SAndroid Build Coastguard Worker   // The bytes processed by each thread will be the same. The difference is the
89*8d67ca89SAndroid Build Coastguard Worker   // patterns. Here's an example:
90*8d67ca89SAndroid Build Coastguard Worker   //
91*8d67ca89SAndroid Build Coastguard Worker   // A: Allocation
92*8d67ca89SAndroid Build Coastguard Worker   // D: Deallocation
93*8d67ca89SAndroid Build Coastguard Worker   //
94*8d67ca89SAndroid Build Coastguard Worker   //   T1    T2    T3
95*8d67ca89SAndroid Build Coastguard Worker   //   A     A     A
96*8d67ca89SAndroid Build Coastguard Worker   //   A     A     D
97*8d67ca89SAndroid Build Coastguard Worker   //   A     D     A
98*8d67ca89SAndroid Build Coastguard Worker   //   A     D     D
99*8d67ca89SAndroid Build Coastguard Worker   //   D     A     A
100*8d67ca89SAndroid Build Coastguard Worker   //   D     A     D
101*8d67ca89SAndroid Build Coastguard Worker   //   D     D     A
102*8d67ca89SAndroid Build Coastguard Worker   //   D     D     D
103*8d67ca89SAndroid Build Coastguard Worker   //
104*8d67ca89SAndroid Build Coastguard Worker   // To do this, `AllocCounts` and `AllocRounds` will be adjusted according to the
105*8d67ca89SAndroid Build Coastguard Worker   // thread id.
106*8d67ca89SAndroid Build Coastguard Worker   auto thread_task = [&](size_t id) {
107*8d67ca89SAndroid Build Coastguard Worker     {
108*8d67ca89SAndroid Build Coastguard Worker       std::unique_lock lock(m);
109*8d67ca89SAndroid Build Coastguard Worker       // Wait until all threads are created.
110*8d67ca89SAndroid Build Coastguard Worker       cv.wait(lock, [&] { return ready; });
111*8d67ca89SAndroid Build Coastguard Worker     }
112*8d67ca89SAndroid Build Coastguard Worker 
113*8d67ca89SAndroid Build Coastguard Worker     void** MemPool;
114*8d67ca89SAndroid Build Coastguard Worker     const size_t AllocCounts = (MaxAllocCounts >> id);
115*8d67ca89SAndroid Build Coastguard Worker     const size_t AllocRounds = (kMinRounds << id);
116*8d67ca89SAndroid Build Coastguard Worker     MemPool = new void*[AllocCounts];
117*8d67ca89SAndroid Build Coastguard Worker 
118*8d67ca89SAndroid Build Coastguard Worker     for (size_t i = 0; i < AllocRounds; ++i) {
119*8d67ca89SAndroid Build Coastguard Worker       for (size_t j = 0; j < AllocCounts; ++j) {
120*8d67ca89SAndroid Build Coastguard Worker         void* ptr = malloc(size);
121*8d67ca89SAndroid Build Coastguard Worker         MemPool[j] = ptr;
122*8d67ca89SAndroid Build Coastguard Worker       }
123*8d67ca89SAndroid Build Coastguard Worker 
124*8d67ca89SAndroid Build Coastguard Worker       // Use a fix seed to reduce the noise of different round of benchmark.
125*8d67ca89SAndroid Build Coastguard Worker       const unsigned seed = 33529;
126*8d67ca89SAndroid Build Coastguard Worker       std::shuffle(MemPool, &MemPool[AllocCounts], std::default_random_engine(seed));
127*8d67ca89SAndroid Build Coastguard Worker 
128*8d67ca89SAndroid Build Coastguard Worker       for (size_t j = 0; j < AllocCounts; ++j) free(MemPool[j]);
129*8d67ca89SAndroid Build Coastguard Worker     }
130*8d67ca89SAndroid Build Coastguard Worker 
131*8d67ca89SAndroid Build Coastguard Worker     delete[] MemPool;
132*8d67ca89SAndroid Build Coastguard Worker   };
133*8d67ca89SAndroid Build Coastguard Worker 
134*8d67ca89SAndroid Build Coastguard Worker   for (auto _ : state) {
135*8d67ca89SAndroid Build Coastguard Worker     state.PauseTiming();
136*8d67ca89SAndroid Build Coastguard Worker     // Don't need to acquire the lock because no thread is created.
137*8d67ca89SAndroid Build Coastguard Worker     ready = false;
138*8d67ca89SAndroid Build Coastguard Worker 
139*8d67ca89SAndroid Build Coastguard Worker     for (size_t i = 0; i < num_threads; ++i) threads[i] = new std::thread(thread_task, i);
140*8d67ca89SAndroid Build Coastguard Worker 
141*8d67ca89SAndroid Build Coastguard Worker     state.ResumeTiming();
142*8d67ca89SAndroid Build Coastguard Worker 
143*8d67ca89SAndroid Build Coastguard Worker     {
144*8d67ca89SAndroid Build Coastguard Worker       std::unique_lock lock(m);
145*8d67ca89SAndroid Build Coastguard Worker       ready = true;
146*8d67ca89SAndroid Build Coastguard Worker     }
147*8d67ca89SAndroid Build Coastguard Worker 
148*8d67ca89SAndroid Build Coastguard Worker     cv.notify_all();
149*8d67ca89SAndroid Build Coastguard Worker 
150*8d67ca89SAndroid Build Coastguard Worker     for (size_t i = 0; i < num_threads; ++i) {
151*8d67ca89SAndroid Build Coastguard Worker       threads[i]->join();
152*8d67ca89SAndroid Build Coastguard Worker       delete threads[i];
153*8d67ca89SAndroid Build Coastguard Worker     }
154*8d67ca89SAndroid Build Coastguard Worker   }
155*8d67ca89SAndroid Build Coastguard Worker 
156*8d67ca89SAndroid Build Coastguard Worker   const size_t ThreadsBytesProcessed = kMaxBytes * kMinRounds * num_threads;
157*8d67ca89SAndroid Build Coastguard Worker   state.SetBytesProcessed(ThreadsBytesProcessed * static_cast<size_t>(state.iterations()));
158*8d67ca89SAndroid Build Coastguard Worker }
159*8d67ca89SAndroid Build Coastguard Worker 
BM_mallopt_purge(benchmark::State & state)160*8d67ca89SAndroid Build Coastguard Worker static void BM_mallopt_purge(benchmark::State& state) {
161*8d67ca89SAndroid Build Coastguard Worker   RunMalloptPurge(state, M_PURGE);
162*8d67ca89SAndroid Build Coastguard Worker }
163*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_mallopt_purge);
164*8d67ca89SAndroid Build Coastguard Worker 
BM_mallopt_purge_all(benchmark::State & state)165*8d67ca89SAndroid Build Coastguard Worker static void BM_mallopt_purge_all(benchmark::State& state) {
166*8d67ca89SAndroid Build Coastguard Worker   RunMalloptPurge(state, M_PURGE_ALL);
167*8d67ca89SAndroid Build Coastguard Worker }
168*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_mallopt_purge_all);
169*8d67ca89SAndroid Build Coastguard Worker 
170*8d67ca89SAndroid Build Coastguard Worker // Note that this will only test a single size class at a time so that we can
171*8d67ca89SAndroid Build Coastguard Worker // observe the impact of contention more often.
172*8d67ca89SAndroid Build Coastguard Worker #define BM_MALLOC_THREADS_THROUGHPUT(SIZE, NUM_THREADS)                                      \
173*8d67ca89SAndroid Build Coastguard Worker   static void BM_malloc_threads_throughput_##SIZE##_##NUM_THREADS(benchmark::State& state) { \
174*8d67ca89SAndroid Build Coastguard Worker     RunThreadsThroughput(state, SIZE, NUM_THREADS);                                          \
175*8d67ca89SAndroid Build Coastguard Worker   }                                                                                          \
176*8d67ca89SAndroid Build Coastguard Worker   BIONIC_BENCHMARK(BM_malloc_threads_throughput_##SIZE##_##NUM_THREADS);
177*8d67ca89SAndroid Build Coastguard Worker 
178*8d67ca89SAndroid Build Coastguard Worker // There are three block categories in Scudo, we choose 1 from each category.
179*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(64, 2);
180*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(64, 4);
181*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(64, 8);
182*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(512, 2);
183*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(512, 4);
184*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(512, 8);
185*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(8192, 2);
186*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(8192, 4);
187*8d67ca89SAndroid Build Coastguard Worker BM_MALLOC_THREADS_THROUGHPUT(8192, 8);
188*8d67ca89SAndroid Build Coastguard Worker 
189*8d67ca89SAndroid Build Coastguard Worker #endif
190