[Linux] Port MallocBench
[WebKit-https.git] / PerformanceTests / MallocBench / MallocBench / message.cpp
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 "CPUCount.h"
27 #include "message.h"
28 #include <condition_variable>
29 #include <deque>
30 #include <functional>
31 #include <mutex>
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <thread>
35
36 #include "mbmalloc.h"
37
38 namespace {
39
40 size_t hash(size_t hash, unsigned short a, unsigned short b)
41 {
42     hash += a ^ b;
43     return hash;
44 }
45
46 class Node {
47     static const size_t payloadCount = 128;
48 public:
49     void* operator new(size_t size)
50     {
51         return mbmalloc(size);
52     }
53
54     void operator delete(void* p, size_t size)
55     {
56         mbfree(p, size);
57     }
58
59     Node()
60         : m_payload()
61     {
62     }
63
64     size_t hash(size_t hash)
65     {
66         for (size_t i = 0; i < payloadCount; i += 2)
67             hash = ::hash(hash, m_payload[i], m_payload[i + 1]);
68         return hash;
69     }
70
71 private:
72     unsigned short m_payload[payloadCount];
73 };
74
75 class Message {
76     static const size_t nodeCount = 1 * 1024;
77
78 public:
79     void* operator new(size_t size)
80     {
81         return mbmalloc(size);
82     }
83
84     void operator delete(void* p, size_t size)
85     {
86         mbfree(p, size);
87     }
88
89     Message()
90         : m_buffer(static_cast<Node**>(mbmalloc(nodeCount * sizeof(Node**))))
91     {
92         for (size_t i = 0; i < nodeCount; ++i)
93             m_buffer[i] = new Node;
94     }
95     
96     ~Message()
97     {
98         for (size_t i = 0; i < nodeCount; ++i)
99             delete m_buffer[i];
100         mbfree(m_buffer, nodeCount * sizeof(Node**));
101     }
102
103     size_t hash()
104     {
105         size_t hash = 0;
106         for (size_t i = 0; i < nodeCount; ++i)
107             hash = m_buffer[i]->hash(hash);
108         return hash;
109     }
110
111 private:
112     Node** m_buffer;
113 };
114
115 } // namespace
116
117 class WorkQueue {
118 public:
119     WorkQueue()
120     {
121         m_thread = std::thread([&] {
122             while (true) {
123                 std::function<void()> target;
124                 {
125                     std::unique_lock<std::mutex> locker(m_mutex);
126                     m_condition.wait(locker, [&] { return !m_queue.empty(); });
127                     auto queued = m_queue.front();
128                     m_queue.pop_front();
129                     if (!queued)
130                         return;
131                     target = std::move(queued);
132                 }
133                 target();
134             }
135         });
136     }
137
138     ~WorkQueue() {
139         {
140             std::unique_lock<std::mutex> locker(m_mutex);
141             m_queue.push_back(nullptr);
142             m_condition.notify_one();
143         }
144         m_thread.join();
145     }
146
147     void dispatchAsync(std::function<void()> target)
148     {
149         std::unique_lock<std::mutex> locker(m_mutex);
150         m_queue.push_back(target);
151         m_condition.notify_one();
152     }
153
154     void dispatchSync(std::function<void()> target)
155     {
156         std::mutex syncMutex;
157         std::condition_variable syncCondition;
158
159         std::unique_lock<std::mutex> locker(syncMutex);
160         bool done = false;
161         dispatchAsync([&] {
162             target();
163             {
164                 std::unique_lock<std::mutex> locker(syncMutex);
165                 done = true;
166                 syncCondition.notify_one();
167             }
168         });
169         syncCondition.wait(locker, [&] { return done; });
170     }
171
172 private:
173     std::mutex m_mutex;
174     std::condition_variable m_condition;
175     std::deque<std::function<void()>> m_queue;
176     std::thread m_thread;
177 };
178
179 void benchmark_message_one(CommandLine& commandLine)
180 {
181     if (commandLine.isParallel())
182         abort();
183
184     const size_t times = 2048;
185     const size_t quantum = 16;
186
187     WorkQueue workQueue;
188     for (size_t i = 0; i < times; i += quantum) {
189         for (size_t j = 0; j < quantum; ++j) {
190             Message* message = new Message;
191             workQueue.dispatchAsync([message] {
192                 size_t hash = message->hash();
193                 if (hash)
194                     abort();
195                 delete message;
196             });
197         }
198         workQueue.dispatchSync([] { });
199     }
200     workQueue.dispatchSync([] { });
201 }
202
203 void benchmark_message_many(CommandLine& commandLine)
204 {
205     if (commandLine.isParallel())
206         abort();
207
208     const size_t times = 768;
209     const size_t quantum = 16;
210
211     const size_t queueCount = cpuCount() - 1;
212     std::unique_ptr<WorkQueue> queues[queueCount];
213     for (size_t i = 0; i < queueCount; ++i)
214         queues[i] = std::make_unique<WorkQueue>();
215
216     for (size_t i = 0; i < times; i += quantum) {
217         for (size_t j = 0; j < quantum; ++j) {
218             for (size_t k = 0; k < queueCount; ++k) {
219                 Message* message = new Message;
220                 queues[k]->dispatchAsync([message] {
221                     size_t hash = message->hash();
222                     if (hash)
223                         abort();
224                     delete message;
225                 });
226             }
227         }
228
229         for (size_t i = 0; i < queueCount; ++i)
230             queues[i]->dispatchSync([] { });
231     }
232
233     for (size_t i = 0; i < queueCount; ++i)
234         queues[i]->dispatchSync([] { });
235 }