Turi Create  4.0
unfair_lock.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #ifndef TURI_SFRAME_UNFAIR_IO_SCHEDULER_HPP
7 #define TURI_SFRAME_UNFAIR_IO_SCHEDULER_HPP
8 #include <cstdint>
9 #include <cstddef>
10 #include <map>
11 #include <core/parallel/pthread_tools.hpp>
12 namespace turi {
13 
14 
15 /**
16  * \internal
17  * \ingroup sframe_physical
18  * \addtogroup sframe_internal SFrame Internal
19  * \{
20  */
21 
22 /**
23  * This class implements a completely unfair lock.
24  *
25  * The basic mechanic of operation is that every thread is assigned a
26  * priority ID (this is via a thread local variable) (rant if apple compiler
27  * has proper C++11 support this would just be using the thread_local keyword.
28  * But we don't. So it is this annoying boilerplate around pthread_getspecific.).
29  *
30  * Then if many threads are contending for the lock, the lock will always go to
31  * the thread with the lowest priority ID.
32  *
33  * Furthermore, the lock has a parameterized "stickiness". In other words, when
34  * a thread releases the lock, it is granted a certain time window in which
35  * if it (or a lower ID thread) returns to acquire the lock, it will be able to
36  * get it immediately. This "stickness" essentially parameterizes the
37  * CPU-utilization Disk-utilization balance. The more IO bound a task is, the
38  * better it is for it to be executed on just one CPU. This threshold attempts
39  * to self-tune by trying to maximize the total throughput of the lock.
40  * (i.e.. maximising lock acquisitions per second). This is done by gradually
41  * adapting the sleep interval: i.e. if increasing it increases throughput,
42  * it gets increases, and vice versa.
43  */
44 class unfair_lock {
45  public:
46  void lock();
47  void unlock();
48  private:
49  turi::mutex m_lock;
50  turi::mutex m_internal_lock;
51  volatile bool m_lock_acquired = false;
52  std::map<size_t, turi::conditional*> m_cond;
53  // autotuning parameters for the lock stickness
54  size_t m_previous_owner_priority = 0;
55  int m_previous_sleep_interval = 0;
56  double m_previous_time_for_epoch = 0;
57  int m_current_sleep_interval = 50;
58  double m_time_for_epoch = 0;
59  size_t m_epoch_counter = 0;
60  bool m_initial = true;
61  timer m_ti;
62 };
63 
64 /// \}
65 //
66 } // turicreate
67 #endif
A simple class that can be used for benchmarking/timing up to microsecond resolution.
Definition: timer.hpp:59