#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_PLATFORM_TASKS_RESOURCEBUDGET_H #define NUCLEX_PLATFORM_TASKS_RESOURCEBUDGET_H #include "Nuclex/Platform/Config.h" #include "Nuclex/Platform/Tasks/ResourceType.h" #include // for std::size_t #include // for std::uint8_t #include // for std::array #include // for std::atomic #include // for std::shared_ptr namespace Nuclex { namespace Platform { namespace Tasks { // ------------------------------------------------------------------------------------------- // class TaskEnvironment; class ResourceManifest; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Platform::Tasks namespace Nuclex { namespace Platform { namespace Tasks { // ------------------------------------------------------------------------------------------- // /// Keeps a running tally of the remaining resource of a task coordinator class ResourceBudget { /// Short alias for a shared pointer to a resource manifest /// /// You'll find the same typedef in ResourceManifest.h which we didn't include here /// to keep dependencies small. It's only used for readability readons and it's a nested /// typedef to avoid clashes when ResourceManifest.h gets included before this file. /// private: typedef std::shared_ptr ResourceManifestPointer; /// Initalizes a new resource budget public: ResourceBudget(); /// Creates a clone of an existing resource budget /// Resource budget that will be cloned public: ResourceBudget(const ResourceBudget &other); /// Takes over the contents of another resource budget /// Resource budget that will be moved public: ResourceBudget(ResourceBudget &&other); /// Frees all resources owned by the resource budget public: ~ResourceBudget(); /// Overwrites this resource budget with another resource budget /// Other resource budget that will be copied /// This resource budget after the copy public: ResourceBudget &operator =(const ResourceBudget &other); /// Replaces this resource budget with another resource budget /// Other resource budget that will be moved /// This resource budget after the assignment public: ResourceBudget &operator =(ResourceBudget &&other); /// Adds a resource that the task manager can allocate to tasks /// Type of resource the task manager can allocate /// Amount of the resource available on the system /// /// /// Calling this method multiple times with the same resource type will not accumulate /// resources but instead handle it as an alternative resource unit (for example, /// adding two times 16 GiB video memory does not allow the coordinator to run tasks /// requiring 32 GiB video memory, but it will allow for two tasks to run in parallel). /// /// /// The order of calls matters and sets up the individual resource units. For example, /// if you add two sets of video memory, the video memory added in the first call is /// treated as resource unit 0 and the video memory from the second all as resource /// unit 1. This is important when the task coordinator tells tasks which resource unit /// they are supposed to use. /// /// public: void AddResource(ResourceType resourceType, std::size_t amountAvailable); /// Queries the amount of a resource still available in the budget /// Type of resource that will be queried /// The total amount of the queried resource in the system /// /// The task coordinator may return a number slightly lower than the maximum, /// for example to keep some RAM available for the operating system. If there are /// multiple GPUs installed, querying for video memory, for example, will return /// the highest amount of video memory installed on any single device. /// public: std::size_t QueryResourceMaximum(Tasks::ResourceType resourceType) const; /// Counts the number of resource units that exist for a given resource /// Type of resource whose units will be counted /// The number of resource units providing the specified resource public: std::size_t CountResourceUnits(ResourceType resourceType) const; /// Picks resource units that can provide the requested resources /// /// /// On input, unit indices that *must* be used (i.e. to select a certain GPU). /// Provide an empty vector or set its contents to std::size_t(-1) in order to /// allow any unit to be selected. /// /// /// Upon return, receives the actually selected unit indices. Tasks must honor /// the unit selections and pick i.e. the correct GPU (the ordering of units /// always matches the order by which lists units). /// /// /// Task environment whose resources will be blocked /// Task-specific resources that will be blocked /// /// True if the budget had enough reserves to allocate all requested resources /// (and units have been picked, but no resource amounts deducted!), false if one /// or more resources were insufficient. /// /// /// /// This method is useful if you want to find resource units that have sufficient /// capacity to activate an environment and run a task (that also needs resources) /// on them at the same time, but not allocate the memory yet. /// /// /// It is normally followed by a call to , possibly only /// to claim the environment memory but not the task memory in case the goal is to /// active an environment, while merely making sure it is put on resource units /// that can later carry a task using that environment. /// /// public: bool Pick( std::array &inOutUnitIndices, const std::shared_ptr &environment, const ResourceManifestPointer &taskResources = ResourceManifestPointer() ) const; /// Picks resource units that can provide the requested resources /// /// /// On input, unit indices that *must* be used (i.e. to select a certain GPU). /// Provide an empty vector or set its contents to std::size_t(-1) in order to /// allow any unit to be selected. /// /// /// Upon return, receives the actually selected unit indices. Tasks must honor /// the unit selections and pick i.e. the correct GPU (the ordering of units /// always matches the order by which lists units). /// /// /// First resource set that will be blocked /// Second resource set that will also be blocked /// /// True if the budget had enough reserves to allocate all requested resources /// (and units have been picked, but no resource amounts deducted!), false if one /// or more resources were insufficient. /// /// /// /// This method is useful if you want to find resource units that have sufficient /// capacity to activate an environment and run a task (that also needs resources) /// on them at the same time, but not allocate the memory yet. /// /// /// It is normally followed by a call to , possibly only /// to claim the environment memory but not the task memory in case the goal is to /// activate an environment, while merely making sure it is put on resource units /// that can later carry a task using that environment. /// /// public: bool Pick( std::array &inOutUnitIndices, const ResourceManifestPointer &primaryResources, const ResourceManifestPointer &secondaryResources = ResourceManifestPointer() ) const; /// Allocates the specified resouces in the budget if possible /// /// /// On input, unit indices that *must* be used (i.e. to select a certain GPU). /// Provide an empty vector or set its contents to std::size_t(-1) in order to /// allow any unit to be selected. /// /// /// Upon return, receives the actually selected unit indices. Tasks must honor /// the unit selections and pick i.e. the correct GPU (the ordering of units /// always matches the order by which lists units). /// /// /// Task environment whose resources will be blocked /// Task-specific resources that will be blocked /// /// True if the budget had enough reserves to allocate all requested resources, /// false if one or more resources were insufficient and nothing was blocked at all. /// public: bool Allocate( std::array &inOutUnitIndices, const std::shared_ptr &environment, const ResourceManifestPointer &taskResources = ResourceManifestPointer() ); /// Allocates the specified resouces in the budget if possible /// /// /// On input, unit indices that *must* be used (i.e. to select a certain GPU). /// Provide an empty vector or set its contents to std::size_t(-1) in order to /// allow any unit to be selected. /// /// /// Upon return, receives the actually selected unit indices. Tasks must honor /// the unit selections and pick i.e. the correct GPU (the ordering of units /// always matches the order by which lists units). /// /// /// First resource set that will be blocked /// Second resource set that will also be blocked /// /// True if the budget had enough reserves to allocate all requested resources, /// false if one or more resources were insufficient and nothing was blocked at all. /// public: bool Allocate( std::array &inOutUnitIndices, const ResourceManifestPointer &primaryResources, const ResourceManifestPointer &secondaryResources = ResourceManifestPointer() ); /// Returns the specified resouces to the budget /// /// Unit indices that were returned by and to which /// the resources will be released again. /// /// Task environment whose resources will be returned /// Task-specific resources that will be returned public: void Release( const std::array &allocatedUnitIndices, const std::shared_ptr &environment, const ResourceManifestPointer &taskResources = ResourceManifestPointer() ); /// Returns the specified resouces to the budget /// /// Unit indices that were returned by and to which /// the resources will be released again. /// /// First resource set that will be returned /// Second resource set that will also be returned public: void Release( const std::array &allocatedUnitIndices, const ResourceManifestPointer &primaryResources, const ResourceManifestPointer &secondaryResources = ResourceManifestPointer() ); /// Checks whether it is at all possible to execute a task /// Environment needed to execute the task /// Resources the task needs to execute /// True if the task is possible to execute public: bool CanEverExecute( const std::shared_ptr &environment, const ResourceManifestPointer &taskResources = ResourceManifestPointer() ) const; /// Checks whether it is at all possible to execute a task /// Resources from the task's environment /// Resources the task needs to execute /// True if the task is possible to execute public: bool CanEverExecute( const ResourceManifestPointer &primaryResources, const ResourceManifestPointer &secondaryResources = ResourceManifestPointer() ) const; /// Checks whether the task can be executed right now /// Environment needed to execute the task /// Resources the task needs to execute /// True if the task can currently be executed public: bool CanExecuteNow( const std::shared_ptr &environment, const ResourceManifestPointer &taskResources = ResourceManifestPointer() ) const; /// Checks whether the task can be executed right now /// Resources from the task's environment /// Resources the task needs to execute /// True if the task can currently be executed public: bool CanExecuteNow( const ResourceManifestPointer &primaryResources, const ResourceManifestPointer &secondaryResources = ResourceManifestPointer() ) const; #pragma region struct UsableResource /// Informations about a resource that has been added to the budget private: struct UsableResource { /// Number of units of this resource (i.e. GPUs or hard drives) public: std::size_t UnitCount; /// Highest total amount of this resource any unit can provide public: std::size_t HighestTotal; /// Total amounts of this resource per unit public: std::size_t *Total; /// Remaining amount of this resource per unit public: std::atomic_size_t *Remaining; }; #pragma endregion // struct UsableResource /// Resources that are managed by the task coordinator private: std::array resources; /// Hard drives that are currently blocked / allocated to a workload private: std::size_t busyHardDrives; /// Memory block storing all the resource counter arrays private: std::uint8_t *allocatedMemoryBlock; //private: std::uint8_t builtInMemory[256]; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Platform::Tasks #endif // NUCLEX_PLATFORM_TASKS_RESOURCEBUDGET_H