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