1 | /* |
2 | * Copyright (C) 2014 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
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. |
12 | * |
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. |
24 | */ |
25 | |
26 | #include "Benchmark.h" |
27 | #include "CPUCount.h" |
28 | #include "alloc_free.h" |
29 | #include "balloon.h" |
30 | #include "big.h" |
31 | #include "churn.h" |
32 | #include "facebook.h" |
33 | #include "flickr.h" |
34 | #include "fragment.h" |
35 | #include "list.h" |
36 | #include "medium.h" |
37 | #include "message.h" |
38 | #include "nimlang.h" |
39 | #include "reddit.h" |
40 | #include "realloc.h" |
41 | #include "stress.h" |
42 | #include "stress_aligned.h" |
43 | #include "theverge.h" |
44 | #include "tree.h" |
45 | #include <algorithm> |
46 | #include <iostream> |
47 | #include <map> |
48 | #include <stdio.h> |
49 | #include <string> |
50 | #include <strings.h> |
51 | #include <sys/time.h> |
52 | #include <thread> |
53 | #include <vector> |
54 | |
55 | #ifdef __APPLE__ |
56 | #include <dispatch/dispatch.h> |
57 | #endif |
58 | |
59 | #include "mbmalloc.h" |
60 | |
61 | using namespace std; |
62 | |
63 | struct BenchmarkPair { |
64 | const char* const name; |
65 | const BenchmarkFunction function; |
66 | }; |
67 | |
68 | static const BenchmarkPair benchmarkPairs[] = { |
69 | { "alloc_free" , benchmark_alloc_free }, |
70 | { "balloon" , benchmark_balloon }, |
71 | { "big" , benchmark_big }, |
72 | { "churn" , benchmark_churn }, |
73 | { "facebook" , benchmark_facebook }, |
74 | { "flickr" , benchmark_flickr }, |
75 | { "flickr_memory_warning" , benchmark_flickr_memory_warning }, |
76 | { "fragment" , benchmark_fragment }, |
77 | { "fragment_iterate" , benchmark_fragment_iterate }, |
78 | { "list_allocate" , benchmark_list_allocate }, |
79 | { "list_traverse" , benchmark_list_traverse }, |
80 | { "medium" , benchmark_medium }, |
81 | { "message_many" , benchmark_message_many }, |
82 | { "message_one" , benchmark_message_one }, |
83 | { "nimlang" , benchmark_nimlang }, |
84 | { "realloc" , benchmark_realloc }, |
85 | { "reddit" , benchmark_reddit }, |
86 | { "reddit_memory_warning" , benchmark_reddit_memory_warning }, |
87 | { "stress" , benchmark_stress }, |
88 | { "stress_aligned" , benchmark_stress_aligned }, |
89 | { "theverge" , benchmark_theverge }, |
90 | { "theverge_memory_warning" , benchmark_theverge_memory_warning }, |
91 | { "tree_allocate" , benchmark_tree_allocate }, |
92 | { "tree_churn" , benchmark_tree_churn }, |
93 | { "tree_traverse" , benchmark_tree_traverse }, |
94 | }; |
95 | |
96 | static const size_t benchmarksPairsCount = sizeof(benchmarkPairs) / sizeof(BenchmarkPair); |
97 | |
98 | static inline bool operator==(const BenchmarkPair& benchmarkPair, const string& string) |
99 | { |
100 | return string == benchmarkPair.name; |
101 | } |
102 | |
103 | static void*** allocateHeap(size_t heapSize, size_t chunkSize, size_t objectSize) |
104 | { |
105 | if (!heapSize) |
106 | return 0; |
107 | |
108 | size_t chunkCount = heapSize / chunkSize; |
109 | size_t objectCount = chunkSize / objectSize; |
110 | void*** chunks = (void***)mbmalloc(chunkCount * sizeof(void**)); |
111 | for (size_t i = 0; i < chunkCount; ++i) { |
112 | chunks[i] = (void**)mbmalloc(objectCount * sizeof(void*)); |
113 | for (size_t j = 0; j < objectCount; ++j) { |
114 | chunks[i][j] = (void*)mbmalloc(objectSize); |
115 | bzero(chunks[i][j], objectSize); |
116 | } |
117 | } |
118 | return chunks; |
119 | } |
120 | |
121 | static void deallocateHeap(void*** chunks, size_t heapSize, size_t chunkSize, size_t objectSize) |
122 | { |
123 | if (!heapSize) |
124 | return; |
125 | |
126 | size_t chunkCount = heapSize / chunkSize; |
127 | size_t objectCount = chunkSize / objectSize; |
128 | for (size_t i = 0; i < chunkCount; ++i) { |
129 | for (size_t j = 0; j < objectCount; ++j) |
130 | mbfree(chunks[i][j], objectSize); |
131 | mbfree(chunks[i], objectCount * sizeof(void*)); |
132 | } |
133 | mbfree(chunks, chunkCount * sizeof(void**)); |
134 | } |
135 | |
136 | Benchmark::Benchmark(CommandLine& commandLine) |
137 | : m_commandLine(commandLine) |
138 | { |
139 | const BenchmarkPair* benchmarkPair = std::find( |
140 | benchmarkPairs, benchmarkPairs + benchmarksPairsCount, m_commandLine.benchmarkName()); |
141 | if (benchmarkPair == benchmarkPairs + benchmarksPairsCount) |
142 | return; |
143 | |
144 | m_benchmarkPair = benchmarkPair; |
145 | } |
146 | |
147 | void Benchmark::printBenchmarks() |
148 | { |
149 | cout << "Benchmarks: " << endl; |
150 | for (size_t i = 0; i < benchmarksPairsCount; ++i) |
151 | cout << "\t" << benchmarkPairs[i].name << endl; |
152 | } |
153 | |
154 | void Benchmark::runOnce() |
155 | { |
156 | if (!m_commandLine.isParallel()) { |
157 | m_benchmarkPair->function(m_commandLine); |
158 | return; |
159 | } |
160 | |
161 | #ifdef __APPLE__ |
162 | dispatch_group_t group = dispatch_group_create(); |
163 | |
164 | for (size_t i = 0; i < cpuCount(); ++i) { |
165 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ |
166 | m_benchmarkPair->function(m_commandLine); |
167 | }); |
168 | } |
169 | |
170 | dispatch_group_wait(group, DISPATCH_TIME_FOREVER); |
171 | |
172 | dispatch_release(group); |
173 | #else |
174 | std::vector<std::thread> threads; |
175 | for (size_t i = 0; i < cpuCount(); ++i) { |
176 | threads.emplace_back([&] { |
177 | m_benchmarkPair->function(m_commandLine); |
178 | }); |
179 | } |
180 | for (auto& thread : threads) |
181 | thread.join(); |
182 | #endif |
183 | } |
184 | |
185 | void Benchmark::run() |
186 | { |
187 | static const size_t objectSize = 32; |
188 | static const size_t chunkSize = 1024 * 1024; |
189 | |
190 | void*** heap = allocateHeap(m_commandLine.heapSize(), chunkSize, objectSize); |
191 | |
192 | if (m_commandLine.warmUp()) |
193 | runOnce(); // Warmup run. |
194 | |
195 | size_t runs = m_commandLine.runs(); |
196 | |
197 | for (size_t i = 0; i < runs; ++i) { |
198 | double start = currentTimeMS(); |
199 | runOnce(); |
200 | double end = currentTimeMS(); |
201 | double elapsed = end - start; |
202 | m_elapsedTime += elapsed; |
203 | } |
204 | m_elapsedTime /= runs; |
205 | |
206 | deallocateHeap(heap, m_commandLine.heapSize(), chunkSize, objectSize); |
207 | |
208 | mbscavenge(); |
209 | m_memory = currentMemoryBytes(); |
210 | } |
211 | |
212 | void Benchmark::printReport() |
213 | { |
214 | size_t kB = 1024; |
215 | |
216 | cout << "Time: \t" << m_elapsedTime << "ms" << endl; |
217 | cout << "Peak Memory:\t" << m_memory.residentMax / kB << "kB" << endl; |
218 | cout << "Memory at End: \t" << m_memory.resident / kB << "kB" << endl; |
219 | } |
220 | |
221 | double Benchmark::currentTimeMS() |
222 | { |
223 | struct timeval now; |
224 | gettimeofday(&now, 0); |
225 | return (now.tv_sec * 1000.0) + now.tv_usec / 1000.0; |
226 | } |
227 | |
228 | |