[GLIB][GTK] Unreviewed test gardening. Updated expectations and baselines after 270164.
[WebKit-https.git] / Source / WTF / benchmarks / LockSpeedTest.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 LockSpeedTest Source/WTF/benchmarks/LockSpeedTest.cpp -O3 -W -ISource/WTF -ISource/WTF/icu -ISource/WTF/benchmarks -LWebKitBuild/Release -lWTF -framework Foundation -licucore -std=c++14 -fvisibility=hidden
28
29 #include "config.h"
30
31 #include "ToyLocks.h"
32 #include <thread>
33 #include <unistd.h>
34 #include <wtf/DataLog.h>
35 #include <wtf/HashMap.h>
36 #include <wtf/Lock.h>
37 #include <wtf/ParkingLot.h>
38 #include <wtf/StdLibExtras.h>
39 #include <wtf/Threading.h>
40 #include <wtf/ThreadingPrimitives.h>
41 #include <wtf/Vector.h>
42 #include <wtf/WordLock.h>
43 #include <wtf/text/CString.h>
44
45 namespace {
46
47 unsigned numThreadGroups;
48 unsigned numThreadsPerGroup;
49 unsigned workPerCriticalSection;
50 unsigned workBetweenCriticalSections;
51 double secondsPerTest;
52     
53 NO_RETURN void usage()
54 {
55     printf("Usage: LockSpeedTest yieldspinlock|pausespinlock|wordlock|lock|barginglock|bargingwordlock|thunderlock|thunderwordlock|cascadelock|cascadewordlock|handofflock|unfairlock|mutex|all <num thread groups> <num threads per group> <work per critical section> <work between critical sections> <spin limit> <seconds per test>\n");
56     exit(1);
57 }
58
59 template<typename Type>
60 struct WithPadding {
61     Type value;
62     char buf[300]; // It's best if this isn't perfect to avoid false sharing.
63 };
64
65 HashMap<CString, Vector<double>> results;
66
67 void reportResult(const char* name, double value)
68 {
69     dataLogF("%s: %.3lf KHz\n", name, value);
70     results.add(name, Vector<double>()).iterator->value.append(value);
71 }
72
73 struct Benchmark {
74     template<typename LockType>
75     static void run(const char* name)
76     {
77         std::unique_ptr<WithPadding<LockType>[]> locks = makeUniqueWithoutFastMallocCheck<WithPadding<LockType>[]>(numThreadGroups);
78         std::unique_ptr<WithPadding<double>[]> words = makeUniqueWithoutFastMallocCheck<WithPadding<double>[]>(numThreadGroups);
79         std::unique_ptr<RefPtr<Thread>[]> threads = makeUniqueWithoutFastMallocCheck<RefPtr<Thread>[]>(numThreadGroups * numThreadsPerGroup);
80
81         volatile bool keepGoing = true;
82
83         MonotonicTime before = MonotonicTime::now();
84     
85         Lock numIterationsLock;
86         uint64_t numIterations = 0;
87     
88         for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) {
89             words[threadGroupIndex].value = 0;
90
91             for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) {
92                 threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = Thread::create(
93                     "Benchmark thread",
94                     [threadGroupIndex, &locks, &words, &keepGoing, &numIterationsLock, &numIterations] () {
95                         double localWord = 0;
96                         double value = 1;
97                         unsigned myNumIterations = 0;
98                         while (keepGoing) {
99                             locks[threadGroupIndex].value.lock();
100                             for (unsigned j = workPerCriticalSection; j--;) {
101                                 words[threadGroupIndex].value += value;
102                                 value = words[threadGroupIndex].value;
103                             }
104                             locks[threadGroupIndex].value.unlock();
105                             for (unsigned j = workBetweenCriticalSections; j--;) {
106                                 localWord += value;
107                                 value = localWord;
108                             }
109                             myNumIterations++;
110                         }
111                         LockHolder locker(numIterationsLock);
112                         numIterations += myNumIterations;
113                     });
114             }
115         }
116
117         sleep(Seconds { secondsPerTest });
118         keepGoing = false;
119     
120         for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;)
121             threads[threadIndex]->waitForCompletion();
122
123         MonotonicTime after = MonotonicTime::now();
124     
125         reportResult(name, numIterations / (after - before).seconds() / 1000);
126     }
127 };
128
129 unsigned rangeMin;
130 unsigned rangeMax;
131 unsigned rangeStep;
132 unsigned* rangeVariable;
133
134 bool parseValue(const char* string, unsigned* variable)
135 {
136     unsigned myRangeMin;
137     unsigned myRangeMax;
138     unsigned myRangeStep;
139     if (sscanf(string, "%u-%u:%u", &myRangeMin, &myRangeMax, &myRangeStep) == 3) {
140         if (rangeVariable) {
141             fprintf(stderr, "Can only have one variable with a range.\n");
142             return false;
143         }
144         
145         rangeMin = myRangeMin;
146         rangeMax = myRangeMax;
147         rangeStep = myRangeStep;
148         rangeVariable = variable;
149         return true;
150     }
151     
152     if (sscanf(string, "%u", variable) == 1)
153         return true;
154     
155     return false;
156 }
157
158 } // anonymous namespace
159
160 int main(int argc, char** argv)
161 {
162     WTF::initialize();
163     
164     if (argc != 8
165         || !parseValue(argv[2], &numThreadGroups)
166         || !parseValue(argv[3], &numThreadsPerGroup)
167         || !parseValue(argv[4], &workPerCriticalSection)
168         || !parseValue(argv[5], &workBetweenCriticalSections)
169         || !parseValue(argv[6], &toyLockSpinLimit)
170         || sscanf(argv[7], "%lf", &secondsPerTest) != 1)
171         usage();
172     
173     if (rangeVariable) {
174         dataLog("Running with rangeMin = ", rangeMin, ", rangeMax = ", rangeMax, ", rangeStep = ", rangeStep, "\n");
175         for (unsigned value = rangeMin; value <= rangeMax; value += rangeStep) {
176             dataLog("Running with value = ", value, "\n");
177             *rangeVariable = value;
178             runEverything<Benchmark>(argv[1]);
179         }
180     } else
181         runEverything<Benchmark>(argv[1]);
182     
183     for (auto& entry : results) {
184         printf("%s = {", entry.key.data());
185         bool first = true;
186         for (double value : entry.value) {
187             if (first)
188                 first = false;
189             else
190                 printf(", ");
191             printf("%.3lf", value);
192         }
193         printf("};\n");
194     }
195
196     return 0;
197 }