1 | /* Copyright 2019 Google LLC. All Rights Reserved. |
2 | |
3 | Licensed under the Apache License, Version 2.0 (the "License"); |
4 | you may not use this file except in compliance with the License. |
5 | You may obtain a copy of the License at |
6 | |
7 | http://www.apache.org/licenses/LICENSE-2.0 |
8 | |
9 | Unless required by applicable law or agreed to in writing, software |
10 | distributed under the License is distributed on an "AS IS" BASIS, |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | See the License for the specific language governing permissions and |
13 | limitations under the License. |
14 | ==============================================================================*/ |
15 | |
16 | #ifndef RUY_RUY_WAIT_H_ |
17 | #define RUY_RUY_WAIT_H_ |
18 | |
19 | #include <condition_variable> // NOLINT(build/c++11) |
20 | #include <functional> |
21 | #include <mutex> // NOLINT(build/c++11) |
22 | |
23 | #include "ruy/time.h" |
24 | |
25 | namespace ruy { |
26 | |
27 | // Waits until some evaluation of `condition` has returned true. |
28 | // |
29 | // There is no guarantee that calling `condition` again after this function |
30 | // has returned would still return true. The only |
31 | // contract is that at some point during the execution of that function, |
32 | // `condition` has returned true. |
33 | // |
34 | // First does some spin-waiting for the specified `spin_duration`, |
35 | // then falls back to passive waiting for the given condvar, guarded |
36 | // by the given mutex. At this point it will try to acquire the mutex lock, |
37 | // around the waiting on the condition variable. |
38 | // Therefore, this function expects that the calling thread hasn't already |
39 | // locked the mutex before calling it. |
40 | // This function will always release the mutex lock before returning. |
41 | // |
42 | // The idea of doing some initial spin-waiting is to help get |
43 | // better and more consistent multithreading benefits for small GEMM sizes. |
44 | // Spin-waiting help ensuring that if we need to wake up soon after having |
45 | // started waiting, then we can wake up quickly (as opposed to, say, |
46 | // having to wait to be scheduled again by the OS). On the other hand, |
47 | // we must still eventually revert to passive waiting for longer waits |
48 | // (e.g. worker threads having finished a GEMM and waiting until the next GEMM) |
49 | // so as to avoid permanently spinning. |
50 | // |
51 | // In situations where other threads might have more useful things to do with |
52 | // these CPU cores than our spin-waiting, it may be best to reduce the value |
53 | // of `spin_duration`. Setting it to zero disables the spin-waiting entirely. |
54 | // |
55 | // There is a risk that the std::function used here might use a heap allocation |
56 | // to store its context. The expected usage pattern is that these functions' |
57 | // contexts will consist of a single pointer value (typically capturing only |
58 | // [this]), and that in this case the std::function implementation will use |
59 | // inline storage, avoiding a heap allocation. However, we can't effectively |
60 | // guard that assumption, and that's not a big concern anyway because the |
61 | // latency of a small heap allocation is probably low compared to the intrinsic |
62 | // latency of what this Wait function does. |
63 | void Wait(const std::function<bool()>& condition, const Duration& spin_duration, |
64 | std::condition_variable* condvar, std::mutex* mutex); |
65 | |
66 | } // namespace ruy |
67 | |
68 | #endif // RUY_RUY_WAIT_H_ |
69 | |