[New Scheduler] Refactor cluster resource scheduler (#10938)

This commit is contained in:
Alex Wu
2020-09-23 15:46:31 -07:00
committed by GitHub
parent ecdaaffc67
commit 295782d411
7 changed files with 594 additions and 563 deletions
+1 -1
View File
@@ -361,7 +361,7 @@ def test_ray_options(shutdown_only):
to_check = ["CPU", "GPU", "memory", "custom1"]
for key in to_check:
assert without_options[key] != with_options[key]
assert without_options[key] != with_options[key], key
assert without_options != with_options
@@ -0,0 +1,384 @@
#include "ray/raylet/scheduling/cluster_resource_data.h"
std::string VectorToString(const std::vector<FixedPoint> &vector) {
std::stringstream buffer;
buffer << "[";
for (size_t i = 0; i < vector.size(); i++) {
buffer << vector[i];
if (i < vector.size() - 1) {
buffer << ", ";
}
}
buffer << "]";
return buffer.str();
}
std::string UnorderedMapToString(const std::unordered_map<std::string, double> &map) {
std::stringstream buffer;
buffer << "[";
for (auto it = map.begin(); it != map.end(); ++it) {
buffer << "(" << it->first << ":" << it->second << ")";
}
buffer << "]";
return buffer.str();
}
/// Convert a vector of doubles to a vector of resource units.
std::vector<FixedPoint> VectorDoubleToVectorFixedPoint(
const std::vector<double> &vector) {
std::vector<FixedPoint> vector_fp(vector.size());
for (size_t i = 0; i < vector.size(); i++) {
vector_fp[i] = vector[i];
}
return vector_fp;
}
/// Convert a vector of resource units to a vector of doubles.
std::vector<double> VectorFixedPointToVectorDouble(
const std::vector<FixedPoint> &vector_fp) {
std::vector<double> vector(vector_fp.size());
for (size_t i = 0; i < vector_fp.size(); i++) {
vector[i] = FixedPoint(vector_fp[i]).Double();
}
return vector;
}
/// Convert a map of resources to a TaskRequest data structure.
TaskRequest ResourceMapToTaskRequest(
StringIdMap &string_to_int_map,
const std::unordered_map<std::string, double> &resource_map) {
size_t i = 0;
TaskRequest task_request;
task_request.predefined_resources.resize(PredefinedResources_MAX);
task_request.custom_resources.resize(resource_map.size());
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
task_request.predefined_resources[0].demand = 0;
task_request.predefined_resources[0].soft = false;
}
for (auto const &resource : resource_map) {
if (resource.first == ray::kCPU_ResourceLabel) {
task_request.predefined_resources[CPU].demand = resource.second;
} else if (resource.first == ray::kGPU_ResourceLabel) {
task_request.predefined_resources[GPU].demand = resource.second;
} else if (resource.first == ray::kTPU_ResourceLabel) {
task_request.predefined_resources[TPU].demand = resource.second;
} else if (resource.first == ray::kMemory_ResourceLabel) {
task_request.predefined_resources[MEM].demand = resource.second;
} else {
task_request.custom_resources[i].id = string_to_int_map.Insert(resource.first);
task_request.custom_resources[i].demand = resource.second;
task_request.custom_resources[i].soft = false;
i++;
}
}
task_request.custom_resources.resize(i);
return task_request;
}
TaskRequest TaskResourceInstances::ToTaskRequest() const {
TaskRequest task_req;
task_req.predefined_resources.resize(PredefinedResources_MAX);
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
task_req.predefined_resources[i].demand = 0;
for (auto predefined_resource_instance : this->predefined_resources[i]) {
task_req.predefined_resources[i].demand += predefined_resource_instance;
}
}
task_req.custom_resources.resize(this->custom_resources.size());
size_t i = 0;
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
task_req.custom_resources[i].id = it->first;
task_req.custom_resources[i].soft = false;
task_req.custom_resources[i].demand = 0;
for (size_t j = 0; j < it->second.size(); j++) {
task_req.custom_resources[i].demand += it->second[j];
}
i++;
}
return task_req;
}
/// Convert a map of resources to a TaskRequest data structure.
///
/// \param string_to_int_map: Map between names and ids maintained by the
/// \param resource_map_total: Total capacities of resources we want to convert.
/// \param resource_map_available: Available capacities of resources we want to convert.
///
/// \request Conversion result to a TaskRequest data structure.
NodeResources ResourceMapToNodeResources(
StringIdMap &string_to_int_map,
const std::unordered_map<std::string, double> &resource_map_total,
const std::unordered_map<std::string, double> &resource_map_available) {
NodeResources node_resources;
node_resources.predefined_resources.resize(PredefinedResources_MAX);
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
node_resources.predefined_resources[i].total =
node_resources.predefined_resources[i].available = 0;
}
for (auto const &resource : resource_map_total) {
ResourceCapacity resource_capacity;
resource_capacity.total = resource.second;
auto it = resource_map_available.find(resource.first);
if (it == resource_map_available.end()) {
resource_capacity.available = 0;
} else {
resource_capacity.available = it->second;
}
if (resource.first == ray::kCPU_ResourceLabel) {
node_resources.predefined_resources[CPU] = resource_capacity;
} else if (resource.first == ray::kGPU_ResourceLabel) {
node_resources.predefined_resources[GPU] = resource_capacity;
} else if (resource.first == ray::kTPU_ResourceLabel) {
node_resources.predefined_resources[TPU] = resource_capacity;
} else if (resource.first == ray::kMemory_ResourceLabel) {
node_resources.predefined_resources[MEM] = resource_capacity;
} else {
// This is a custom resource.
node_resources.custom_resources.emplace(string_to_int_map.Insert(resource.first),
resource_capacity);
}
}
return node_resources;
}
bool NodeResources::operator==(const NodeResources &other) {
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
if (this->predefined_resources[i].total != other.predefined_resources[i].total) {
return false;
}
if (this->predefined_resources[i].available !=
other.predefined_resources[i].available) {
return false;
}
}
if (this->custom_resources.size() != other.custom_resources.size()) {
return false;
}
for (auto it1 = this->custom_resources.begin(); it1 != this->custom_resources.end();
++it1) {
auto it2 = other.custom_resources.find(it1->first);
if (it2 == other.custom_resources.end()) {
return false;
}
if (it1->second.total != it2->second.total) {
return false;
}
if (it1->second.available != it2->second.available) {
return false;
}
}
return true;
}
std::string NodeResources::DebugString(StringIdMap string_to_in_map) const {
std::stringstream buffer;
buffer << " {\n";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << "\t";
switch (i) {
case CPU:
buffer << "CPU: ";
break;
case MEM:
buffer << "MEM: ";
break;
case GPU:
buffer << "GPU: ";
break;
case TPU:
buffer << "TPU: ";
break;
default:
RAY_CHECK(false) << "This should never happen.";
break;
}
buffer << "(" << this->predefined_resources[i].total << ":"
<< this->predefined_resources[i].available << ")\n";
}
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
buffer << "\t" << string_to_in_map.Get(it->first) << ":(" << it->second.total << ":"
<< it->second.available << ")\n";
}
buffer << "}" << std::endl;
return buffer.str();
}
bool NodeResourceInstances::operator==(const NodeResourceInstances &other) {
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
if (!EqualVectors(this->predefined_resources[i].total,
other.predefined_resources[i].total)) {
return false;
}
if (!EqualVectors(this->predefined_resources[i].available,
other.predefined_resources[i].available)) {
return false;
}
}
if (this->custom_resources.size() != other.custom_resources.size()) {
return false;
}
for (auto it1 = this->custom_resources.begin(); it1 != this->custom_resources.end();
++it1) {
auto it2 = other.custom_resources.find(it1->first);
if (it2 == other.custom_resources.end()) {
return false;
}
if (!EqualVectors(it1->second.total, it2->second.total)) {
return false;
}
if (!EqualVectors(it1->second.available, it2->second.available)) {
return false;
}
}
return true;
}
std::string NodeResourceInstances::DebugString(StringIdMap string_to_int_map) const {
std::stringstream buffer;
buffer << "{\n";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << "\t";
switch (i) {
case CPU:
buffer << "CPU: ";
break;
case MEM:
buffer << "MEM: ";
break;
case GPU:
buffer << "GPU: ";
break;
case TPU:
buffer << "TPU: ";
break;
default:
RAY_CHECK(false) << "This should never happen.";
break;
}
buffer << "(" << VectorToString(predefined_resources[i].total) << ":"
<< VectorToString(this->predefined_resources[i].available) << ")\n";
}
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
buffer << "\t" << string_to_int_map.Get(it->first) << ":("
<< VectorToString(it->second.total) << ":"
<< VectorToString(it->second.available) << ")\n";
}
buffer << "}" << std::endl;
return buffer.str();
};
TaskResourceInstances NodeResourceInstances::GetAvailableResourceInstances() {
TaskResourceInstances task_resources;
task_resources.predefined_resources.resize(PredefinedResources_MAX);
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
task_resources.predefined_resources[i] = this->predefined_resources[i].available;
}
for (const auto &it : this->custom_resources) {
task_resources.custom_resources.emplace(it.first, it.second.available);
}
return task_resources;
};
std::string TaskRequest::DebugString() const {
std::stringstream buffer;
buffer << " {";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << "(" << this->predefined_resources[i].demand << ":"
<< this->predefined_resources[i].soft << ") ";
}
buffer << "}";
buffer << " [";
for (size_t i = 0; i < this->custom_resources.size(); i++) {
buffer << this->custom_resources[i].id << ":"
<< "(" << this->custom_resources[i].demand << ":"
<< this->custom_resources[i].soft << ") ";
}
buffer << "]" << std::endl;
return buffer.str();
}
bool TaskResourceInstances::IsEmpty() const {
// Check whether all resource instances of a task are zero.
for (const auto &predefined_resource : predefined_resources) {
for (const auto &predefined_resource_instance : predefined_resource) {
if (predefined_resource_instance != 0) {
return false;
}
}
}
for (const auto &custom_resource : custom_resources) {
for (const auto &custom_resource_instances : custom_resource.second) {
if (custom_resource_instances != 0) {
return false;
}
}
}
return true;
}
std::string TaskResourceInstances::DebugString() const {
std::stringstream buffer;
buffer << std::endl << " Allocation: {";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << VectorToString(this->predefined_resources[i]);
}
buffer << "}";
buffer << " [";
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
buffer << it->first << ":" << VectorToString(it->second) << ", ";
}
buffer << "]" << std::endl;
return buffer.str();
}
bool EqualVectors(const std::vector<FixedPoint> &v1, const std::vector<FixedPoint> &v2) {
return (v1.size() == v2.size() && std::equal(v1.begin(), v1.end(), v2.begin()));
}
bool TaskResourceInstances::operator==(const TaskResourceInstances &other) {
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
if (!EqualVectors(this->predefined_resources[i], other.predefined_resources[i])) {
return false;
}
}
if (this->custom_resources.size() != other.custom_resources.size()) {
return false;
}
for (auto it1 = this->custom_resources.begin(); it1 != this->custom_resources.end();
++it1) {
auto it2 = other.custom_resources.find(it1->first);
if (it2 == other.custom_resources.end()) {
return false;
}
if (!EqualVectors(it1->second, it2->second)) {
return false;
}
}
return true;
}
@@ -0,0 +1,186 @@
// Copyright 2017 The Ray Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <iostream>
#include <sstream>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "ray/common/task/scheduling_resources.h"
#include "ray/raylet/scheduling/fixed_point.h"
#include "ray/raylet/scheduling/scheduling_ids.h"
#include "ray/util/logging.h"
/// List of predefined resources.
enum PredefinedResources { CPU, MEM, GPU, TPU, PredefinedResources_MAX };
/// Helper function to compare two vectors with FixedPoint values.
bool EqualVectors(const std::vector<FixedPoint> &v1, const std::vector<FixedPoint> &v2);
/// Convert a vector of doubles to a vector of resource units.
std::vector<FixedPoint> VectorDoubleToVectorFixedPoint(const std::vector<double> &vector);
/// Convert a vector of resource units to a vector of doubles.
std::vector<double> VectorFixedPointToVectorDouble(
const std::vector<FixedPoint> &vector_fp);
struct ResourceCapacity {
FixedPoint total;
FixedPoint available;
};
/// Capacities of each instance of a resource.
struct ResourceInstanceCapacities {
std::vector<FixedPoint> total;
std::vector<FixedPoint> available;
};
struct ResourceRequest {
/// Amount of resource being requested.
FixedPoint demand;
/// Specify whether the request is soft or hard.
/// If hard, the entire request is denied if the demand exceeds the resource
/// availability. Otherwise, the request can be still be granted.
/// Prefernces are given to the nodes with the lowest number of violations.
bool soft;
};
/// Resource request, including resource ID. This is used for custom resources.
struct ResourceRequestWithId : ResourceRequest {
/// Resource ID.
int64_t id;
};
// Data structure specifying the capacity of each resource requested by a task.
class TaskRequest {
public:
/// List of predefined resources required by the task.
std::vector<ResourceRequest> predefined_resources;
/// List of custom resources required by the task.
std::vector<ResourceRequestWithId> custom_resources;
/// List of placement hints. A placement hint is a node on which
/// we desire to run this task. This is a soft constraint in that
/// the task will run on a different node in the cluster, if none of the
/// nodes in this list can schedule this task.
absl::flat_hash_set<int64_t> placement_hints;
/// Returns human-readable string for this task request.
std::string DebugString() const;
};
// Data structure specifying the capacity of each instance of each resource
// allocated to a task.
class TaskResourceInstances {
public:
/// The list of instances of each predifined resource allocated to a task.
std::vector<std::vector<FixedPoint>> predefined_resources;
/// The list of instances of each custom resource allocated to a task.
absl::flat_hash_map<int64_t, std::vector<FixedPoint>> custom_resources;
bool operator==(const TaskResourceInstances &other);
/// For each resource of this request aggregate its instances.
TaskRequest ToTaskRequest() const;
/// Get CPU instances only.
std::vector<FixedPoint> GetCPUInstances() const {
if (!this->predefined_resources.empty()) {
return this->predefined_resources[CPU];
} else {
return {};
}
};
std::vector<double> GetCPUInstancesDouble() const {
if (!this->predefined_resources.empty()) {
return VectorFixedPointToVectorDouble(this->predefined_resources[CPU]);
} else {
return {};
}
};
/// Get GPU instances only.
std::vector<FixedPoint> GetGPUInstances() const {
if (!this->predefined_resources.empty()) {
return this->predefined_resources[GPU];
} else {
return {};
}
};
std::vector<double> GetGPUInstancesDouble() const {
if (!this->predefined_resources.empty()) {
return VectorFixedPointToVectorDouble(this->predefined_resources[GPU]);
} else {
return {};
}
};
/// Get mem instances only.
std::vector<FixedPoint> GetMemInstances() const {
if (!this->predefined_resources.empty()) {
return this->predefined_resources[MEM];
} else {
return {};
}
};
std::vector<double> GetMemInstancesDouble() const {
if (!this->predefined_resources.empty()) {
return VectorFixedPointToVectorDouble(this->predefined_resources[MEM]);
} else {
return {};
}
};
/// Check whether there are no resource instances.
bool IsEmpty() const;
/// Returns human-readable string for these resources.
std::string DebugString() const;
};
/// Total and available capacities of each resource of a node.
class NodeResources {
public:
/// Available and total capacities for predefined resources.
std::vector<ResourceCapacity> predefined_resources;
/// Map containing custom resources. The key of each entry represents the
/// custom resource ID.
absl::flat_hash_map<int64_t, ResourceCapacity> custom_resources;
/// Returns if this equals another node resources.
bool operator==(const NodeResources &other);
/// Returns human-readable string for these resources.
std::string DebugString(StringIdMap string_to_int_map) const;
};
/// Total and available capacities of each resource instance.
/// This is used to describe the resources of the local node.
class NodeResourceInstances {
public:
/// Available and total capacities for each instance of a predefined resource.
std::vector<ResourceInstanceCapacities> predefined_resources;
/// Map containing custom resources. The key of each entry represents the
/// custom resource ID.
absl::flat_hash_map<int64_t, ResourceInstanceCapacities> custom_resources;
/// Extract available resource instances.
TaskResourceInstances GetAvailableResourceInstances();
/// Returns if this equals another node resources.
bool operator==(const NodeResourceInstances &other);
/// Returns human-readable string for these resources.
std::string DebugString(StringIdMap string_to_int_map) const;
};
/// \request Conversion result to a TaskRequest data structure.
NodeResources ResourceMapToNodeResources(
StringIdMap &string_to_int_map,
const std::unordered_map<std::string, double> &resource_map_total,
const std::unordered_map<std::string, double> &resource_map_available);
/// Convert a map of resources to a TaskRequest data structure.
TaskRequest ResourceMapToTaskRequest(
StringIdMap &string_to_int_map,
const std::unordered_map<std::string, double> &resource_map);
@@ -14,413 +14,6 @@
#include "ray/raylet/scheduling/cluster_resource_scheduler.h"
std::string VectorToString(const std::vector<FixedPoint> &vector) {
std::stringstream buffer;
buffer << "[";
for (size_t i = 0; i < vector.size(); i++) {
buffer << vector[i];
if (i < vector.size() - 1) {
buffer << ", ";
}
}
buffer << "]";
return buffer.str();
}
std::string UnorderedMapToString(const std::unordered_map<std::string, double> &map) {
std::stringstream buffer;
buffer << "[";
for (auto it = map.begin(); it != map.end(); ++it) {
buffer << "(" << it->first << ":" << it->second << ")";
}
buffer << "]";
return buffer.str();
}
/// Convert a vector of doubles to a vector of resource units.
std::vector<FixedPoint> VectorDoubleToVectorFixedPoint(
const std::vector<double> &vector) {
std::vector<FixedPoint> vector_fp(vector.size());
for (size_t i = 0; i < vector.size(); i++) {
vector_fp[i] = vector[i];
}
return vector_fp;
}
/// Convert a vector of resource units to a vector of doubles.
std::vector<double> VectorFixedPointToVectorDouble(
const std::vector<FixedPoint> &vector_fp) {
std::vector<double> vector(vector_fp.size());
for (size_t i = 0; i < vector_fp.size(); i++) {
vector[i] = FixedPoint(vector_fp[i]).Double();
}
return vector;
}
/// Convert a map of resources to a TaskRequest data structure.
TaskRequest ResourceMapToTaskRequest(
StringIdMap &string_to_int_map,
const std::unordered_map<std::string, double> &resource_map) {
size_t i = 0;
TaskRequest task_request;
task_request.predefined_resources.resize(PredefinedResources_MAX);
task_request.custom_resources.resize(resource_map.size());
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
task_request.predefined_resources[0].demand = 0;
task_request.predefined_resources[0].soft = false;
}
for (auto const &resource : resource_map) {
if (resource.first == ray::kCPU_ResourceLabel) {
task_request.predefined_resources[CPU].demand = resource.second;
} else if (resource.first == ray::kGPU_ResourceLabel) {
task_request.predefined_resources[GPU].demand = resource.second;
} else if (resource.first == ray::kTPU_ResourceLabel) {
task_request.predefined_resources[TPU].demand = resource.second;
} else if (resource.first == ray::kMemory_ResourceLabel) {
task_request.predefined_resources[MEM].demand = resource.second;
} else {
task_request.custom_resources[i].id = string_to_int_map.Insert(resource.first);
task_request.custom_resources[i].demand = resource.second;
task_request.custom_resources[i].soft = false;
i++;
}
}
task_request.custom_resources.resize(i);
return task_request;
}
TaskRequest TaskResourceInstances::ToTaskRequest() const {
TaskRequest task_req;
task_req.predefined_resources.resize(PredefinedResources_MAX);
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
task_req.predefined_resources[i].demand = 0;
for (auto predefined_resource_instance : this->predefined_resources[i]) {
task_req.predefined_resources[i].demand += predefined_resource_instance;
}
}
task_req.custom_resources.resize(this->custom_resources.size());
size_t i = 0;
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
task_req.custom_resources[i].id = it->first;
task_req.custom_resources[i].soft = false;
task_req.custom_resources[i].demand = 0;
for (size_t j = 0; j < it->second.size(); j++) {
task_req.custom_resources[i].demand += it->second[j];
}
i++;
}
return task_req;
}
/// Convert a map of resources to a TaskRequest data structure.
///
/// \param string_to_int_map: Map between names and ids maintained by the
/// \param resource_map_total: Total capacities of resources we want to convert.
/// \param resource_map_available: Available capacities of resources we want to convert.
///
/// \request Conversion result to a TaskRequest data structure.
NodeResources ResourceMapToNodeResources(
StringIdMap &string_to_int_map,
const std::unordered_map<std::string, double> &resource_map_total,
const std::unordered_map<std::string, double> &resource_map_available) {
NodeResources node_resources;
node_resources.predefined_resources.resize(PredefinedResources_MAX);
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
node_resources.predefined_resources[i].total =
node_resources.predefined_resources[i].available = 0;
}
for (auto const &resource : resource_map_total) {
ResourceCapacity resource_capacity;
resource_capacity.total = resource.second;
auto it = resource_map_available.find(resource.first);
if (it == resource_map_available.end()) {
resource_capacity.available = 0;
} else {
resource_capacity.available = it->second;
}
if (resource.first == ray::kCPU_ResourceLabel) {
node_resources.predefined_resources[CPU] = resource_capacity;
} else if (resource.first == ray::kGPU_ResourceLabel) {
node_resources.predefined_resources[GPU] = resource_capacity;
} else if (resource.first == ray::kTPU_ResourceLabel) {
node_resources.predefined_resources[TPU] = resource_capacity;
} else if (resource.first == ray::kMemory_ResourceLabel) {
node_resources.predefined_resources[MEM] = resource_capacity;
} else {
// This is a custom resource.
node_resources.custom_resources.emplace(string_to_int_map.Insert(resource.first),
resource_capacity);
}
}
return node_resources;
}
bool operator<(FixedPoint const &ru1, FixedPoint const &ru2) {
return (ru1.i_ < ru2.i_);
};
bool operator>(FixedPoint const &ru1, FixedPoint const &ru2) {
return (ru1.i_ > ru2.i_);
};
bool operator<=(FixedPoint const &ru1, FixedPoint const &ru2) {
return (ru1.i_ <= ru2.i_);
};
bool operator>=(FixedPoint const &ru1, FixedPoint const &ru2) {
return (ru1.i_ >= ru2.i_);
};
bool operator==(FixedPoint const &ru1, FixedPoint const &ru2) {
return (ru1.i_ == ru2.i_);
};
bool operator!=(FixedPoint const &ru1, FixedPoint const &ru2) {
return (ru1.i_ != ru2.i_);
};
std::ostream &operator<<(std::ostream &out, const FixedPoint &ru) {
out << ru.i_;
return out;
}
bool NodeResources::operator==(const NodeResources &other) {
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
if (this->predefined_resources[i].total != other.predefined_resources[i].total) {
return false;
}
if (this->predefined_resources[i].available !=
other.predefined_resources[i].available) {
return false;
}
}
if (this->custom_resources.size() != other.custom_resources.size()) {
return false;
}
for (auto it1 = this->custom_resources.begin(); it1 != this->custom_resources.end();
++it1) {
auto it2 = other.custom_resources.find(it1->first);
if (it2 == other.custom_resources.end()) {
return false;
}
if (it1->second.total != it2->second.total) {
return false;
}
if (it1->second.available != it2->second.available) {
return false;
}
}
return true;
}
std::string NodeResources::DebugString(StringIdMap string_to_in_map) const {
std::stringstream buffer;
buffer << " {\n";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << "\t";
switch (i) {
case CPU:
buffer << "CPU: ";
break;
case MEM:
buffer << "MEM: ";
break;
case GPU:
buffer << "GPU: ";
break;
case TPU:
buffer << "TPU: ";
break;
default:
RAY_CHECK(false) << "This should never happen.";
break;
}
buffer << "(" << this->predefined_resources[i].total << ":"
<< this->predefined_resources[i].available << ")\n";
}
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
buffer << "\t" << string_to_in_map.Get(it->first) << ":(" << it->second.total << ":"
<< it->second.available << ")\n";
}
buffer << "}" << std::endl;
return buffer.str();
}
bool NodeResourceInstances::operator==(const NodeResourceInstances &other) {
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
if (!EqualVectors(this->predefined_resources[i].total,
other.predefined_resources[i].total)) {
return false;
}
if (!EqualVectors(this->predefined_resources[i].available,
other.predefined_resources[i].available)) {
return false;
}
}
if (this->custom_resources.size() != other.custom_resources.size()) {
return false;
}
for (auto it1 = this->custom_resources.begin(); it1 != this->custom_resources.end();
++it1) {
auto it2 = other.custom_resources.find(it1->first);
if (it2 == other.custom_resources.end()) {
return false;
}
if (!EqualVectors(it1->second.total, it2->second.total)) {
return false;
}
if (!EqualVectors(it1->second.available, it2->second.available)) {
return false;
}
}
return true;
}
std::string NodeResourceInstances::DebugString(StringIdMap string_to_int_map) const {
std::stringstream buffer;
buffer << "{\n";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << "\t";
switch (i) {
case CPU:
buffer << "CPU: ";
break;
case MEM:
buffer << "MEM: ";
break;
case GPU:
buffer << "GPU: ";
break;
case TPU:
buffer << "TPU: ";
break;
default:
RAY_CHECK(false) << "This should never happen.";
break;
}
buffer << "(" << VectorToString(predefined_resources[i].total) << ":"
<< VectorToString(this->predefined_resources[i].available) << ")\n";
}
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
buffer << "\t" << string_to_int_map.Get(it->first) << ":("
<< VectorToString(it->second.total) << ":"
<< VectorToString(it->second.available) << ")\n";
}
buffer << "}" << std::endl;
return buffer.str();
};
TaskResourceInstances NodeResourceInstances::GetAvailableResourceInstances() {
TaskResourceInstances task_resources;
task_resources.predefined_resources.resize(PredefinedResources_MAX);
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
task_resources.predefined_resources[i] = this->predefined_resources[i].available;
}
for (const auto &it : this->custom_resources) {
task_resources.custom_resources.emplace(it.first, it.second.available);
}
return task_resources;
};
std::string TaskRequest::DebugString() const {
std::stringstream buffer;
buffer << " {";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << "(" << this->predefined_resources[i].demand << ":"
<< this->predefined_resources[i].soft << ") ";
}
buffer << "}";
buffer << " [";
for (size_t i = 0; i < this->custom_resources.size(); i++) {
buffer << this->custom_resources[i].id << ":"
<< "(" << this->custom_resources[i].demand << ":"
<< this->custom_resources[i].soft << ") ";
}
buffer << "]" << std::endl;
return buffer.str();
}
bool TaskResourceInstances::IsEmpty() const {
// Check whether all resource instances of a task are zero.
for (const auto &predefined_resource : predefined_resources) {
for (const auto &predefined_resource_instance : predefined_resource) {
if (predefined_resource_instance != 0) {
return false;
}
}
}
for (const auto &custom_resource : custom_resources) {
for (const auto &custom_resource_instances : custom_resource.second) {
if (custom_resource_instances != 0) {
return false;
}
}
}
return true;
}
std::string TaskResourceInstances::DebugString() const {
std::stringstream buffer;
buffer << std::endl << " Allocation: {";
for (size_t i = 0; i < this->predefined_resources.size(); i++) {
buffer << VectorToString(this->predefined_resources[i]);
}
buffer << "}";
buffer << " [";
for (auto it = this->custom_resources.begin(); it != this->custom_resources.end();
++it) {
buffer << it->first << ":" << VectorToString(it->second) << ", ";
}
buffer << "]" << std::endl;
return buffer.str();
}
bool EqualVectors(const std::vector<FixedPoint> &v1, const std::vector<FixedPoint> &v2) {
return (v1.size() == v2.size() && std::equal(v1.begin(), v1.end(), v2.begin()));
}
bool TaskResourceInstances::operator==(const TaskResourceInstances &other) {
for (size_t i = 0; i < PredefinedResources_MAX; i++) {
if (!EqualVectors(this->predefined_resources[i], other.predefined_resources[i])) {
return false;
}
}
if (this->custom_resources.size() != other.custom_resources.size()) {
return false;
}
for (auto it1 = this->custom_resources.begin(); it1 != this->custom_resources.end();
++it1) {
auto it2 = other.custom_resources.find(it1->first);
if (it2 == other.custom_resources.end()) {
return false;
}
if (!EqualVectors(it1->second, it2->second)) {
return false;
}
}
return true;
}
ClusterResourceScheduler::ClusterResourceScheduler(
int64_t local_node_id, const NodeResources &local_node_resources)
: local_node_id_(local_node_id) {
@@ -21,161 +21,14 @@
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "ray/common/task/scheduling_resources.h"
#include "ray/raylet/scheduling/cluster_resource_data.h"
#include "ray/raylet/scheduling/fixed_point.h"
#include "ray/raylet/scheduling/scheduling_ids.h"
#include "ray/util/logging.h"
/// List of predefined resources.
enum PredefinedResources { CPU, MEM, GPU, TPU, PredefinedResources_MAX };
// Specify resources that consists of unit-size instances.
static std::unordered_set<int64_t> UnitInstanceResources{CPU, GPU, TPU};
/// Helper function to compare two vectors with FixedPoint values.
bool EqualVectors(const std::vector<FixedPoint> &v1, const std::vector<FixedPoint> &v2);
/// Convert a vector of doubles to a vector of resource units.
std::vector<FixedPoint> VectorDoubleToVectorFixedPoint(const std::vector<double> &vector);
/// Convert a vector of resource units to a vector of doubles.
std::vector<double> VectorFixedPointToVectorDouble(
const std::vector<FixedPoint> &vector_fp);
struct ResourceCapacity {
FixedPoint total;
FixedPoint available;
};
/// Capacities of each instance of a resource.
struct ResourceInstanceCapacities {
std::vector<FixedPoint> total;
std::vector<FixedPoint> available;
};
struct ResourceRequest {
/// Amount of resource being requested.
FixedPoint demand;
/// Specify whether the request is soft or hard.
/// If hard, the entire request is denied if the demand exceeds the resource
/// availability. Otherwise, the request can be still be granted.
/// Prefernces are given to the nodes with the lowest number of violations.
bool soft;
};
/// Resource request, including resource ID. This is used for custom resources.
struct ResourceRequestWithId : ResourceRequest {
/// Resource ID.
int64_t id;
};
// Data structure specifying the capacity of each resource requested by a task.
class TaskRequest {
public:
/// List of predefined resources required by the task.
std::vector<ResourceRequest> predefined_resources;
/// List of custom resources required by the task.
std::vector<ResourceRequestWithId> custom_resources;
/// List of placement hints. A placement hint is a node on which
/// we desire to run this task. This is a soft constraint in that
/// the task will run on a different node in the cluster, if none of the
/// nodes in this list can schedule this task.
absl::flat_hash_set<int64_t> placement_hints;
/// Returns human-readable string for this task request.
std::string DebugString() const;
};
// Data structure specifying the capacity of each instance of each resource
// allocated to a task.
class TaskResourceInstances {
public:
/// The list of instances of each predifined resource allocated to a task.
std::vector<std::vector<FixedPoint>> predefined_resources;
/// The list of instances of each custom resource allocated to a task.
absl::flat_hash_map<int64_t, std::vector<FixedPoint>> custom_resources;
bool operator==(const TaskResourceInstances &other);
/// For each resource of this request aggregate its instances.
TaskRequest ToTaskRequest() const;
/// Get CPU instances only.
std::vector<FixedPoint> GetCPUInstances() const {
if (!this->predefined_resources.empty()) {
return this->predefined_resources[CPU];
} else {
return {};
}
};
std::vector<double> GetCPUInstancesDouble() const {
if (!this->predefined_resources.empty()) {
return VectorFixedPointToVectorDouble(this->predefined_resources[CPU]);
} else {
return {};
}
};
/// Get GPU instances only.
std::vector<FixedPoint> GetGPUInstances() const {
if (!this->predefined_resources.empty()) {
return this->predefined_resources[GPU];
} else {
return {};
}
};
std::vector<double> GetGPUInstancesDouble() const {
if (!this->predefined_resources.empty()) {
return VectorFixedPointToVectorDouble(this->predefined_resources[GPU]);
} else {
return {};
}
};
/// Get mem instances only.
std::vector<FixedPoint> GetMemInstances() const {
if (!this->predefined_resources.empty()) {
return this->predefined_resources[MEM];
} else {
return {};
}
};
std::vector<double> GetMemInstancesDouble() const {
if (!this->predefined_resources.empty()) {
return VectorFixedPointToVectorDouble(this->predefined_resources[MEM]);
} else {
return {};
}
};
/// Check whether there are no resource instances.
bool IsEmpty() const;
/// Returns human-readable string for these resources.
std::string DebugString() const;
};
/// Total and available capacities of each resource of a node.
class NodeResources {
public:
/// Available and total capacities for predefined resources.
std::vector<ResourceCapacity> predefined_resources;
/// Map containing custom resources. The key of each entry represents the
/// custom resource ID.
absl::flat_hash_map<int64_t, ResourceCapacity> custom_resources;
/// Returns if this equals another node resources.
bool operator==(const NodeResources &other);
/// Returns human-readable string for these resources.
std::string DebugString(StringIdMap string_to_int_map) const;
};
/// Total and available capacities of each resource instance.
/// This is used to describe the resources of the local node.
class NodeResourceInstances {
public:
/// Available and total capacities for each instance of a predefined resource.
std::vector<ResourceInstanceCapacities> predefined_resources;
/// Map containing custom resources. The key of each entry represents the
/// custom resource ID.
absl::flat_hash_map<int64_t, ResourceInstanceCapacities> custom_resources;
/// Extract available resource instances.
TaskResourceInstances GetAvailableResourceInstances();
/// Returns if this equals another node resources.
bool operator==(const NodeResourceInstances &other);
/// Returns human-readable string for these resources.
std::string DebugString(StringIdMap string_to_int_map) const;
};
/// Class encapsulating the cluster resources and the logic to assign
/// tasks to nodes based on the task's constraints and the available
/// resources at those nodes.
+14
View File
@@ -8,6 +8,8 @@ FixedPoint::FixedPoint(double d) {
i_ = (uint64_t)((d * RESOURCE_UNIT_SCALING) + 0.5);
}
FixedPoint::FixedPoint(int i) { i_ = (i * RESOURCE_UNIT_SCALING); }
FixedPoint FixedPoint::operator+(FixedPoint const &ru) {
FixedPoint res;
res.i_ = i_ + ru.i_;
@@ -53,4 +55,16 @@ FixedPoint FixedPoint::operator=(double const d) {
return *this;
}
bool FixedPoint::operator<(FixedPoint const &ru1) const { return (i_ < ru1.i_); };
bool FixedPoint::operator>(FixedPoint const &ru1) const { return (i_ > ru1.i_); };
bool FixedPoint::operator<=(FixedPoint const &ru1) const { return (i_ <= ru1.i_); };
bool FixedPoint::operator>=(FixedPoint const &ru1) const { return (i_ >= ru1.i_); };
bool FixedPoint::operator==(FixedPoint const &ru1) const { return (i_ == ru1.i_); };
bool FixedPoint::operator!=(FixedPoint const &ru1) const { return (i_ != ru1.i_); };
std::ostream &operator<<(std::ostream &out, FixedPoint const &ru1) {
out << ru1.i_;
return out;
}
double FixedPoint::Double() { return round(i_) / RESOURCE_UNIT_SCALING; };
+8 -7
View File
@@ -12,6 +12,7 @@ class FixedPoint {
public:
FixedPoint(double d = 0);
FixedPoint(int i);
FixedPoint operator+(FixedPoint const &ru);
@@ -29,14 +30,14 @@ class FixedPoint {
FixedPoint operator=(double const d);
friend bool operator<(FixedPoint const &ru1, FixedPoint const &ru2);
friend bool operator>(FixedPoint const &ru1, FixedPoint const &ru2);
friend bool operator<=(FixedPoint const &ru1, FixedPoint const &ru2);
friend bool operator>=(FixedPoint const &ru1, FixedPoint const &ru2);
friend bool operator==(FixedPoint const &ru1, FixedPoint const &ru2);
friend bool operator!=(FixedPoint const &ru1, FixedPoint const &ru2);
bool operator<(FixedPoint const &ru1) const;
bool operator>(FixedPoint const &ru1) const;
bool operator<=(FixedPoint const &ru1) const;
bool operator>=(FixedPoint const &ru1) const;
bool operator==(FixedPoint const &ru1) const;
bool operator!=(FixedPoint const &ru1) const;
double Double();
friend std::ostream &operator<<(std::ostream &out, const FixedPoint &ru);
friend std::ostream &operator<<(std::ostream &out, FixedPoint const &ru1);
};