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