2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "Benchmark.h"
37 #include <dispatch/dispatch.h>
39 #include <mach/mach.h>
40 #include <mach/task_info.h>
51 struct BenchmarkPair {
52 const char* const name;
53 const BenchmarkFunction function;
56 static const BenchmarkPair benchmarkPairs[] = {
57 { "churn", benchmark_churn },
58 { "list_allocate", benchmark_list_allocate },
59 { "list_traverse", benchmark_list_traverse },
60 { "tree_allocate", benchmark_tree_allocate },
61 { "tree_traverse", benchmark_tree_traverse },
62 { "tree_churn", benchmark_tree_churn },
63 { "fragment", benchmark_fragment },
64 { "fragment_iterate", benchmark_fragment_iterate },
65 { "message_one", benchmark_message_one },
66 { "message_many", benchmark_message_many },
67 { "medium", benchmark_medium },
68 { "big", benchmark_big },
69 { "facebook", benchmark_facebook },
70 { "balloon", benchmark_balloon },
73 static const size_t benchmarksPairsCount = sizeof(benchmarkPairs) / sizeof(BenchmarkPair);
75 static inline bool operator==(const BenchmarkPair& benchmarkPair, const string& string)
77 return string == benchmarkPair.name;
80 static void*** allocateHeap(size_t heapSize, size_t chunkSize, size_t objectSize)
85 size_t chunkCount = heapSize / chunkSize;
86 size_t objectCount = chunkSize / objectSize;
87 void*** chunks = (void***)mbmalloc(chunkCount * sizeof(void**));
88 for (size_t i = 0; i < chunkCount; ++i) {
89 chunks[i] = (void**)mbmalloc(objectCount * sizeof(void*));
90 for (size_t j = 0; j < objectCount; ++j) {
91 chunks[i][j] = (void*)mbmalloc(objectSize);
92 bzero(chunks[i][j], objectSize);
98 static void deallocateHeap(void*** chunks, size_t heapSize, size_t chunkSize, size_t objectSize)
103 size_t chunkCount = heapSize / chunkSize;
104 size_t objectCount = chunkSize / objectSize;
105 for (size_t i = 0; i < chunkCount; ++i) {
106 for (size_t j = 0; j < objectCount; ++j)
107 mbfree(chunks[i][j], objectSize);
108 mbfree(chunks[i], objectCount * sizeof(void*));
110 mbfree(chunks, chunkCount * sizeof(void**));
113 Benchmark::Benchmark(const string& benchmarkName, bool isParallel, bool measureHeap, size_t heapSize)
116 , m_isParallel(isParallel)
117 , m_heapSize(heapSize)
118 , m_measureHeap(measureHeap)
120 const BenchmarkPair* benchmarkPair = std::find(
121 benchmarkPairs, benchmarkPairs + benchmarksPairsCount, benchmarkName);
122 if (benchmarkPair == benchmarkPairs + benchmarksPairsCount)
125 m_benchmarkPair = benchmarkPair;
128 void Benchmark::printBenchmarks()
130 cout << "Benchmarks: " << endl;
131 for (size_t i = 0; i < benchmarksPairsCount; ++i)
132 cout << "\t" << benchmarkPairs[i].name << endl;
135 void Benchmark::runOnce()
138 m_benchmarkPair->function(m_isParallel);
142 dispatch_group_t group = dispatch_group_create();
144 for (size_t i = 0; i < cpuCount(); ++i) {
145 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
146 m_benchmarkPair->function(m_isParallel);
150 dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
152 dispatch_release(group);
155 void Benchmark::run()
157 static const size_t count = 4;
158 static const size_t objectSize = 32;
159 static const size_t chunkSize = 1024 * 1024;
161 void*** heap = allocateHeap(m_heapSize, chunkSize, objectSize);
163 runOnce(); // Warmup run.
165 for (size_t i = 0; i < count; ++i) {
166 double start = currentTimeMS();
168 double end = currentTimeMS();
169 double elapsed = end - start;
170 m_elapsedTime += elapsed;
172 m_elapsedTime /= count;
174 deallocateHeap(heap, m_heapSize, chunkSize, objectSize);
179 // Wait a bit for any async freeing to finish.
182 last = currentMemoryBytes().resident;
183 std::this_thread::sleep_for(std::chrono::seconds(2));
184 } while (currentMemoryBytes().resident < last);
186 m_memory = currentMemoryBytes();
189 void Benchmark::printReport()
193 cout << "Time: \t" << m_elapsedTime << "ms" << endl;
197 cout << "Memory: \t" << m_memory.resident / kB << "kB" << endl;
198 cout << "Peak Memory:\t" << m_memory.residentMax / kB << "kB" << endl;
201 double Benchmark::currentTimeMS()
204 gettimeofday(&now, 0);
205 return (now.tv_sec * 1000.0) + now.tv_usec / 1000.0;
208 Benchmark::Memory Benchmark::currentMemoryBytes()
212 task_vm_info_data_t vm_info;
213 mach_msg_type_number_t vm_size = TASK_VM_INFO_COUNT;
214 if (KERN_SUCCESS != task_info(mach_task_self(), TASK_VM_INFO_PURGEABLE, (task_info_t)(&vm_info), &vm_size)) {
215 cout << "Failed to get mach task info" << endl;
219 memory.resident = vm_info.internal - vm_info.purgeable_volatile_pmap;
220 memory.residentMax = vm_info.resident_size_peak;