[JSC] Remove some invalid immediate instruction forms from ARM64 Air
[WebKit-https.git] / Source / WTF / benchmarks / ConditionSpeedTest.cpp
1 /*
2  * Copyright (C) 2015 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 // On Mac, you can build this like so:
27 // clang++ -o ConditionSpeedTest Source/WTF/benchmarks/ConditionSpeedTest.cpp -O3 -W -ISource/WTF -LWebKitBuild/Release -lWTF -framework Foundation -licucore -std=c++11
28
29 #include "config.h"
30
31 #include <mutex>
32 #include <thread>
33 #include <unistd.h>
34 #include <wtf/Condition.h>
35 #include <wtf/CurrentTime.h>
36 #include <wtf/DataLog.h>
37 #include <wtf/Deque.h>
38 #include <wtf/Lock.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/StringPrintStream.h>
41 #include <wtf/Threading.h>
42 #include <wtf/ThreadingPrimitives.h>
43 #include <wtf/Vector.h>
44
45 namespace {
46
47 const bool verbose = false;
48
49 unsigned numProducers;
50 unsigned numConsumers;
51 unsigned maxQueueSize;
52 unsigned numMessagesPerProducer;
53     
54 NO_RETURN void usage()
55 {
56     printf("Usage: ConditionSpeedTest lock|mutex|all <num producers> <num consumers> <max queue size> <num messages per producer>\n");
57     exit(1);
58 }
59
60 template<typename Functor, typename ConditionType, typename LockType>
61 void wait(ConditionType& condition, LockType& lock, const Functor& predicate)
62 {
63     while (!predicate())
64         condition.wait(lock);
65 }
66
67 template<typename LockType, typename ConditionType, typename NotifyFunctor, typename NotifyAllFunctor>
68 void runTest(
69     unsigned numProducers,
70     unsigned numConsumers,
71     unsigned maxQueueSize,
72     unsigned numMessagesPerProducer,
73     const NotifyFunctor& notify,
74     const NotifyAllFunctor& notifyAll)
75 {
76     Deque<unsigned> queue;
77     bool shouldContinue = true;
78     LockType lock;
79     ConditionType emptyCondition;
80     ConditionType fullCondition;
81
82     Vector<ThreadIdentifier> consumerThreads;
83     Vector<ThreadIdentifier> producerThreads;
84
85     Vector<unsigned> received;
86     LockType receivedLock;
87     
88     for (unsigned i = numConsumers; i--;) {
89         ThreadIdentifier threadIdentifier = createThread(
90             "Consumer thread",
91             [&] () {
92                 for (;;) {
93                     unsigned result;
94                     unsigned mustNotify = false;
95                     {
96                         std::unique_lock<LockType> locker(lock);
97                         wait(
98                             emptyCondition, lock,
99                             [&] () {
100                                 if (verbose)
101                                     dataLog(toString(currentThread(), ": Checking consumption predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
102                                 return !shouldContinue || !queue.isEmpty();
103                             });
104                         if (!shouldContinue && queue.isEmpty())
105                             return;
106                         mustNotify = queue.size() == maxQueueSize;
107                         result = queue.takeFirst();
108                     }
109                     notify(fullCondition, mustNotify);
110
111                     {
112                         std::lock_guard<LockType> locker(receivedLock);
113                         received.append(result);
114                     }
115                 }
116             });
117         consumerThreads.append(threadIdentifier);
118     }
119
120     for (unsigned i = numProducers; i--;) {
121         ThreadIdentifier threadIdentifier = createThread(
122             "Producer Thread",
123             [&] () {
124                 for (unsigned i = 0; i < numMessagesPerProducer; ++i) {
125                     bool mustNotify = false;
126                     {
127                         std::unique_lock<LockType> locker(lock);
128                         wait(
129                             fullCondition, lock,
130                             [&] () {
131                                 if (verbose)
132                                     dataLog(toString(currentThread(), ": Checking production predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
133                                 return queue.size() < maxQueueSize;
134                             });
135                         mustNotify = queue.isEmpty();
136                         queue.append(i);
137                     }
138                     notify(emptyCondition, mustNotify);
139                 }
140             });
141         producerThreads.append(threadIdentifier);
142     }
143
144     for (ThreadIdentifier threadIdentifier : producerThreads)
145         waitForThreadCompletion(threadIdentifier);
146
147     {
148         std::lock_guard<LockType> locker(lock);
149         shouldContinue = false;
150     }
151     notifyAll(emptyCondition);
152
153     for (ThreadIdentifier threadIdentifier : consumerThreads)
154         waitForThreadCompletion(threadIdentifier);
155
156     RELEASE_ASSERT(numProducers * numMessagesPerProducer == received.size());
157     std::sort(received.begin(), received.end());
158     for (unsigned messageIndex = 0; messageIndex < numMessagesPerProducer; ++messageIndex) {
159         for (unsigned producerIndex = 0; producerIndex < numProducers; ++producerIndex)
160             RELEASE_ASSERT(messageIndex == received[messageIndex * numProducers + producerIndex]);
161     }
162 }
163
164 template<typename LockType, typename ConditionType, typename NotifyFunctor, typename NotifyAllFunctor>
165 void runBenchmark(
166     const char* name,
167     const NotifyFunctor& notify,
168     const NotifyAllFunctor& notifyAll)
169 {
170     double before = monotonicallyIncreasingTimeMS();
171     
172     runTest<LockType, ConditionType>(
173         numProducers,
174         numConsumers,
175         maxQueueSize,
176         numMessagesPerProducer,
177         notify,
178         notifyAll);
179
180     double after = monotonicallyIncreasingTimeMS();
181
182     printf("%s: %.3lf ms.\n", name, after - before);
183 }
184
185 } // anonymous namespace
186
187 int main(int argc, char** argv)
188 {
189     WTF::initializeThreading();
190
191     if (argc != 6
192         || sscanf(argv[2], "%u", &numProducers) != 1
193         || sscanf(argv[3], "%u", &numConsumers) != 1
194         || sscanf(argv[4], "%u", &maxQueueSize) != 1
195         || sscanf(argv[5], "%u", &numMessagesPerProducer) != 1)
196         usage();
197
198     bool didRun = false;
199     if (!strcmp(argv[1], "lock") || !strcmp(argv[1], "all")) {
200         runBenchmark<Lock, Condition>(
201             "WTF Lock NotifyOne",
202             [&] (Condition& condition, bool mustNotify) {
203                 condition.notifyOne();
204             },
205             [&] (Condition& condition) {
206                 condition.notifyAll();
207             });
208         runBenchmark<Lock, Condition>(
209             "WTF Lock NotifyAll",
210             [&] (Condition& condition, bool mustNotify) {
211                 if (mustNotify)
212                     condition.notifyAll();
213             },
214             [&] (Condition& condition) {
215                 condition.notifyAll();
216             });
217         didRun = true;
218     }
219     if (!strcmp(argv[1], "mutex") || !strcmp(argv[1], "all")) {
220         runBenchmark<Mutex, ThreadCondition>(
221             "Platform Mutex NotifyOne",
222             [&] (ThreadCondition& condition, bool mustNotify) {
223                 condition.signal();
224             },
225             [&] (ThreadCondition& condition) {
226                 condition.broadcast();
227             });
228         runBenchmark<Mutex, ThreadCondition>(
229             "Platform Mutex NotifyAll",
230             [&] (ThreadCondition& condition, bool mustNotify) {
231                 if (mustNotify)
232                     condition.broadcast();
233             },
234             [&] (ThreadCondition& condition) {
235                 condition.broadcast();
236             });
237         didRun = true;
238     }
239
240     if (!didRun)
241         usage();
242
243     return 0;
244 }
245