1 /* 2 * Copyright (c) 2017-2020 Arm Limited. 3 * 4 * SPDX-License-Identifier: MIT 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all 14 * copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 #ifndef ARM_COMPUTE_ISCHEDULER_H 25 #define ARM_COMPUTE_ISCHEDULER_H 26 27 #include "arm_compute/core/CPP/CPPTypes.h" 28 #include "arm_compute/core/Types.h" 29 #include "arm_compute/core/experimental/Types.h" 30 31 #include <functional> 32 #include <limits> 33 34 namespace arm_compute 35 { 36 class ICPPKernel; 37 class ITensor; 38 39 /** Scheduler interface to run kernels */ 40 class IScheduler 41 { 42 public: 43 /** Strategies available to split a workload */ 44 enum class StrategyHint 45 { 46 STATIC, /**< Split the workload evenly among the threads */ 47 DYNAMIC, /**< Split the workload dynamically using a bucket system */ 48 }; 49 50 /** Function to be used and map a given thread id to a logical core id 51 * 52 * Mapping function expects the thread index and total number of cores as input, 53 * and returns the logical core index to bind against 54 */ 55 using BindFunc = std::function<int(int, int)>; 56 57 /** When arm_compute::ISchedular::Hints::_split_dimension is initialized with this value 58 * then the schedular is free to break down the problem space over as many dimensions 59 * as it wishes 60 */ 61 static constexpr unsigned int split_dimensions_all = std::numeric_limits<unsigned>::max(); 62 63 /** Scheduler hints 64 * 65 * Collection of preferences set by the function regarding how to split a given workload 66 */ 67 class Hints 68 { 69 public: 70 /** Constructor 71 * 72 * @param[in] split_dimension Dimension along which to split the kernel's execution window. 73 * @param[in] strategy (Optional) Split strategy. 74 * @param[in] threshold (Optional) Dynamic scheduling capping threshold. 75 */ 76 Hints(unsigned int split_dimension, StrategyHint strategy = StrategyHint::STATIC, int threshold = 0) _split_dimension(split_dimension)77 : _split_dimension(split_dimension), _strategy(strategy), _threshold(threshold) 78 { 79 } 80 /** Set the split_dimension hint 81 * 82 * @param[in] split_dimension Dimension along which to split the kernel's execution window. 83 * 84 * @return the Hints object 85 */ set_split_dimension(unsigned int split_dimension)86 Hints &set_split_dimension(unsigned int split_dimension) 87 { 88 _split_dimension = split_dimension; 89 return *this; 90 } 91 /** Return the prefered split dimension 92 * 93 * @return The split dimension 94 */ split_dimension()95 unsigned int split_dimension() const 96 { 97 return _split_dimension; 98 } 99 100 /** Set the strategy hint 101 * 102 * @param[in] strategy Prefered strategy to use to split the workload 103 * 104 * @return the Hints object 105 */ set_strategy(StrategyHint strategy)106 Hints &set_strategy(StrategyHint strategy) 107 { 108 _strategy = strategy; 109 return *this; 110 } 111 /** Return the prefered strategy to use to split workload. 112 * 113 * @return The strategy 114 */ strategy()115 StrategyHint strategy() const 116 { 117 return _strategy; 118 } 119 /** Return the granule capping threshold to be used by dynamic scheduling. 120 * 121 * @return The capping threshold 122 */ threshold()123 int threshold() const 124 { 125 return _threshold; 126 } 127 128 private: 129 unsigned int _split_dimension; 130 StrategyHint _strategy; 131 int _threshold; 132 }; 133 /** Signature for the workloads to execute */ 134 using Workload = std::function<void(const ThreadInfo &)>; 135 /** Default constructor. */ 136 IScheduler(); 137 138 /** Destructor. */ 139 virtual ~IScheduler() = default; 140 141 /** Sets the number of threads the scheduler will use to run the kernels. 142 * 143 * @param[in] num_threads If set to 0, then one thread per CPU core available on the system will be used, otherwise the number of threads specified. 144 */ 145 virtual void set_num_threads(unsigned int num_threads) = 0; 146 147 /** Sets the number of threads the scheduler will use to run the kernels but also using a binding function to pin the threads to given logical cores 148 * 149 * @param[in] num_threads If set to 0, then one thread per CPU core available on the system will be used, otherwise the number of threads specified. 150 * @param[in] func Binding function to use. 151 */ 152 virtual void set_num_threads_with_affinity(unsigned int num_threads, BindFunc func); 153 154 /** Returns the number of threads that the SingleThreadScheduler has in his pool. 155 * 156 * @return Number of threads available in SingleThreadScheduler. 157 */ 158 virtual unsigned int num_threads() const = 0; 159 160 /** Runs the kernel in the same thread as the caller synchronously. 161 * 162 * @param[in] kernel Kernel to execute. 163 * @param[in] hints Hints for the scheduler. 164 */ 165 virtual void schedule(ICPPKernel *kernel, const Hints &hints) = 0; 166 167 /** Runs the kernel in the same thread as the caller synchronously. 168 * 169 * @param[in] kernel Kernel to execute. 170 * @param[in] hints Hints for the scheduler. 171 * @param[in] tensors Vector containing the tensors to operate on. 172 */ 173 virtual void schedule_op(ICPPKernel *kernel, const Hints &hints, ITensorPack &tensors) = 0; 174 175 /** Execute all the passed workloads 176 * 177 * @note there is no guarantee regarding the order in which the workloads will be executed or whether or not they will be executed in parallel. 178 * 179 * @param[in] workloads Array of workloads to run 180 * @param[in] tag String that can be used by profiling tools to identify the workloads run by the scheduler (Can be null). 181 */ 182 virtual void run_tagged_workloads(std::vector<Workload> &workloads, const char *tag); 183 184 /** Get CPU info. 185 * 186 * @return CPU info. 187 */ 188 CPUInfo &cpu_info(); 189 /** Get a hint for the best possible number of execution threads 190 * 191 * @warning In case we can't work out the best number of threads, 192 * std::thread::hardware_concurrency() is returned else 1 in case of bare metal builds 193 * 194 * @return Best possible number of execution threads to use 195 */ 196 unsigned int num_threads_hint() const; 197 198 protected: 199 /** Execute all the passed workloads 200 * 201 * @note there is no guarantee regarding the order in which the workloads will be executed or whether or not they will be executed in parallel. 202 * 203 * @param[in] workloads Array of workloads to run 204 */ 205 virtual void run_workloads(std::vector<Workload> &workloads) = 0; 206 CPUInfo _cpu_info; 207 208 void schedule_common(ICPPKernel *kernel, const Hints &hints, ITensorPack &tensors); 209 210 private: 211 unsigned int _num_threads_hint = {}; 212 }; 213 } // namespace arm_compute 214 #endif /* ARM_COMPUTE_ISCHEDULER_H */ 215