加入注释 #18
|
|
@ -14,38 +14,65 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Include the header file "runtime/collective/collective_comm_lib_loader.h" which contains the declarations for the collective communication library loader.
|
||||
#include "runtime/collective/collective_comm_lib_loader.h"
|
||||
|
||||
// Start of the "mindspore" namespace
|
||||
namespace mindspore {
|
||||
namespace device {
|
||||
bool CollectiveCommLibLoader::Initialize() {
|
||||
std::string err_msg = "";
|
||||
#ifndef _WIN32
|
||||
collective_comm_lib_ptr_ = dlopen(comm_lib_name_.c_str(), RTLD_LAZY);
|
||||
err_msg = GetDlErrorMsg();
|
||||
#else
|
||||
collective_comm_lib_ptr_ = LoadLibrary(comm_lib_name_.c_str());
|
||||
err_msg = std::to_string(GetLastError());
|
||||
#endif
|
||||
if (collective_comm_lib_ptr_ == nullptr) {
|
||||
MS_LOG(EXCEPTION) << "Loading " + comm_lib_name_ + " failed. Error: " + err_msg;
|
||||
}
|
||||
return true;
|
||||
// Start of the "device" namespace
|
||||
namespace device {
|
||||
// Definition of the "Initialize" function of the "CollectiveCommLibLoader" class
|
||||
bool CollectiveCommLibLoader::Initialize() {
|
||||
// Initialize an empty error message string
|
||||
std::string err_msg = "";
|
||||
|
||||
// Check if the platform is not Windows
|
||||
#ifndef _WIN32
|
||||
// Load the collective communication library using dlopen and store the result in collective_comm_lib_ptr_
|
||||
collective_comm_lib_ptr_ = dlopen(comm_lib_name_.c_str(), RTLD_LAZY);
|
||||
// Get the error message from dlopen and store it in err_msg
|
||||
err_msg = GetDlErrorMsg();
|
||||
// If the platform is Windows
|
||||
#else
|
||||
// Load the collective communication library using LoadLibrary and store the result in collective_comm_lib_ptr_
|
||||
collective_comm_lib_ptr_ = LoadLibrary(comm_lib_name_.c_str());
|
||||
// Get the last error code as a string and store it in err_msg
|
||||
err_msg = std::to_string(GetLastError());
|
||||
// End of the platform check
|
||||
#endif
|
||||
|
||||
// Check if the collective_comm_lib_ptr_ is nullptr, indicating a failure in loading the library
|
||||
if (collective_comm_lib_ptr_ == nullptr) {
|
||||
// Log an exception with the error message
|
||||
MS_LOG(EXCEPTION) << "Loading " + comm_lib_name_ + " failed. Error: " + err_msg;
|
||||
}
|
||||
|
||||
// Return true to indicate successful initialization
|
||||
return true;
|
||||
}
|
||||
} // End of the "device" namespace
|
||||
} // End of the "mindspore" namespace
|
||||
|
||||
// Define the function `Finalize` for the `CollectiveCommLibLoader` class
|
||||
bool CollectiveCommLibLoader::Finalize() {
|
||||
// Check if the `collective_comm_lib_ptr_` is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(collective_comm_lib_ptr_);
|
||||
}
|
||||
|
||||
bool CollectiveCommLibLoader::Finalize() {
|
||||
MS_EXCEPTION_IF_NULL(collective_comm_lib_ptr_);
|
||||
|
||||
#ifndef _WIN32
|
||||
// If the operating system is not Windows, use dlclose to close the dynamic library handle
|
||||
if (dlclose(collective_comm_lib_ptr_) != 0) {
|
||||
// If dlclose fails, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Closing " + comm_lib_name_ + " handle failed. Error: " + GetDlErrorMsg();
|
||||
}
|
||||
#else
|
||||
// If the operating system is Windows, use FreeLibrary to close the dynamic library handle
|
||||
if (!FreeLibrary(reinterpret_cast<HINSTANCE__ *>(collective_comm_lib_ptr_))) {
|
||||
// If FreeLibrary fails, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Closing " + comm_lib_name_ + " handle failed. Error: " + std::to_string(GetLastError());
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
} // namespace device
|
||||
} // namespace mindspore
|
||||
// Return true to indicate successful closing of the library handle
|
||||
return true;
|
||||
} // End of namespace device
|
||||
} // End of namespace mindspore
|
||||
|
|
@ -14,67 +14,119 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Include the header file "collective_communication_lib.h" from the "runtime/collective" directory.
|
||||
#include "runtime/collective/collective_communication_lib.h"
|
||||
|
||||
// Start of the namespace "mindspore"
|
||||
namespace mindspore {
|
||||
// Start of the namespace "device"
|
||||
namespace device {
|
||||
// Definition of the function "Finalize" in the class "CollectiveCommunicationLib"
|
||||
bool CollectiveCommunicationLib::Finalize() {
|
||||
// Check if the library is not initialized or already finalized
|
||||
if (!initialized_ || finalized_.load()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Iterate over each element in the 'groups_' map using a range-based for loop
|
||||
for (const auto &group : groups_) {
|
||||
// Check if the value of the current element is null
|
||||
CHECK_IF_NULL(group.second);
|
||||
// Call the 'Finalize' function of the current group and check if it returns false
|
||||
if (!group.second->Finalize()) {
|
||||
// If 'Finalize' returns false, return false from the main function
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Clear the 'groups_' map
|
||||
groups_.clear();
|
||||
// Set the 'initialized_' flag to false
|
||||
initialized_ = false;
|
||||
// Set the 'finalized_' flag to true
|
||||
finalized_ = true;
|
||||
// Return true to indicate successful completion of the main function
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to destroy a communication group based on its name
|
||||
bool CollectiveCommunicationLib::DestroyCommunicationGroup(const std::string &group_name) {
|
||||
// Check if the group exists in the map of groups
|
||||
if (groups_.count(group_name) == 0) {
|
||||
return false;
|
||||
return false; // Return false if the group does not exist
|
||||
}
|
||||
auto group = groups_[group_name];
|
||||
CHECK_IF_NULL(group);
|
||||
if (!group->Finalize()) {
|
||||
return false;
|
||||
auto group = groups_[group_name]; // Get the group object from the map
|
||||
CHECK_IF_NULL(group); // Check if the group object is null
|
||||
if (!group->Finalize()) { // Call the Finalize function of the group object and check if it returns false
|
||||
return false; // Return false if the Finalize function fails
|
||||
}
|
||||
(void)groups_.erase(group_name);
|
||||
return true;
|
||||
(void)groups_.erase(group_name); // Remove the group from the map
|
||||
return true; // Return true to indicate successful destruction of the group
|
||||
}
|
||||
|
||||
// Function to get the rank ID of a group in the CollectiveCommunicationLib class
|
||||
uint32_t CollectiveCommunicationLib::GetRankId(const std::string &group_name) {
|
||||
// Check if the group exists in the groups_ map
|
||||
CHECK_RET(groups_.count(group_name) != 0, true, "The group " + group_name + " does not exist.");
|
||||
|
||||
// Get the group object from the groups_ map
|
||||
auto group = groups_[group_name];
|
||||
|
||||
// Check if the group object is not null
|
||||
CHECK_IF_NULL(group);
|
||||
|
||||
// Return the rank ID of the group using the GetGroupRank() function of the group object
|
||||
return group->GetGroupRank(global_rank_id_);
|
||||
}
|
||||
|
||||
// Function to get the size of a group in the CollectiveCommunicationLib class
|
||||
uint32_t CollectiveCommunicationLib::GetGroupSize(const std::string &group_name) {
|
||||
// Check if the group exists in the groups_ map
|
||||
CHECK_RET(groups_.count(group_name) != 0, true, "The group " + group_name + " does not exist.");
|
||||
|
||||
// Get the group object from the groups_ map
|
||||
auto group = groups_[group_name];
|
||||
|
||||
// Check if the group object is null
|
||||
CHECK_IF_NULL(group);
|
||||
|
||||
// Return the size of the group
|
||||
return group->group_size();
|
||||
}
|
||||
|
||||
// Function to get a communication group based on its name
|
||||
CommunicationGroupPtr CollectiveCommunicationLib::GetGroup(const std::string &group_name) {
|
||||
// Check if the group exists in the map of groups
|
||||
if (groups_.count(group_name) == 0) {
|
||||
// If the group does not exist, return a nullptr
|
||||
return nullptr;
|
||||
}
|
||||
// If the group exists, return a pointer to the group
|
||||
return groups_[group_name];
|
||||
}
|
||||
|
||||
const std::string &CollectiveCommunicationLib::global_group_name() const { return global_group_name_; }
|
||||
// Define a member function named "global_group_name" in the "CollectiveCommunicationLib" class
|
||||
// The function returns a constant reference to a std::string object
|
||||
const std::string &CollectiveCommunicationLib::global_group_name() const {
|
||||
// Return the value of the member variable "global_group_name_"
|
||||
return global_group_name_;
|
||||
}
|
||||
|
||||
uint32_t CollectiveCommunicationLib::global_rank_id() const { return global_rank_id_; }
|
||||
// Define a member function named "global_rank_id" in the "CollectiveCommunicationLib" class
|
||||
uint32_t CollectiveCommunicationLib::global_rank_id() const {
|
||||
// Return the value of the member variable "global_rank_id_"
|
||||
return global_rank_id_;
|
||||
}
|
||||
|
||||
uint32_t CollectiveCommunicationLib::local_rank_id() const { return local_rank_id_; }
|
||||
// This is a member function of the CollectiveCommunicationLib class
|
||||
// It returns the value of the private member variable local_rank_id_
|
||||
// The return type is uint32_t
|
||||
uint32_t CollectiveCommunicationLib::local_rank_id() const {
|
||||
return local_rank_id_;
|
||||
}
|
||||
|
||||
uint32_t CollectiveCommunicationLib::global_rank_size() const { return global_rank_size_; }
|
||||
} // namespace device
|
||||
} // namespace mindspore
|
||||
// Define the function `global_rank_size` in the `CollectiveCommunicationLib` namespace, returning a `uint32_t` value
|
||||
uint32_t CollectiveCommunicationLib::global_rank_size() const {
|
||||
return global_rank_size_; // Return the value of the member variable `global_rank_size_`
|
||||
}
|
||||
|
||||
} // End of the `device` namespace
|
||||
} // End of the `mindspore` namespace
|
||||
|
|
@ -16,42 +16,80 @@
|
|||
|
||||
#include "runtime/collective/communication_group.h"
|
||||
|
||||
// Start of the "mindspore" namespace
|
||||
namespace mindspore {
|
||||
namespace device {
|
||||
CommunicationGroup::CommunicationGroup(const std::string &name, const std::vector<uint32_t> &group_ranks,
|
||||
uint32_t global_rank)
|
||||
: initialized_(false),
|
||||
global_rank_(global_rank),
|
||||
size_(group_ranks.size()),
|
||||
name_(name),
|
||||
group_ranks_(group_ranks) {
|
||||
uint32_t group_rank = 0;
|
||||
// The input group_ranks contains the global ranks of the processes in this group.
|
||||
(void)std::for_each(group_ranks.begin(), group_ranks.end(), [&](const uint32_t &global_rank) {
|
||||
global_to_group_ranks_[global_rank] = group_rank;
|
||||
group_to_global_ranks_[group_rank] = global_rank;
|
||||
group_rank++;
|
||||
});
|
||||
}
|
||||
// Start of the "device" namespace
|
||||
namespace device {
|
||||
// Constructor for the CommunicationGroup class
|
||||
CommunicationGroup::CommunicationGroup(const std::string &name, const std::vector<uint32_t> &group_ranks,
|
||||
uint32_t global_rank)
|
||||
: initialized_(false), // Initialize the "initialized_" member variable to false
|
||||
global_rank_(global_rank), // Initialize the "global_rank_" member variable with the provided value
|
||||
size_(group_ranks.size()), // Initialize the "size_" member variable with the size of the "group_ranks" vector
|
||||
name_(name), // Initialize the "name_" member variable with the provided value
|
||||
group_ranks_(group_ranks) { // Initialize the "group_ranks_" member variable with the provided vector
|
||||
uint32_t group_rank = 0; // Initialize the "group_rank" variable to 0
|
||||
// Iterate over each element in the "group_ranks" vector using a lambda function
|
||||
(void)std::for_each(group_ranks.begin(), group_ranks.end(), [&](const uint32_t &global_rank) {
|
||||
global_to_group_ranks_[global_rank] = group_rank; // Map the global rank to the group rank
|
||||
group_to_global_ranks_[group_rank] = global_rank; // Map the group rank to the global rank
|
||||
group_rank++; // Increment the group rank
|
||||
});
|
||||
}
|
||||
} // End of the "device" namespace
|
||||
} // End of the "mindspore" namespace
|
||||
|
||||
// This function is a member function of the CommunicationGroup class
|
||||
// It takes a uint32_t parameter named global_rank and returns a uint32_t value
|
||||
|
||||
uint32_t CommunicationGroup::GetGroupRank(uint32_t global_rank) {
|
||||
// Check if the global_rank exists in the global_to_group_ranks_ map
|
||||
// If it doesn't exist, throw an exception with an error message
|
||||
CHECK_RET((global_to_group_ranks_.count(global_rank) != 0), true,
|
||||
"Group " + name_ + " doesn't contain the global rank " + std::to_string(global_rank));
|
||||
|
||||
// If the global_rank exists in the map, return the corresponding group rank
|
||||
return global_to_group_ranks_[global_rank];
|
||||
}
|
||||
|
||||
// This function is a member function of the CommunicationGroup class
|
||||
// It takes a group_rank as input and returns the corresponding global rank
|
||||
|
||||
uint32_t CommunicationGroup::GetGlobalRank(uint32_t group_rank) {
|
||||
// Check if the group_rank exists in the group_to_global_ranks_ map
|
||||
CHECK_RET((group_to_global_ranks_.count(group_rank) != 0), true,
|
||||
"Group " + name_ + " doesn't contain the group rank " + std::to_string(group_rank));
|
||||
|
||||
// If the group_rank exists, return the corresponding global rank from the map
|
||||
return group_to_global_ranks_[group_rank];
|
||||
}
|
||||
|
||||
uint32_t CommunicationGroup::group_size() const { return size_; }
|
||||
// Define a member function named "group_size" for the class "CommunicationGroup"
|
||||
// The function returns a value of type "uint32_t"
|
||||
uint32_t CommunicationGroup::group_size() const {
|
||||
// Return the value of the member variable "size_"
|
||||
return size_;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t> &CommunicationGroup::group_ranks() const { return group_ranks_; }
|
||||
// Define a member function named "group_ranks" for the class "CommunicationGroup"
|
||||
// The function returns a constant reference to a vector of unsigned 32-bit integers
|
||||
const std::vector<uint32_t> &CommunicationGroup::group_ranks() const {
|
||||
// Return the private member variable "group_ranks_"
|
||||
return group_ranks_;
|
||||
}
|
||||
|
||||
const std::map<uint32_t, uint32_t> &CommunicationGroup::global_to_group_ranks() const { return global_to_group_ranks_; }
|
||||
// Define a constant member function named global_to_group_ranks() that returns a reference to a constant std::map<uint32_t, uint32_t>
|
||||
const std::map<uint32_t, uint32_t> &CommunicationGroup::global_to_group_ranks() const {
|
||||
// Return the member variable global_to_group_ranks_
|
||||
return global_to_group_ranks_;
|
||||
}
|
||||
|
||||
// Define a constant member function named "group_to_global_ranks" in the "CommunicationGroup" class
|
||||
// This function returns a reference to a constant std::map object with key type uint32_t and value type uint32_t
|
||||
const std::map<uint32_t, uint32_t> &CommunicationGroup::group_to_global_ranks() const {
|
||||
// Return the private member variable "group_to_global_ranks_"
|
||||
return group_to_global_ranks_;
|
||||
}
|
||||
|
||||
const std::map<uint32_t, uint32_t> &CommunicationGroup::group_to_global_ranks() const { return group_to_global_ranks_; }
|
||||
} // namespace device
|
||||
} // namespace mindspore
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,146 +14,295 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Include the header file for the kernel runtime manager
|
||||
#include "runtime/device/kernel_runtime_manager.h"
|
||||
|
||||
// Include the header file for the log adapter utility
|
||||
#include "utils/log_adapter.h"
|
||||
|
||||
// Check if ENABLE_CPU is defined and _WIN32 is not defined
|
||||
#if ((defined ENABLE_CPU) && (!defined _WIN32))
|
||||
#include "ps/ps_cache/ps_cache_manager.h"
|
||||
|
||||
// Include the header file for the PS cache manager
|
||||
#include "ps/ps_cache/ps_cache_manager.h"
|
||||
|
||||
#endif
|
||||
|
||||
// Include the header file for the pynative task manager
|
||||
#include "backend/common/session/pynative_task_manager.h"
|
||||
|
||||
namespace mindspore {
|
||||
namespace device {
|
||||
|
||||
// Function to clear runtime resources
|
||||
void KernelRuntimeManager::ClearRuntimeResource() {
|
||||
// Just remove PyNative tasks before runtime resource release.
|
||||
|
||||
// Reset PyNative tasks before releasing runtime resources
|
||||
session::PynativeTaskManager::GetInstance().Reset();
|
||||
|
||||
// Sync embedding table if cache is enabled and running on worker node
|
||||
#if ((defined ENABLE_CPU) && (!defined _WIN32))
|
||||
if (ps::PSContext::instance()->is_worker() && ps::PsDataPrefetch::GetInstance().cache_enable()) {
|
||||
ps::ps_cache_instance.SyncEmbeddingTable();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Acquire a lock to ensure thread safety
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
// Iterate over the runtime map and release device resources for each device
|
||||
for (auto &iter : runtime_map_) {
|
||||
MS_LOG(INFO) << "Release device " << iter.first;
|
||||
MS_EXCEPTION_IF_NULL(iter.second);
|
||||
iter.second->ReleaseDeviceRes();
|
||||
}
|
||||
|
||||
// Clear the runtime map
|
||||
runtime_map_.clear();
|
||||
}
|
||||
} // namespace device
|
||||
} // namespace mindspore
|
||||
|
||||
// A method to clear the runtime resources associated with a specific graph
|
||||
|
||||
void KernelRuntimeManager::ClearGraphResource(uint32_t graph_id) {
|
||||
|
||||
// Acquire a lock to ensure thread safety
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
// Iterate over the runtime_map_ container
|
||||
for (auto &iter : runtime_map_) {
|
||||
|
||||
// Log an informational message indicating the device and graph being cleared
|
||||
MS_LOG(INFO) << "Clear device " << iter.first << " graph " << graph_id << " runtime resource";
|
||||
|
||||
// Check if the kernel runtime is nullptr
|
||||
if (!iter.second) {
|
||||
|
||||
// Log an error message indicating that the kernel runtime is nullptr
|
||||
MS_LOG(ERROR) << "Kernel runtime is nullptr";
|
||||
|
||||
// Continue to the next iteration of the loop
|
||||
continue;
|
||||
}
|
||||
|
||||
// Call the ClearGraphRuntimeResource method of the kernel runtime to clear the runtime resources for the specified graph
|
||||
iter.second->ClearGraphRuntimeResource(graph_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Definition of the static member function Instance() of the class KernelRuntimeManager
|
||||
|
||||
// Return a reference to the static instance of KernelRuntimeManager
|
||||
KernelRuntimeManager &KernelRuntimeManager::Instance() {
|
||||
|
||||
// Create a static instance of KernelRuntimeManager using default constructor
|
||||
static KernelRuntimeManager instance{};
|
||||
|
||||
// Return the reference to the static instance
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Definition of the Register function in the KernelRuntimeManager class
|
||||
|
||||
void KernelRuntimeManager::Register(const std::string &device_name, KernelRuntimeCreator &&runtime_creator) {
|
||||
|
||||
// Check if the device_name is already registered in the runtime_creators_ map
|
||||
if (runtime_creators_.find(device_name) == runtime_creators_.end()) {
|
||||
|
||||
// If the device_name is not found, add it to the runtime_creators_ map
|
||||
(void)runtime_creators_.emplace(device_name, runtime_creator);
|
||||
}
|
||||
}
|
||||
|
||||
// Define a member function named GetDeviceKey in the class KernelRuntimeManager
|
||||
std::string KernelRuntimeManager::GetDeviceKey(const std::string &device_name, uint32_t device_id) {
|
||||
|
||||
// Concatenate the device_name and device_id using the + operator and store the result in device_key
|
||||
std::string device_key = device_name + "_" + std::to_string(device_id);
|
||||
|
||||
// Return the device_key
|
||||
return device_key;
|
||||
}
|
||||
|
||||
// Function to get a single instance of KernelRuntime based on device name and device ID
|
||||
KernelRuntime *KernelRuntimeManager::GetSingleKernelRuntime(const std::string &device_name, uint32_t device_id) {
|
||||
|
||||
// Generate a unique key for the device name and device ID
|
||||
auto runtime_key = GetDeviceKey(device_name, device_id);
|
||||
|
||||
// Check if the runtime for the given key already exists in the runtime map
|
||||
auto runtime_iter = runtime_map_.find(runtime_key);
|
||||
|
||||
// If the runtime exists, return it
|
||||
if (runtime_iter != runtime_map_.end()) {
|
||||
return runtime_iter->second.get();
|
||||
} else if (!runtime_map_.empty()) {
|
||||
}
|
||||
// If the runtime does not exist, but there are other runtimes in the map
|
||||
else if (!runtime_map_.empty()) {
|
||||
|
||||
// Get the key of the first runtime in the map
|
||||
auto cur_runtime_key = runtime_map_.begin()->first;
|
||||
|
||||
// Find the position of the last underscore in the key
|
||||
auto find_pos = cur_runtime_key.rfind('_');
|
||||
|
||||
// If the underscore is found
|
||||
if (find_pos != std::string::npos) {
|
||||
|
||||
// Check if the size of the key is greater than the position of the underscore + 1
|
||||
if (cur_runtime_key.size() > find_pos + 1) {
|
||||
|
||||
// Get the current device ID from the key
|
||||
auto cur_device_id = cur_runtime_key.substr(find_pos + 1);
|
||||
|
||||
// Throw an exception indicating that the device ID cannot be changed in the runtime
|
||||
MS_LOG(EXCEPTION) << "Can't change device id in runtime, already set device id: " << cur_device_id
|
||||
<< ", set device id: " << device_id << " failed";
|
||||
} else {
|
||||
}
|
||||
// If the size of the key is not greater than the position of the underscore + 1
|
||||
else {
|
||||
|
||||
// Throw an exception indicating an error in the current runtime key size
|
||||
MS_LOG(EXCEPTION) << "Can't change device id in runtime, current runtime_key size error, set device id: "
|
||||
<< device_id << " failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the runtime does not exist and there are no other runtimes in the map, get a new KernelRuntime instance
|
||||
return GetKernelRuntime(device_name, device_id);
|
||||
}
|
||||
// Closing brace to end the main function
|
||||
}
|
||||
|
||||
// GetKernelRuntime function of KernelRuntimeManager class
|
||||
KernelRuntime *KernelRuntimeManager::GetKernelRuntime(const std::string &device_name, uint32_t device_id) {
|
||||
|
||||
// Generate a unique key for the runtime based on the device name and device id
|
||||
std::string runtime_key = GetDeviceKey(device_name, device_id);
|
||||
|
||||
// Acquire a lock to ensure thread safety
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
// Check if the runtime already exists in the runtime map
|
||||
auto runtime_iter = runtime_map_.find(runtime_key);
|
||||
if (runtime_iter != runtime_map_.end()) {
|
||||
// If the runtime exists, return a pointer to it
|
||||
return runtime_iter->second.get();
|
||||
}
|
||||
|
||||
// If the runtime does not exist, create a new one
|
||||
std::shared_ptr<KernelRuntime> kernel_runtime;
|
||||
|
||||
// Find the runtime creator for the specified device name
|
||||
auto creator_iter = runtime_creators_.find(device_name);
|
||||
if (creator_iter != runtime_creators_.end()) {
|
||||
// If a runtime creator is found, create a new instance of the kernel runtime
|
||||
MS_EXCEPTION_IF_NULL(creator_iter->second);
|
||||
kernel_runtime = (creator_iter->second)();
|
||||
MS_EXCEPTION_IF_NULL(kernel_runtime);
|
||||
kernel_runtime->set_device_id(device_id);
|
||||
|
||||
// Add the new runtime to the runtime map
|
||||
runtime_map_[runtime_key] = kernel_runtime;
|
||||
} else {
|
||||
// If no runtime creator is found, throw an exception
|
||||
MS_LOG(EXCEPTION) << "No kernel runtime creator for " << device_name << " with device id " << device_id;
|
||||
}
|
||||
|
||||
return kernel_runtime.get();
|
||||
}
|
||||
|
||||
KernelRuntime *KernelRuntimeManager::GetCurrentKernelRuntime() {
|
||||
auto ms_context = MsContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(ms_context);
|
||||
uint32_t device_id = ms_context->get_param<uint32_t>(MS_CTX_DEVICE_ID);
|
||||
std::string device_name = ms_context->get_param<std::string>(MS_CTX_DEVICE_TARGET);
|
||||
return GetKernelRuntime(device_name, device_id);
|
||||
}
|
||||
// Return the value of the `kernel_runtime` variable using the `get()` function
|
||||
return kernel_runtime.get();
|
||||
|
||||
// Get the current instance of the MsContext
|
||||
auto ms_context = MsContext::GetInstance();
|
||||
|
||||
// Throw an exception if the MsContext is null
|
||||
MS_EXCEPTION_IF_NULL(ms_context);
|
||||
|
||||
// Get the device ID from the MsContext
|
||||
uint32_t device_id = ms_context->get_param<uint32_t>(MS_CTX_DEVICE_ID);
|
||||
|
||||
// Get the device name from the MsContext
|
||||
std::string device_name = ms_context->get_param<std::string>(MS_CTX_DEVICE_TARGET);
|
||||
|
||||
// Return the kernel runtime for the specified device name and device ID
|
||||
return GetKernelRuntime(device_name, device_id);
|
||||
|
||||
// Release the kernel runtime for a specific device
|
||||
void KernelRuntimeManager::ReleaseKernelRuntime(const std::string &device_name, uint32_t device_id) {
|
||||
|
||||
// Reset the Pynative task manager
|
||||
session::PynativeTaskManager::GetInstance().Reset();
|
||||
|
||||
// Generate the runtime key using the device name and device ID
|
||||
std::string runtime_key = GetDeviceKey(device_name, device_id);
|
||||
|
||||
// Acquire a lock to ensure thread safety
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
// Find the runtime corresponding to the runtime key in the runtime map
|
||||
auto runtime_iter = runtime_map_.find(runtime_key);
|
||||
|
||||
// If the runtime is not found, return
|
||||
if (runtime_iter == runtime_map_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the runtime pointer from the iterator
|
||||
auto runtime = runtime_iter->second.get();
|
||||
|
||||
// If the runtime pointer is null, return
|
||||
if (runtime == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If CPU is enabled and not on Windows, and the current context is a worker with caching enabled,
|
||||
// synchronize the embedding table
|
||||
#if ((defined ENABLE_CPU) && (!defined _WIN32))
|
||||
if (ps::PSContext::instance()->is_worker() && ps::PsDataPrefetch::GetInstance().cache_enable()) {
|
||||
ps::ps_cache_instance.SyncEmbeddingTable();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Release the device resources of the runtime
|
||||
runtime->ReleaseDeviceRes();
|
||||
|
||||
// Erase the runtime from the runtime map
|
||||
runtime_map_.erase(runtime_iter);
|
||||
}
|
||||
|
||||
// Definition of the WaitTaskFinishOnDevice function in the KernelRuntimeManager class
|
||||
|
||||
void KernelRuntimeManager::WaitTaskFinishOnDevice() const {
|
||||
|
||||
// Iterate over each key-value pair in the runtime_map_
|
||||
for (const auto &iter : runtime_map_) {
|
||||
|
||||
// Get the kernel_runtime object from the current key-value pair
|
||||
auto kernel_runtime = iter.second;
|
||||
|
||||
try {
|
||||
|
||||
// Check if kernel_runtime is not null and if SyncStream returns false
|
||||
if (kernel_runtime != nullptr && !kernel_runtime->SyncStream()) {
|
||||
|
||||
// Print an error message and return if SyncStream fails
|
||||
MS_LOG(ERROR) << "SyncStream failed";
|
||||
return;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
|
||||
// Print an error message with the exception details and return if an exception occurs during SyncStream
|
||||
MS_LOG(ERROR) << "SyncStream failed, exception:" << ex.what();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of the device namespace
|
||||
} // namespace device
|
||||
} // namespace mindspore
|
||||
|
||||
// End of the mindspore namespace
|
||||
} // namespace mindspore
|
||||
|
|
@ -14,73 +14,146 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Include the header file for launching multiplication on a device
|
||||
#include "runtime/device/launch_mul.h"
|
||||
|
||||
// Include the header file for utility functions
|
||||
#include "abstract/utils.h"
|
||||
|
||||
// Include the header file for single kernel graph in a session
|
||||
#include "backend/common/session/single_kernel_graph.h"
|
||||
|
||||
// Include the header file for ANF runtime algorithm
|
||||
#include "backend/common/session/anf_runtime_algorithm.h"
|
||||
|
||||
// Include the header file for ANF algorithm utilities
|
||||
#include "include/common/utils/anfalgo.h"
|
||||
|
||||
// Include the header file for parallel context utilities
|
||||
#include "include/common/utils/parallel_context.h"
|
||||
|
||||
// Define the namespace "mindspore::device"
|
||||
namespace mindspore::device {
|
||||
std::shared_ptr<session::KernelGraph> LaunchMul::ObtainMulKernelGraph() {
|
||||
std::vector<TypeId> input_dtypes = {dtype_, dtype_};
|
||||
std::vector<TypeId> output_dtypes = {dtype_};
|
||||
// obtain input & output shapes
|
||||
size_t dtype_size = abstract::TypeIdSize(dtype_);
|
||||
if (dtype_size == 0) {
|
||||
MS_LOG(EXCEPTION) << "Divide by zero.";
|
||||
}
|
||||
int64_t shape = SizeToLong(total_size_ / dtype_size);
|
||||
std::vector<std::vector<int64_t>> input_shapes = {{shape}, {1}};
|
||||
std::vector<std::vector<size_t>> output_shapes = {{static_cast<size_t>(shape)}};
|
||||
auto mul_graph = session::SingleKernelGraph::ConstructKernelGraphBasedOnSingleOp(
|
||||
kMulOpName, input_dtypes, input_shapes, output_dtypes, output_shapes);
|
||||
MS_EXCEPTION_IF_NULL(mul_graph);
|
||||
return mul_graph;
|
||||
|
||||
// Define the function "ObtainMulKernelGraph" which returns a shared pointer to a KernelGraph object
|
||||
std::shared_ptr<session::KernelGraph> LaunchMul::ObtainMulKernelGraph() {
|
||||
|
||||
// Create a vector of input data types with the same data type as "dtype_"
|
||||
std::vector<TypeId> input_dtypes = {dtype_, dtype_};
|
||||
|
||||
// Create a vector of output data types with the same data type as "dtype_"
|
||||
std::vector<TypeId> output_dtypes = {dtype_};
|
||||
|
||||
// Calculate the size of the data type in bytes
|
||||
size_t dtype_size = abstract::TypeIdSize(dtype_);
|
||||
|
||||
// Check if the data type size is zero
|
||||
if (dtype_size == 0) {
|
||||
// If the data type size is zero, throw an exception with the message "Divide by zero."
|
||||
MS_LOG(EXCEPTION) << "Divide by zero.";
|
||||
}
|
||||
|
||||
// Calculate the shape of the input and output tensors
|
||||
int64_t shape = SizeToLong(total_size_ / dtype_size);
|
||||
std::vector<std::vector<int64_t>> input_shapes = {{shape}, {1}};
|
||||
std::vector<std::vector<size_t>> output_shapes = {{static_cast<size_t>(shape)}};
|
||||
|
||||
// Construct a KernelGraph object based on a single operation with the name "kMulOpName"
|
||||
auto mul_graph = session::SingleKernelGraph::ConstructKernelGraphBasedOnSingleOp(
|
||||
kMulOpName, input_dtypes, input_shapes, output_dtypes, output_shapes);
|
||||
|
||||
// Check if the mul_graph is null
|
||||
MS_EXCEPTION_IF_NULL(mul_graph);
|
||||
|
||||
// Return the mul_graph
|
||||
return mul_graph;
|
||||
}
|
||||
}
|
||||
|
||||
kernel::KernelMod *LaunchMul::ObtainLaunchMulKernelMod() {
|
||||
if (mul_graph_ == nullptr) {
|
||||
// construct mul kernel graph
|
||||
// If the mul_graph_ is not yet constructed, construct it
|
||||
mul_graph_ = ObtainMulKernelGraph();
|
||||
MS_EXCEPTION_IF_NULL(mul_graph_);
|
||||
// kernel select
|
||||
|
||||
// Perform kernel selection on the mul_graph_
|
||||
KernelSelect(mul_graph_);
|
||||
// kernel build
|
||||
|
||||
// Build the kernels for the mul_graph_
|
||||
KernelBuild(mul_graph_);
|
||||
}
|
||||
// obtain kernel_mod
|
||||
|
||||
// Obtain the kernel_mod for the mul_graph_
|
||||
if (mul_graph_->execution_order().size() != 1) {
|
||||
// If the execution order of the mul graph has more than one node, log an error
|
||||
MS_LOG(ERROR) << "the execution order of the mul graph should have only one node, however, it has "
|
||||
<< mul_graph_->execution_order().size() << " nodes.";
|
||||
}
|
||||
|
||||
// Return the kernel_mod of the first node in the execution order
|
||||
return AnfAlgo::GetKernelMod(mul_graph_->execution_order()[0]);
|
||||
}
|
||||
|
||||
void LaunchMul::ObtainMulInputsAddr() {
|
||||
inputs_addr_.push_back(input1_addr_);
|
||||
// This function is a member function of the class LaunchMul.
|
||||
// It is used to obtain the memory addresses of the inputs for multiplication.
|
||||
|
||||
auto parallel_context = parallel::ParallelContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(parallel_context);
|
||||
auto device_num = parallel_context->device_num();
|
||||
if (device_num == 0) {
|
||||
MS_LOG(ERROR) << "device num can't be zero";
|
||||
}
|
||||
input2_value_ = 1.0f / device_num;
|
||||
auto size = abstract::TypeIdSize(dtype_);
|
||||
auto input_size = AlignSizeForLaunchKernel(size * 1);
|
||||
// alloc memory
|
||||
input2_addr_ = AllocDeviceMem(input_size);
|
||||
CopyHostMemToDevice(size, input_size);
|
||||
inputs_addr_.push_back(input2_addr_);
|
||||
void LaunchMul::ObtainMulInputsAddr() {
|
||||
// Push the memory address of input1_addr_ into the inputs_addr_ vector
|
||||
inputs_addr_.push_back(input1_addr_);
|
||||
}
|
||||
|
||||
// Get the instance of the parallel context
|
||||
auto parallel_context = parallel::ParallelContext::GetInstance();
|
||||
|
||||
// Check if the parallel context is not null
|
||||
MS_EXCEPTION_IF_NULL(parallel_context);
|
||||
|
||||
// Get the number of devices from the parallel context
|
||||
auto device_num = parallel_context->device_num();
|
||||
|
||||
// Check if the number of devices is zero
|
||||
if (device_num == 0) {
|
||||
// Log an error message if the number of devices is zero
|
||||
MS_LOG(ERROR) << "device num can't be zero";
|
||||
}
|
||||
|
||||
// Calculate the value of input2_value_ by dividing 1.0f by the number of devices
|
||||
input2_value_ = 1.0f / device_num;
|
||||
|
||||
// Calculate the size of the data type (dtype_)
|
||||
auto size = abstract::TypeIdSize(dtype_);
|
||||
|
||||
// Calculate the input size by multiplying the size of the data type by 1 and aligning it for launch kernel
|
||||
auto input_size = AlignSizeForLaunchKernel(size * 1);
|
||||
|
||||
// Allocate memory on the device
|
||||
input2_addr_ = AllocDeviceMem(input_size);
|
||||
|
||||
// Copy host memory to the device memory
|
||||
CopyHostMemToDevice(size, input_size);
|
||||
|
||||
// Push the address of input2_addr_ to the inputs_addr_ vector
|
||||
inputs_addr_.push_back(input2_addr_);
|
||||
|
||||
// Definition of the function "FreeInputDeviceMemory" belonging to the "LaunchMul" class
|
||||
|
||||
void LaunchMul::FreeInputDeviceMemory() {
|
||||
|
||||
// Set the value of "input1_addr_" to nullptr, effectively freeing the memory
|
||||
input1_addr_ = nullptr;
|
||||
|
||||
// Check if "input2_addr_" is not nullptr
|
||||
if (input2_addr_ != nullptr) {
|
||||
|
||||
// Call the function "FreeDeviceMem" to free the memory pointed to by "input2_addr_"
|
||||
FreeDeviceMem(input2_addr_);
|
||||
|
||||
// Set the value of "input2_addr_" to nullptr, effectively freeing the memory
|
||||
input2_addr_ = nullptr;
|
||||
}
|
||||
|
||||
// Clear the contents of the "inputs_addr_" vector, effectively freeing the memory
|
||||
inputs_addr_.clear();
|
||||
}
|
||||
} // namespace mindspore::device
|
||||
|
||||
// End of the "mindspore::device" namespace
|
||||
|
|
@ -14,77 +14,153 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "runtime/hardware/device_context_manager.h"
|
||||
// Include the header file "runtime/hardware/device_context_manager.h" which contains the declaration of the DeviceContextManager class or related functions.
|
||||
|
||||
// Define the namespace "mindspore"
|
||||
namespace mindspore {
|
||||
|
||||
// Define the nested namespace "device" within the "mindspore" namespace
|
||||
namespace device {
|
||||
|
||||
// Define the member function "GetInstance" of the class "DeviceContextManager"
|
||||
DeviceContextManager &DeviceContextManager::GetInstance() {
|
||||
|
||||
// Create a static instance of the class "DeviceContextManager" using empty braces to invoke the default constructor
|
||||
static DeviceContextManager instance{};
|
||||
|
||||
// Return the static instance of the class "DeviceContextManager"
|
||||
return instance;
|
||||
}
|
||||
} // End of namespace "device"
|
||||
} // End of namespace "mindspore"
|
||||
|
||||
// Definition of the Register function in the DeviceContextManager class
|
||||
|
||||
void DeviceContextManager::Register(const std::string &device_name, DeviceContextCreator &&device_context_creator) {
|
||||
|
||||
// Check if the device_name is not already registered in the device_context_creators_ map
|
||||
if (device_context_creators_.find(device_name) == device_context_creators_.end()) {
|
||||
|
||||
// If the device_name is not found, add it to the device_context_creators_ map
|
||||
(void)device_context_creators_.emplace(device_name, device_context_creator);
|
||||
}
|
||||
}
|
||||
|
||||
// A member function of the DeviceContextManager class that clears all device contexts
|
||||
|
||||
void DeviceContextManager::ClearDeviceContexts() {
|
||||
|
||||
// Iterate over each device context in the device_contexts_ map
|
||||
for (auto &iter : device_contexts_) {
|
||||
|
||||
// Log an informational message indicating the device being released
|
||||
MS_LOG(INFO) << "Release device " << iter.first;
|
||||
|
||||
// Check if the device context is not null
|
||||
MS_EXCEPTION_IF_NULL(iter.second);
|
||||
|
||||
// Call the Destroy() function of the device context to release any resources
|
||||
iter.second->Destroy();
|
||||
}
|
||||
|
||||
// Clear the device_contexts_ map
|
||||
device_contexts_.clear();
|
||||
}
|
||||
|
||||
DeviceContext *DeviceContextManager::GetOrCreateDeviceContext(const DeviceContextKey &device_context_key) {
|
||||
std::string device_context_key_str = device_context_key.ToString();
|
||||
// Get the string representation of the device context key by calling the ToString() function on the device_context_key object
|
||||
|
||||
// Find the iterator in the map `device_contexts_` that corresponds to the given `device_context_key_str`
|
||||
auto device_context_iter = device_contexts_.find(device_context_key_str);
|
||||
|
||||
// If the iterator is not equal to the end iterator of the map, it means the key was found
|
||||
if (device_context_iter != device_contexts_.end()) {
|
||||
// Return a pointer to the value associated with the key in the map
|
||||
return device_context_iter->second.get();
|
||||
}
|
||||
|
||||
std::shared_ptr<DeviceContext> device_context;
|
||||
auto creator_iter = device_context_creators_.find(device_context_key.device_name_);
|
||||
if (creator_iter != device_context_creators_.end()) {
|
||||
// Declare a shared pointer to a DeviceContext object
|
||||
std::shared_ptr<DeviceContext> device_context;
|
||||
|
||||
// Find the creator iterator in the device_context_creators_ map using the device name from device_context_key
|
||||
auto creator_iter = device_context_creators_.find(device_context_key.device_name_);
|
||||
|
||||
// Check if the creator iterator is not equal to the end iterator of the device_context_creators_ map
|
||||
if (creator_iter != device_context_creators_.end()) {
|
||||
// Call the creator function pointed to by the iterator with device_context_key as the argument
|
||||
device_context = (creator_iter->second)(device_context_key);
|
||||
|
||||
// Check if the device_context is null
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
// Add the device_context to the device_contexts_ map using device_context_key_str as the key
|
||||
device_contexts_[device_context_key_str] = device_context;
|
||||
} else {
|
||||
} else {
|
||||
// Log an exception if the creator iterator is equal to the end iterator, indicating that the device context creation failed
|
||||
MS_LOG(EXCEPTION) << "Create device context failed, please make sure target device:"
|
||||
<< device_context_key.device_name_ << " is available.";
|
||||
}
|
||||
return device_context.get();
|
||||
}
|
||||
|
||||
void DeviceContextManager::UpdateDeviceContextKey(const DeviceContextKey &old_key, const DeviceContextKey &new_key) {
|
||||
std::string old_key_str = old_key.ToString();
|
||||
std::string new_key_str = new_key.ToString();
|
||||
// Return the raw pointer of the device_context
|
||||
return device_context.get();
|
||||
|
||||
auto handle = device_contexts_.extract(old_key_str);
|
||||
if (handle.empty()) {
|
||||
// Update the device context key by converting the old key and new key to strings
|
||||
|
||||
// Convert the old key to a string
|
||||
std::string old_key_str = old_key.ToString();
|
||||
|
||||
// Convert the new key to a string
|
||||
std::string new_key_str = new_key.ToString();
|
||||
|
||||
// Extract the value associated with the key "old_key_str" from the "device_contexts_" container
|
||||
auto handle = device_contexts_.extract(old_key_str);
|
||||
|
||||
// Check if the extracted handle is empty (i.e., the key was not found in the container)
|
||||
if (handle.empty()) {
|
||||
// If the handle is empty, log an exception with an error message indicating that the device context for "old_key_str" was not found
|
||||
MS_LOG(EXCEPTION) << "Can not find device context for: " << old_key_str;
|
||||
}
|
||||
|
||||
handle.key() = new_key_str;
|
||||
(void)device_contexts_.insert(std::move(handle));
|
||||
}
|
||||
|
||||
// Assign the value of new_key_str to the key member of the handle object
|
||||
handle.key() = new_key_str;
|
||||
|
||||
// Insert the handle object into the device_contexts_ container using move semantics
|
||||
// The (void) is used to suppress any unused variable warnings
|
||||
(void)device_contexts_.insert(std::move(handle));
|
||||
|
||||
// Definition of the function "WaitTaskFinishOnDevice" in the "DeviceContextManager" class
|
||||
|
||||
void DeviceContextManager::WaitTaskFinishOnDevice() const {
|
||||
|
||||
// Iterate over each item in the "device_contexts_" map
|
||||
for (const auto &item : device_contexts_) {
|
||||
|
||||
// Get the device context from the current item
|
||||
auto device_context = item.second;
|
||||
|
||||
try {
|
||||
|
||||
// Check if the device context is not null and if the SyncStream function returns false
|
||||
if (device_context != nullptr && !device_context->SyncStream()) {
|
||||
|
||||
// Print an error message using the MS_LOG macro
|
||||
MS_LOG(ERROR) << "SyncStream failed";
|
||||
|
||||
// Return from the function
|
||||
return;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
|
||||
// Print an error message using the MS_LOG macro, including the exception message
|
||||
MS_LOG(ERROR) << "SyncStream failed, exception:" << ex.what();
|
||||
|
||||
// Return from the function
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of the "device" namespace
|
||||
} // namespace device
|
||||
} // namespace mindspore
|
||||
|
||||
// End of the "mindspore" namespace
|
||||
} // namespace mindspore
|
||||
|
|
@ -16,173 +16,293 @@
|
|||
|
||||
#include "runtime/pynative/op_executor.h"
|
||||
|
||||
// Define the namespace "mindspore::runtime"
|
||||
namespace mindspore::runtime {
|
||||
// Define the member function "GetInstance" of the class "OpExecutor"
|
||||
OpExecutor &OpExecutor::GetInstance() {
|
||||
// Create a static instance of the class "OpExecutor"
|
||||
static OpExecutor instance;
|
||||
// Return the reference to the static instance
|
||||
return instance;
|
||||
}
|
||||
|
||||
OpExecutor::OpExecutor() { worker_ = std::make_shared<std::thread>(&OpExecutor::WorkerLoop, this); }
|
||||
// Constructor for the OpExecutor class
|
||||
OpExecutor::OpExecutor() {
|
||||
// Create a shared pointer to a new std::thread object
|
||||
// Pass the address of the WorkerLoop member function of the current OpExecutor object as the thread function
|
||||
// Pass 'this' pointer as the argument to the thread function
|
||||
worker_ = std::make_shared<std::thread>(&OpExecutor::WorkerLoop, this);
|
||||
}
|
||||
|
||||
OpExecutor::~OpExecutor() { WorkerJoin(); }
|
||||
// Destructor for the OpExecutor class
|
||||
OpExecutor::~OpExecutor() {
|
||||
// Call the WorkerJoin() function to ensure all worker threads have completed their tasks
|
||||
WorkerJoin();
|
||||
}
|
||||
|
||||
// Define the Register function of the OpExecutor class
|
||||
void OpExecutor::Register(const std::function<void()> &callback) {
|
||||
// Assign the provided callback function to the batch_build_callback_ member variable
|
||||
batch_build_callback_ = callback;
|
||||
// Set the registered_ flag to true to indicate that a callback function has been registered
|
||||
registered_ = true;
|
||||
}
|
||||
|
||||
// Reset the OpExecutor object
|
||||
void OpExecutor::Reset() {
|
||||
// Clear any resources used by the OpExecutor
|
||||
ClearResources();
|
||||
// Set the batch_build_callback_ to nullptr, indicating that no callback function is registered
|
||||
batch_build_callback_ = nullptr;
|
||||
// Set registered_ to false, indicating that the OpExecutor is not registered
|
||||
registered_ = false;
|
||||
}
|
||||
|
||||
// There is still one task in progress
|
||||
// Try block to handle exceptions
|
||||
try {
|
||||
// Call the function WaitForRun() to wait for a task to complete
|
||||
WaitForRun();
|
||||
} catch (const std::exception &e) {
|
||||
// Catch block to handle exceptions of type std::exception
|
||||
// Log an error message with the error message from the exception
|
||||
MS_LOG(ERROR) << "Wait failed, error message:" << e.what();
|
||||
} catch (...) {
|
||||
// Catch block to handle any other type of exception
|
||||
// Log a generic error message
|
||||
MS_LOG(ERROR) << "Wait failed";
|
||||
}
|
||||
}
|
||||
|
||||
void OpExecutor::ClearResources() {
|
||||
MS_LOG(DEBUG) << "Start clear tasks";
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
ClearRunOpTasks();
|
||||
// Definition of the function ClearResources() in the class OpExecutor
|
||||
|
||||
// Set the build task failed, and no need to run op_run_tasks.
|
||||
void OpExecutor::ClearResources() {
|
||||
// Log a debug message indicating the start of clearing tasks
|
||||
MS_LOG(DEBUG) << "Start clear tasks";
|
||||
|
||||
// Create a lock_guard object to lock the task_mutex_ mutex
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
|
||||
// Call the ClearRunOpTasks() function to clear the run op tasks
|
||||
ClearRunOpTasks();
|
||||
}
|
||||
|
||||
// Loop through each build task in the op_build_tasks_ vector
|
||||
for (auto &build_task : op_build_tasks_) {
|
||||
// Set the build_ready flag of the build task to false
|
||||
build_task->SetBuildReady(false);
|
||||
}
|
||||
// Clear the op_build_tasks_ vector
|
||||
op_build_tasks_.clear();
|
||||
// Print a debug message indicating that the tasks have been cleared
|
||||
MS_LOG(DEBUG) << "End clear tasks";
|
||||
}
|
||||
|
||||
// This function is a member function of the OpExecutor class and is called WaitForBuild
|
||||
void OpExecutor::WaitForBuild() {
|
||||
// Check if the executing_ flag is false
|
||||
if (!executing_) {
|
||||
// Create an instance of the ExecuteGuard class
|
||||
ExecuteGuard guard;
|
||||
// Check if the batch_build_callback_ function pointer is not null
|
||||
if (batch_build_callback_ != nullptr) {
|
||||
// Call the batch_build_callback_ function
|
||||
batch_build_callback_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Definition of the WaitForRun function in the OpExecutor class
|
||||
|
||||
void OpExecutor::WaitForRun() {
|
||||
// Log a debug message indicating the start of the function
|
||||
MS_LOG(DEBUG) << "Start";
|
||||
|
||||
// Create a unique lock object using the task_mutex_ mutex
|
||||
std::unique_lock<std::mutex> lock(task_mutex_);
|
||||
|
||||
// Wait until the op_run_tasks_ queue is empty, using a lambda function as the condition
|
||||
task_cond_var_.wait(lock, [this]() { return op_run_tasks_.empty(); });
|
||||
|
||||
// Check for any exceptions that occurred during the execution of the tasks
|
||||
MsException::Instance().CheckException();
|
||||
|
||||
// Log a debug message indicating that all tasks have finished
|
||||
MS_LOG(DEBUG) << "All task finish";
|
||||
}
|
||||
|
||||
// This function is a member function of the OpExecutor class
|
||||
void OpExecutor::Wait() {
|
||||
// Call the WaitForBuild function to wait for the build process to complete
|
||||
WaitForBuild();
|
||||
|
||||
// Call the WaitForRun function to wait for the run process to complete
|
||||
WaitForRun();
|
||||
}
|
||||
|
||||
// This function is a member function of the class OpExecutor
|
||||
// It takes a shared pointer to an OpBuildTask object as a parameter
|
||||
void OpExecutor::PushOpBuildTask(const std::shared_ptr<OpBuildTask> &op_build_task) {
|
||||
// Create a lock_guard object to lock the task_mutex_ mutex
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
// Add the op_build_task to the end of the op_build_tasks_ vector
|
||||
op_build_tasks_.push_back(op_build_task);
|
||||
}
|
||||
|
||||
// This function is a member function of the OpExecutor class
|
||||
// It takes a shared pointer to an OpTask object as a parameter
|
||||
void OpExecutor::PushOpRunTask(const std::shared_ptr<OpTask> &op_run_task) {
|
||||
// Create a lock_guard object to lock the task_mutex_ and automatically unlock it when it goes out of scope
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
|
||||
// Push the op_run_task into the op_run_tasks_ queue
|
||||
op_run_tasks_.push(op_run_task);
|
||||
|
||||
// Insert the name of the op_run_task's context's graph_compiler_info into the actor_in_queue_ set
|
||||
actor_in_queue_.insert(op_run_task->context()->graph_compiler_info()->name_);
|
||||
|
||||
// Notify all threads waiting on the task_cond_var_ condition variable
|
||||
task_cond_var_.notify_all();
|
||||
}
|
||||
|
||||
// A member function of the OpExecutor class that clears the op build tasks
|
||||
void OpExecutor::ClearOpBuildTasks() {
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
std::lock_guard<std::mutex> lock(task_mutex_); // Lock the task mutex to ensure thread safety
|
||||
|
||||
// Iterate through each op build task
|
||||
for (auto &task : op_build_tasks_) {
|
||||
task->SetBuildReady(true);
|
||||
task->SetBuildReady(true); // Set the build ready flag of the task to true
|
||||
}
|
||||
op_build_tasks_.clear();
|
||||
MS_LOG(DEBUG) << "Clear build task";
|
||||
|
||||
op_build_tasks_.clear(); // Clear the op build tasks vector
|
||||
MS_LOG(DEBUG) << "Clear build task"; // Log a debug message indicating that the build tasks have been cleared
|
||||
}
|
||||
|
||||
// Check if the build queue is empty
|
||||
bool OpExecutor::BuildQueueEmpty() {
|
||||
// Acquire a lock on the task mutex to ensure thread safety
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
|
||||
// Return true if the op_build_tasks_ queue is empty, false otherwise
|
||||
return op_build_tasks_.empty();
|
||||
}
|
||||
|
||||
// A member function of the OpExecutor class that checks if the build queue is full
|
||||
bool OpExecutor::BuildQueueFull() {
|
||||
// Create a lock_guard object to lock the task_mutex_ and automatically unlock it when it goes out of scope
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
|
||||
// Check if the size of the op_build_tasks_ vector is greater than the maximum queue size
|
||||
return op_build_tasks_.size() > kMaxQueueSize;
|
||||
}
|
||||
|
||||
// Check if an actor is in the queue
|
||||
bool OpExecutor::ActorInQueue(const std::string &actor_info) {
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
auto iter = actor_in_queue_.find(actor_info);
|
||||
return iter != actor_in_queue_.end();
|
||||
std::lock_guard<std::mutex> lock(task_mutex_); // Lock the mutex to ensure thread safety
|
||||
auto iter = actor_in_queue_.find(actor_info); // Find the actor in the queue
|
||||
return iter != actor_in_queue_.end(); // Return true if the actor is found, false otherwise
|
||||
}
|
||||
|
||||
// A member function of the OpExecutor class that clears the run operation tasks
|
||||
void OpExecutor::ClearRunOpTasks() {
|
||||
// Clear the actor input queue
|
||||
actor_in_queue_.clear();
|
||||
|
||||
// Create an empty queue of shared pointers to OpTask
|
||||
std::queue<std::shared_ptr<OpTask>> empty;
|
||||
|
||||
// No need to worry about ExitOpTask.
|
||||
// ClearRunOpTasks is executed before ~OpExecutor
|
||||
|
||||
// Swap the contents of op_run_tasks_ with the empty queue, effectively clearing op_run_tasks_
|
||||
std::swap(op_run_tasks_, empty);
|
||||
}
|
||||
|
||||
// This is the worker loop function of the OpExecutor class
|
||||
void OpExecutor::WorkerLoop() {
|
||||
while (true) {
|
||||
std::shared_ptr<OpTask> task;
|
||||
std::shared_ptr<OpTask> task; // Declare a shared pointer to an OpTask object
|
||||
{
|
||||
MS_LOG(DEBUG) << "Wait task in queue";
|
||||
std::unique_lock<std::mutex> lock(task_mutex_);
|
||||
task_cond_var_.wait(lock, [this]() { return !op_run_tasks_.empty(); });
|
||||
task = op_run_tasks_.front();
|
||||
MS_LOG(DEBUG) << "Wait task in queue"; // Log a debug message indicating that the worker is waiting for a task in the queue
|
||||
std::unique_lock<std::mutex> lock(task_mutex_); // Acquire a unique lock on the task mutex
|
||||
task_cond_var_.wait(lock, [this]() { return !op_run_tasks_.empty(); }); // Wait on the task condition variable until the op_run_tasks_ queue is not empty
|
||||
task = op_run_tasks_.front(); // Get the first task from the op_run_tasks_ queue
|
||||
}
|
||||
|
||||
// Log a debug message indicating that we are getting a task
|
||||
MS_LOG(DEBUG) << "Get task";
|
||||
// Check if the task pointer is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(task);
|
||||
// Check if the task type is an exit task
|
||||
if (task->task_type() == kExitTask) {
|
||||
// Log a debug message indicating that the thread is exiting
|
||||
MS_LOG(DEBUG) << "Thread exit";
|
||||
// Return, as there is nothing else to do
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to run the task
|
||||
try {
|
||||
task->Run();
|
||||
// Acquire a lock on the task mutex
|
||||
std::unique_lock<std::mutex> lock(task_mutex_);
|
||||
// Check if the op run tasks queue is not empty
|
||||
if (!op_run_tasks_.empty()) {
|
||||
// Remove the front task from the op run tasks queue
|
||||
op_run_tasks_.pop();
|
||||
// Remove the task's name from the actor in queue set
|
||||
actor_in_queue_.erase(task->context()->graph_compiler_info()->name_);
|
||||
}
|
||||
|
||||
if (op_run_tasks_.empty()) {
|
||||
MS_LOG(DEBUG) << "Task queue empty";
|
||||
task_cond_var_.notify_all();
|
||||
if (op_run_tasks_.empty()) { // Check if the task queue is empty
|
||||
MS_LOG(DEBUG) << "Task queue empty"; // Log a debug message indicating that the task queue is empty
|
||||
task_cond_var_.notify_all(); // Notify all waiting threads that the task queue is empty
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
MS_LOG(ERROR) << "Run lazy task failed, error message:" << e.what();
|
||||
} catch (const std::exception &e) { // Catch any exceptions that occur during the execution of the code within the try block
|
||||
MS_LOG(ERROR) << "Run lazy task failed, error message:" << e.what(); // Log an error message indicating that running the lazy task failed, along with the error message provided by the exception
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(task_mutex_);
|
||||
ClearRunOpTasks();
|
||||
MsException::Instance().SetException();
|
||||
task_cond_var_.notify_all();
|
||||
std::unique_lock<std::mutex> lock(task_mutex_); // Acquire a unique lock on the task mutex
|
||||
ClearRunOpTasks(); // Clear the run operation tasks
|
||||
MsException::Instance().SetException(); // Set the exception flag in the MsException singleton instance
|
||||
task_cond_var_.notify_all(); // Notify all waiting threads that an exception has occurred
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Definition of the WorkerJoin function in the OpExecutor class
|
||||
|
||||
void OpExecutor::WorkerJoin() {
|
||||
try {
|
||||
// Avoid worker thread join itself which will cause deadlock
|
||||
// Check if the worker thread is joinable and if it is not the same as the current thread
|
||||
if (worker_->joinable() && worker_->get_id() != std::this_thread::get_id()) {
|
||||
{
|
||||
// Acquire a lock on the task mutex to ensure thread safety
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
|
||||
// Create a shared pointer to an ExitOpTask object
|
||||
auto task = std::make_shared<ExitOpTask>();
|
||||
|
||||
// Push the task onto the op_run_tasks_ queue
|
||||
op_run_tasks_.push(task);
|
||||
|
||||
// Notify all waiting threads that a task has been added to the queue
|
||||
task_cond_var_.notify_all();
|
||||
|
||||
// Log a debug message indicating that an exit task has been pushed and all threads have been notified
|
||||
MS_LOG(DEBUG) << "Push exit task and notify all";
|
||||
}
|
||||
|
||||
// Join the worker thread, blocking the current thread until the worker thread finishes execution
|
||||
worker_->join();
|
||||
|
||||
// Log a debug message indicating that the worker thread has finished joining
|
||||
MS_LOG(DEBUG) << "Worker join finish";
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
// Log an error message if an exception is caught during the execution of the WorkerJoin function
|
||||
MS_LOG(ERROR) << "WorkerJoin failed: " << e.what();
|
||||
} catch (...) {
|
||||
// Log an error message if an unknown exception is caught during the execution of the WorkerJoin function
|
||||
MS_LOG(ERROR) << "WorkerJoin failed";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,134 +14,267 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "runtime/pynative/op_runtime_info.h"
|
||||
// Include the header file "runtime/pynative/op_runtime_info.h" which contains declarations for runtime information related to PyNative operations.
|
||||
|
||||
// Include the header file for the ANF runtime algorithm in the backend common session
|
||||
#include "backend/common/session/anf_runtime_algorithm.h"
|
||||
|
||||
// Include the header file for the ANF algorithm in the common utils
|
||||
#include "include/common/utils/anfalgo.h"
|
||||
|
||||
// Start of the `mindspore::runtime` namespace
|
||||
namespace mindspore::runtime {
|
||||
|
||||
// Start of an anonymous namespace, used for declaring functions or variables with internal linkage
|
||||
namespace {
|
||||
|
||||
// Function to cache execution order for a given `KernelGraphPtr`
|
||||
void CacheForExecutionOrder(const KernelGraphPtr &graph) {
|
||||
// Check if the graph is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
|
||||
// Get the execution order of the nodes in the graph
|
||||
const auto &nodes = graph->execution_order();
|
||||
|
||||
// Iterate over each node in the execution order
|
||||
for (auto const &node : nodes) {
|
||||
// Create empty vectors to store the output formats, types, and tensor sizes
|
||||
std::vector<std::string> formats;
|
||||
std::vector<TypeId> types;
|
||||
std::vector<size_t> tensor_sizes;
|
||||
|
||||
// Get the number of output tensors for the current node
|
||||
auto output_num = common::AnfAlgo::GetOutputTensorNum(node);
|
||||
|
||||
// Iterate over each output tensor of the current node
|
||||
for (size_t i = 0; i < output_num; ++i) {
|
||||
// Get the output format, device data type, and tensor memory size for the current output tensor
|
||||
std::string output_format = AnfAlgo::GetOutputFormat(node, i);
|
||||
auto output_type = AnfAlgo::GetOutputDeviceDataType(node, i);
|
||||
auto tensor_size = AnfAlgo::GetOutputTensorMemSize(node, i);
|
||||
|
||||
// Add the output format, type, and tensor size to their respective vectors
|
||||
formats.emplace_back(output_format);
|
||||
types.emplace_back(output_type);
|
||||
tensor_sizes.emplace_back(tensor_size);
|
||||
}
|
||||
|
||||
// For input
|
||||
// Create a vector to store pairs of pointers to device::KernelInfo objects and their corresponding sizes
|
||||
std::vector<std::pair<device::KernelInfo *, size_t>> input_kernel_infos;
|
||||
|
||||
// Get the number of input tensors for the given node
|
||||
auto input_size = common::AnfAlgo::GetInputTensorNum(node);
|
||||
|
||||
// Iterate over each input tensor
|
||||
for (size_t i = 0; i < input_size; ++i) {
|
||||
|
||||
// Get the previous node's output kernel and index for the current input tensor
|
||||
session::KernelWithIndex kernel_with_index = common::AnfAlgo::GetPrevNodeOutput(node, i, true);
|
||||
|
||||
// Check if the kernel_with_index.first pointer is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(kernel_with_index.first);
|
||||
|
||||
// Add a pair of the kernel_info pointer and the index to the input_kernel_infos vector
|
||||
input_kernel_infos.emplace_back(dynamic_cast<device::KernelInfo *>(kernel_with_index.first->kernel_info()),
|
||||
kernel_with_index.second);
|
||||
}
|
||||
|
||||
// For workspace and output
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
auto kernel_info = dynamic_cast<device::KernelInfo *>(node->kernel_info());
|
||||
// Check if the given node is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
// Attempt to cast the kernel_info of the node to a device::KernelInfo pointer
|
||||
// and assign it to the variable kernel_info
|
||||
auto kernel_info = dynamic_cast<device::KernelInfo *>(node->kernel_info());
|
||||
|
||||
// Set the user data of the node to an instance of OpRuntimeInfo
|
||||
// The user data is set using the set_user_data function of the node object
|
||||
// The user data is created using std::make_shared to create a shared pointer to an instance of OpRuntimeInfo
|
||||
// OpRuntimeInfo is constructed using the provided arguments: formats, types, tensor_sizes, kernel_info, input_kernel_infos
|
||||
node->set_user_data<runtime::OpRuntimeInfo>(
|
||||
std::make_shared<runtime::OpRuntimeInfo>(formats, types, tensor_sizes, kernel_info, input_kernel_infos));
|
||||
}
|
||||
}
|
||||
|
||||
// A function to cache information about the inputs of a given graph
|
||||
void CacheForGraphInputs(const KernelGraphPtr &graph) {
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
|
||||
// Get the inputs of the graph
|
||||
const auto &inputs = graph->inputs();
|
||||
|
||||
// Iterate over each input
|
||||
for (const auto &input : inputs) {
|
||||
MS_EXCEPTION_IF_NULL(input);
|
||||
|
||||
// Skip if the input is not a Parameter
|
||||
if (!input->isa<Parameter>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create empty vectors to store the formats, types, and sizes of the input tensors
|
||||
std::vector<std::string> formats;
|
||||
std::vector<TypeId> types;
|
||||
std::vector<size_t> tensor_sizes;
|
||||
|
||||
// Get the number of output tensors for the input
|
||||
auto output_size = common::AnfAlgo::GetOutputTensorNum(input);
|
||||
|
||||
// Iterate over each output tensor of the input
|
||||
for (size_t index = 0; index < output_size; index++) {
|
||||
// Get the format, type, and size of the output tensor
|
||||
auto format = AnfAlgo::GetOutputFormat(input, index);
|
||||
auto type_id = AnfAlgo::GetOutputDeviceDataType(input, index);
|
||||
|
||||
// If the type is unknown, get the inferred data type
|
||||
if (type_id == kTypeUnknown) {
|
||||
type_id = common::AnfAlgo::GetOutputInferDataType(input, index);
|
||||
}
|
||||
|
||||
auto tensor_size = AnfAlgo::GetOutputTensorMemSize(input, index);
|
||||
|
||||
// Add the format, type, and size to their respective vectors
|
||||
formats.emplace_back(format);
|
||||
types.emplace_back(type_id);
|
||||
tensor_sizes.emplace_back(tensor_size);
|
||||
}
|
||||
|
||||
// Continue with the rest of the code...
|
||||
}
|
||||
}
|
||||
// Add the type_id to the end of the types vector
|
||||
types.emplace_back(type_id);
|
||||
|
||||
// Add the tensor_size to the end of the tensor_sizes vector
|
||||
tensor_sizes.emplace_back(tensor_size);
|
||||
}
|
||||
|
||||
// Set the user data of the input to a new instance of OpRuntimeInfo
|
||||
input->set_user_data<runtime::OpRuntimeInfo>(std::make_shared<runtime::OpRuntimeInfo>(
|
||||
formats, types, tensor_sizes, nullptr, std::vector<std::pair<device::KernelInfo *, size_t>>()));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Define the output_format function of the OpRuntimeInfo class, which takes an index as input and returns a string
|
||||
std::string OpRuntimeInfo::output_format(size_t index) const {
|
||||
|
||||
// Check if the index is valid, i.e., if it is within the range of the output_format_ vector
|
||||
if (index >= output_format_.size()) {
|
||||
|
||||
// If the index is invalid, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Invalid index:" << index << " total output_format:" << output_format_.size();
|
||||
}
|
||||
|
||||
// If the index is valid, return the string at the corresponding index in the output_format_ vector
|
||||
return output_format_[index];
|
||||
}
|
||||
|
||||
// Return the output type at the specified index in the `output_type_` vector
|
||||
TypeId OpRuntimeInfo::output_type(size_t index) const {
|
||||
|
||||
// Check if the index is out of bounds
|
||||
if (index >= output_type_.size()) {
|
||||
|
||||
// If the index is invalid, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Invalid index:" << index << " total output_type:" << output_type_.size();
|
||||
}
|
||||
|
||||
// Return the output type at the specified index
|
||||
return output_type_[index];
|
||||
}
|
||||
|
||||
// Return the size of the output tensor at the given index in the `output_tensor_size_` vector
|
||||
size_t OpRuntimeInfo::output_tensor_size(size_t index) const {
|
||||
|
||||
// Check if the given index is valid, i.e., within the range of the `output_tensor_size_` vector
|
||||
if (index >= output_tensor_size_.size()) {
|
||||
|
||||
// If the index is invalid, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Invalid index::" << index << " total output_tensor_size:" << output_tensor_size_.size();
|
||||
}
|
||||
|
||||
// Return the size of the output tensor at the given index
|
||||
return output_tensor_size_[index];
|
||||
}
|
||||
|
||||
// Get the device address of the output tensor at the specified index
|
||||
device::DeviceAddressPtr OpRuntimeInfo::GetOutputDeviceAddress(size_t index) const {
|
||||
|
||||
// Check if the kernel info is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(kernel_info_);
|
||||
|
||||
// Return the mutable output address of the kernel info at the specified index
|
||||
return kernel_info_->GetMutableOutputAddr(index);
|
||||
}
|
||||
|
||||
// Get the device address of the workspace for a given index in the OpRuntimeInfo object
|
||||
device::DeviceAddressPtr OpRuntimeInfo::GetWorkspaceDeviceAddress(size_t index) const {
|
||||
|
||||
// Check if the kernel_info_ pointer is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(kernel_info_);
|
||||
|
||||
// Return the mutable workspace address for the given index from the kernel_info_ object
|
||||
return kernel_info_->GetMutableWorkspaceAddr(index);
|
||||
}
|
||||
|
||||
// Get the device address of the input at the specified index
|
||||
device::DeviceAddressPtr OpRuntimeInfo::GetInputDeviceAddress(size_t index) const {
|
||||
|
||||
// Check if the index is out of range
|
||||
if (index >= input_kernel_infos_.size()) {
|
||||
|
||||
// Log an error message indicating the index and the size of the input kernel infos
|
||||
MS_LOG(ERROR) << "Output range! index:" << index << " input size:" << input_kernel_infos_.size();
|
||||
|
||||
// Return a null pointer to indicate failure
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto kernel_info_pair = input_kernel_infos_[index];
|
||||
MS_EXCEPTION_IF_NULL(kernel_info_pair.first);
|
||||
return kernel_info_pair.first->GetMutableOutputAddr(kernel_info_pair.second);
|
||||
// Assign the value of the pair at index 'index' in the 'input_kernel_infos_' vector to the variable 'kernel_info_pair'
|
||||
auto kernel_info_pair = input_kernel_infos_[index];
|
||||
|
||||
// Check if the first element of the pair is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(kernel_info_pair.first);
|
||||
|
||||
// Return the mutable output address of the first element of the pair, using the index stored in the second element of the pair
|
||||
return kernel_info_pair.first->GetMutableOutputAddr(kernel_info_pair.second);
|
||||
|
||||
// Define the member function GetInputSize() of the class OpRuntimeInfo
|
||||
size_t OpRuntimeInfo::GetInputSize() const {
|
||||
|
||||
// Return the size of the input_kernel_infos_ vector
|
||||
return input_kernel_infos_.size();
|
||||
}
|
||||
|
||||
size_t OpRuntimeInfo::GetInputSize() const { return input_kernel_infos_.size(); }
|
||||
|
||||
// Get the size of the output for the OpRuntimeInfo object
|
||||
size_t OpRuntimeInfo::GetOutputSize() const {
|
||||
|
||||
// Check if the kernel_info_ pointer is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(kernel_info_);
|
||||
|
||||
// Return the size of the output address list in the kernel_info_ object
|
||||
return kernel_info_->output_address_list().size();
|
||||
}
|
||||
|
||||
// Get the size of the workspace required by the OpRuntimeInfo object
|
||||
size_t OpRuntimeInfo::GetWorkspaceSize() const {
|
||||
|
||||
// Check if the kernel_info_ pointer is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(kernel_info_);
|
||||
|
||||
// Return the size of the workspace address list stored in the kernel_info_ object
|
||||
return kernel_info_->workspace_address_list().size();
|
||||
}
|
||||
|
||||
// Definition of the function "CacheGraphOpRuntimeInfo" belonging to the "OpRuntimeInfo" class
|
||||
|
||||
void OpRuntimeInfo::CacheGraphOpRuntimeInfo(const KernelGraphPtr &graph) {
|
||||
|
||||
// Call the "CacheForExecutionOrder" function to cache runtime information for the execution order of the graph
|
||||
CacheForExecutionOrder(graph);
|
||||
|
||||
// Call the "CacheForGraphInputs" function to cache runtime information for the inputs of the graph
|
||||
CacheForGraphInputs(graph);
|
||||
}
|
||||
} // namespace mindspore::runtime
|
||||
|
||||
// End of the "mindspore::runtime" namespace
|
||||
|
|
@ -14,276 +14,538 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "runtime/pynative/run_op_helper.h"
|
||||
// Include the header file "runtime/pynative/run_op_helper.h" which contains helper functions for running operations in PyNative mode.
|
||||
|
||||
// Include the necessary headers for string manipulation, vector operations, memory management, and algorithm functions
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
// Include the custom log adapter header file
|
||||
#include "utils/log_adapter.h"
|
||||
|
||||
// Include the custom ANF runtime algorithm header file
|
||||
#include "backend/common/session/anf_runtime_algorithm.h"
|
||||
|
||||
// Include the custom convert utils header file
|
||||
#include "include/common/utils/convert_utils.h"
|
||||
|
||||
// Include the custom MS device shape transfer header file
|
||||
#include "runtime/device/ms_device_shape_transfer.h"
|
||||
|
||||
// Include the custom op runtime info header file
|
||||
#include "runtime/pynative/op_runtime_info.h"
|
||||
|
||||
// Include the custom op executor header file
|
||||
#include "runtime/pynative/op_executor.h"
|
||||
|
||||
// Include the custom actor common header file
|
||||
#include "runtime/graph_scheduler/actor/actor_common.h"
|
||||
|
||||
namespace mindspore::runtime {
|
||||
|
||||
namespace {
|
||||
// 1. Device type is different in heterogeneous scenes.
|
||||
// 2. The device address format is different.
|
||||
|
||||
// Function to update input tensors from device
|
||||
// 1. Device type may be different in heterogeneous scenes.
|
||||
// 2. The device address format may be different.
|
||||
void UpdateInputTensorFromDevice(const std::vector<AnfNodePtr> &input_nodes,
|
||||
const std::vector<tensor::TensorPtr> &input_tensors,
|
||||
const device::DeviceContext *device_context) {
|
||||
// Log a debug message to indicate the start of the function
|
||||
MS_LOG(DEBUG) << "Start";
|
||||
|
||||
// Get the size of the input nodes vector
|
||||
auto input_size = input_nodes.size();
|
||||
|
||||
// Iterate over each input node and its corresponding tensor
|
||||
for (size_t i = 0; i < input_size; ++i) {
|
||||
auto &tensor = input_tensors[i];
|
||||
auto &input_node = input_nodes[i];
|
||||
|
||||
// Check if the tensor is null
|
||||
MS_EXCEPTION_IF_NULL(tensor);
|
||||
auto tensor_address = std::dynamic_pointer_cast<device::DeviceAddress>(tensor->device_address());
|
||||
|
||||
// Get the device address of the tensor
|
||||
auto tensor_address = std::dynamic_pointer_cast<device::DeviceAddress>(tensor->device_address);
|
||||
|
||||
// Get the mutable output address of the input node
|
||||
auto node_address = AnfAlgo::GetMutableOutputAddr(input_node, 0);
|
||||
// node_address can't be null
|
||||
|
||||
// Check if the node address is null
|
||||
MS_EXCEPTION_IF_NULL(node_address);
|
||||
|
||||
// Check if the tensor address is not null
|
||||
if (tensor_address != nullptr) {
|
||||
// Check if the device type or format of the tensor address is different from the device context
|
||||
if (tensor_address->DeviceType() != device_context->GetDeviceAddressType() ||
|
||||
tensor_address->format() != node_address->format()) {
|
||||
// Need wait for OpExecutor task finish
|
||||
tensor->data_sync();
|
||||
// If tensor address is null, we will set Parameter address to the Tensor.
|
||||
tensor->set_device_address(nullptr);
|
||||
// ... perform some action
|
||||
}
|
||||
}
|
||||
}
|
||||
MS_LOG(DEBUG) << "End";
|
||||
}
|
||||
|
||||
void UpdateParameterShapeFromInputTensor(const AnfNodePtr &input_node, const tensor::TensorPtr &input_tensor) {
|
||||
MS_EXCEPTION_IF_NULL(input_node);
|
||||
if (input_tensor == nullptr || !input_node->isa<Parameter>()) {
|
||||
return;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
} // namespace mindspore::runtime
|
||||
// Need to wait for the OpExecutor task to finish before proceeding
|
||||
tensor->data_sync();
|
||||
|
||||
// If the tensor address is null, we will set the Parameter address to the Tensor
|
||||
tensor->set_device_address(nullptr);
|
||||
|
||||
// End of the function
|
||||
MS_LOG(DEBUG) << "End";
|
||||
|
||||
// A function to update the shape of a parameter based on an input tensor
|
||||
|
||||
// Check if the input node is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(input_node);
|
||||
|
||||
// Check if the input tensor is null or if the input node is not of type Parameter
|
||||
// If either condition is true, return from the function
|
||||
if (input_tensor == nullptr || !input_node->isa<Parameter>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cast the input_node to a ParameterPtr and assign it to the variable input_param
|
||||
auto input_param = input_node->cast<ParameterPtr>();
|
||||
|
||||
// Check if input_param is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(input_param);
|
||||
|
||||
// Check if input_param has dynamic shape
|
||||
if (!input_param->has_dynamic_shape()) {
|
||||
// If it does not have dynamic shape, return and exit the function
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the shape of the input tensor
|
||||
auto shape = input_tensor->shape();
|
||||
std::vector<size_t> update_shape;
|
||||
std::transform(shape.begin(), shape.end(), std::back_inserter(update_shape), IntToSize);
|
||||
MS_LOG(DEBUG) << "Update input node shape to:" << update_shape;
|
||||
common::AnfAlgo::SetOutputInferTypeAndShape({common::AnfAlgo::GetOutputInferDataType(input_node, 0)}, {update_shape},
|
||||
input_node.get());
|
||||
}
|
||||
|
||||
// Create an empty vector to store the updated shape
|
||||
std::vector<size_t> update_shape;
|
||||
|
||||
// Use std::transform to iterate over each element in the shape vector and apply the IntToSize function to convert each element to size_t
|
||||
std::transform(shape.begin(), shape.end(), std::back_inserter(update_shape), IntToSize);
|
||||
|
||||
// Print the updated shape to the debug log
|
||||
MS_LOG(DEBUG) << "Update input node shape to:" << update_shape;
|
||||
|
||||
// Set the output inference type and shape for the input node using the updated shape
|
||||
common::AnfAlgo::SetOutputInferTypeAndShape({common::AnfAlgo::GetOutputInferDataType(input_node, 0)}, {update_shape}, input_node.get());
|
||||
|
||||
// This function updates the device address of input nodes with the corresponding device address from input tensors
|
||||
void UpdateInputNodeDeviceAddress(const std::vector<AnfNodePtr> &input_nodes,
|
||||
const std::vector<tensor::TensorPtr> &input_tensors,
|
||||
const device::DeviceContext *device_context) {
|
||||
// Log a debug message to indicate the start of the function
|
||||
MS_LOG(DEBUG) << "Start";
|
||||
|
||||
// Get the size of the input nodes and input tensors
|
||||
auto input_size = input_nodes.size();
|
||||
auto tensor_size = input_tensors.size();
|
||||
|
||||
// Check if the sizes of input nodes and input tensors are equal
|
||||
if (input_size != tensor_size) {
|
||||
// If not equal, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "input node size:" << input_size << " not equal to tensors size:" << tensor_size;
|
||||
}
|
||||
|
||||
// Iterate over each input node and input tensor
|
||||
for (size_t i = 0; i < input_size; ++i) {
|
||||
// Get a reference to the current input node and input tensor
|
||||
auto &input_node = input_nodes[i];
|
||||
auto &input_tensor = input_tensors[i];
|
||||
|
||||
// Check if the input tensor is null
|
||||
MS_EXCEPTION_IF_NULL(input_tensor);
|
||||
auto tensor_address = std::dynamic_pointer_cast<device::DeviceAddress>(input_tensor->device_address());
|
||||
|
||||
// Get the device address of the input tensor
|
||||
auto tensor_address = std::dynamic_pointer_cast<device::DeviceAddress>(input_tensor->device_address);
|
||||
|
||||
// Get the mutable output address of the input node at index 0
|
||||
auto node_address = AnfAlgo::GetMutableOutputAddr(input_node, 0);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
UpdateParameterShapeFromInputTensor(input_node, input_tensor);
|
||||
// Call the function UpdateParameterShapeFromInputTensor with the arguments input_node and input_tensor
|
||||
|
||||
MS_EXCEPTION_IF_NULL(node_address);
|
||||
if (tensor_address == nullptr) {
|
||||
input_tensor->set_device_address(node_address);
|
||||
input_tensor->set_sync_status(kNeedSyncHostToDeviceImmediately);
|
||||
input_tensor->set_lazy_callback([]() { runtime::OpExecutor::GetInstance().Wait(); });
|
||||
node_address->set_from_persistent_mem(input_tensor->is_parameter());
|
||||
node_address->SetNodeIndex(input_node, 0);
|
||||
UpdateRefCount(node_address.get(), true);
|
||||
}
|
||||
// Check if the pointer `node_address` is null, and throw an exception if it is null
|
||||
MS_EXCEPTION_IF_NULL(node_address);
|
||||
|
||||
// The DeviceType and format of DeviceAddress is always the same after UpdateInputTensor
|
||||
// Check if the pointer `tensor_address` is null
|
||||
if (tensor_address == nullptr) {
|
||||
// Set the device address of the input tensor to `node_address`
|
||||
input_tensor->set_device_address(node_address);
|
||||
|
||||
// Set the sync status of the input tensor to `kNeedSyncHostToDeviceImmediately`
|
||||
input_tensor->set_sync_status(kNeedSyncHostToDeviceImmediately);
|
||||
|
||||
// Set a lazy callback function for the input tensor
|
||||
input_tensor->set_lazy_callback([]() { runtime::OpExecutor::GetInstance().Wait(); });
|
||||
|
||||
// Set the `from_persistent_mem` flag of `node_address` based on whether the input tensor is a parameter
|
||||
node_address->set_from_persistent_mem(input_tensor->is_parameter());
|
||||
|
||||
// Set the node index of `node_address` to `input_node` with an index of 0
|
||||
node_address->SetNodeIndex(input_node, 0);
|
||||
|
||||
// Update the reference count of `node_address` by incrementing it
|
||||
UpdateRefCount(node_address.get(), true);
|
||||
}
|
||||
|
||||
// Check if the tensor address is not null and is different from the node address
|
||||
if (tensor_address != nullptr && tensor_address != node_address) {
|
||||
|
||||
// Set the output address of the tensor to the input node
|
||||
AnfAlgo::SetOutputAddr(tensor_address, 0, input_node.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Print "End" for debugging purposes
|
||||
MS_LOG(DEBUG) << "End";
|
||||
}
|
||||
|
||||
// Function to update the reference node's output device address in a given graph
|
||||
void UpdateRefNodeOutputDeviceAddress(const KernelGraphPtr &graph) {
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
|
||||
// Get the reference node map from the graph
|
||||
auto ref_node_map = graph->GetRefMap();
|
||||
|
||||
// Iterate over each entry in the reference node map
|
||||
for (const auto &iter : ref_node_map) {
|
||||
// Get the output pair (reference node and output index)
|
||||
auto &output_pair = iter.first;
|
||||
auto &input_pair = iter.second;
|
||||
auto &ref_node = output_pair.first;
|
||||
auto output_index = output_pair.second;
|
||||
|
||||
// Get the input pair (input node and input node output index)
|
||||
auto &input_pair = iter.second;
|
||||
auto &input_node = input_pair.first;
|
||||
auto input_node_output_index = input_pair.second;
|
||||
|
||||
// Rest of the code is not provided, so we cannot provide further comments
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// Get the mutable output address of the input node using the AnfAlgo::GetMutableOutputAddr function
|
||||
auto input_addr = AnfAlgo::GetMutableOutputAddr(input_node, input_node_output_index, false);
|
||||
|
||||
// Get the mutable output address of the reference node using the AnfAlgo::GetMutableOutputAddr function
|
||||
auto ref_node_output_addr = AnfAlgo::GetMutableOutputAddr(ref_node, output_index, false);
|
||||
|
||||
// Check if the input address is different from the reference node's output address
|
||||
if (input_addr != ref_node_output_addr) {
|
||||
|
||||
// If the addresses are different, set the output address of the input node to the reference node's output address
|
||||
AnfAlgo::SetOutputAddr(input_addr, output_index, ref_node.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to copy tensor data to a device
|
||||
void CopyTensorDataToDevice(const tensor::TensorPtr &tensor, const AnfNodePtr &node,
|
||||
const device::DeviceContext *device_context) {
|
||||
// Check if the tensor and device context are not null
|
||||
MS_EXCEPTION_IF_NULL(tensor);
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
// Get the device address of the tensor
|
||||
auto device_address = std::dynamic_pointer_cast<device::DeviceAddress>(tensor->device_address());
|
||||
MS_EXCEPTION_IF_NULL(device_address);
|
||||
|
||||
// Check if the device address pointer is null and if memory allocation fails
|
||||
if ((device_address->GetPtr() == nullptr) &&
|
||||
(!device_context->AllocateMemory(device_address.get(), device_address->GetSize()))) {
|
||||
// Throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Allocate memory failed";
|
||||
}
|
||||
}
|
||||
|
||||
// Copy data from host tensor to device.
|
||||
|
||||
// Get the size of the tensor in bytes
|
||||
auto tensor_size = LongToSize(tensor->data().nbytes());
|
||||
|
||||
// Get the data type of the tensor
|
||||
auto tensor_type = tensor->data_type();
|
||||
|
||||
// Log a debug message indicating the node being copied to the device
|
||||
MS_LOG(DEBUG) << "Copy to device, node:" << node->DebugString();
|
||||
|
||||
// Synchronize the host tensor data to the device
|
||||
// using the runtime padding shape, tensor size, tensor type,
|
||||
// tensor data pointer, and host format
|
||||
if (!device_address->SyncHostToDevice(trans::GetRuntimePaddingShape(node, 0), tensor_size, tensor_type,
|
||||
tensor->data_c(), tensor->device_info().host_format_)) {
|
||||
// If the synchronization fails, throw an exception
|
||||
MS_LOG(EXCEPTION) << "SyncHostToDevice failed";
|
||||
}
|
||||
}
|
||||
|
||||
void CopyValueNodeTensorToDevice(const ValueNodePtr &node, const device::DeviceContext *device_context) {
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
// Function to copy the value of a node's tensor to a device using the provided device context
|
||||
|
||||
auto &node_value = node->value();
|
||||
MS_EXCEPTION_IF_NULL(node_value);
|
||||
// Check if the node pointer is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
std::vector<tensor::TensorPtr> tensors;
|
||||
TensorValueToTensor(node_value, &tensors);
|
||||
for (size_t i = 0; i < tensors.size(); i++) {
|
||||
// Check if the device context pointer is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
// Create a reference variable named "node_value" that refers to the value of the "node" object
|
||||
auto &node_value = node->value();
|
||||
|
||||
// Check if the "node_value" is null, and if it is, throw an exception
|
||||
MS_EXCEPTION_IF_NULL(node_value);
|
||||
|
||||
// Declare a vector of tensor pointers named "tensors"
|
||||
std::vector<tensor::TensorPtr> tensors;
|
||||
|
||||
// Convert the tensor values in "node_value" to actual tensors and store them in the "tensors" vector
|
||||
TensorValueToTensor(node_value, &tensors);
|
||||
|
||||
// Iterate over the elements in the "tensors" vector using a for loop
|
||||
for (size_t i = 0; i < tensors.size(); i++) {
|
||||
|
||||
// Get a reference to the current tensor in the "tensors" vector
|
||||
const auto &tensor = tensors[i];
|
||||
MS_EXCEPTION_IF_NULL(tensor);
|
||||
|
||||
// Check if the tensor is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(tensor);
|
||||
}
|
||||
|
||||
// Get the mutable output address of the node at index i using AnfAlgo::GetMutableOutputAddr
|
||||
const auto &node_address = AnfAlgo::GetMutableOutputAddr(node, i, false);
|
||||
|
||||
// Check if the node address is null
|
||||
MS_EXCEPTION_IF_NULL(node_address);
|
||||
|
||||
// If the pointer of the node address is not null, return
|
||||
if (node_address->GetPtr() != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the device address of the tensor to the node address
|
||||
tensor->set_device_address(node_address);
|
||||
|
||||
// Update the reference count of the node address
|
||||
UpdateRefCount(node_address.get(), true);
|
||||
|
||||
// Copy the tensor data to the device using CopyTensorDataToDevice
|
||||
CopyTensorDataToDevice(tensor, node, device_context);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to copy the value node string to the device
|
||||
void CopyValueNodeStringToDevice(const ValueNodePtr &node, const device::DeviceContext *device_context) {
|
||||
// Check if the value node pointer is null
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
// Check if the device context pointer is null
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
// Get the mutable output address of the node at index 0
|
||||
const auto &node_address = AnfAlgo::GetMutableOutputAddr(node, 0, false);
|
||||
|
||||
// Check if the node address is null
|
||||
MS_EXCEPTION_IF_NULL(node_address);
|
||||
|
||||
// Check if the pointer in the node address is not null
|
||||
if (node_address->GetPtr() != nullptr) {
|
||||
// If the pointer is not null, return from the function
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!device_context->AllocateMemory(node_address.get(), node_address->GetSize())) {
|
||||
// Check if memory allocation for the device context is successful
|
||||
if (!device_context->AllocateMemory(node_address.get(), node_address->GetSize())) {
|
||||
|
||||
// If memory allocation fails, throw an exception with the error message "Allocate memory failed"
|
||||
MS_LOG(EXCEPTION) << "Allocate memory failed";
|
||||
}
|
||||
}
|
||||
|
||||
// Create a reference to the value of the node
|
||||
auto &node_value = node->value();
|
||||
|
||||
// Check if the node value is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(node_value);
|
||||
// Copy data to device.
|
||||
|
||||
// Get the value of the node as a string
|
||||
auto value = GetValue<std::string>(node_value);
|
||||
|
||||
// Get the size of the string value
|
||||
size_t tensor_size = value.size();
|
||||
|
||||
// Create a shape vector with dimensions {1, tensor_size}
|
||||
ShapeVector shape = {1, SizeToLong(tensor_size)};
|
||||
|
||||
// Copy the data from the host to the device
|
||||
// using the SyncHostToDevice function of the node_address object
|
||||
// If the copy fails, throw an exception
|
||||
if (!node_address->SyncHostToDevice(shape, tensor_size, kNumberTypeUInt8, value.data())) {
|
||||
MS_LOG(EXCEPTION) << "SyncHostToDevice failed";
|
||||
}
|
||||
}
|
||||
|
||||
// Function to copy value node data to the device
|
||||
void CopyValueNodeDataToDevice(const KernelGraphPtr &graph, const device::DeviceContext *device_context) {
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
|
||||
// Log a debug message indicating the start of the function
|
||||
MS_LOG(DEBUG) << "Start";
|
||||
|
||||
// Get the value nodes from the graph
|
||||
const auto &value_nodes = graph->graph_value_nodes();
|
||||
|
||||
// Iterate over each value node
|
||||
for (const auto &value_node : value_nodes) {
|
||||
MS_EXCEPTION_IF_NULL(value_node);
|
||||
|
||||
// Get the value of the value node
|
||||
auto &node_value = value_node->value();
|
||||
MS_EXCEPTION_IF_NULL(node_value);
|
||||
|
||||
// Check the type of the value node
|
||||
if (node_value->isa<tensor::Tensor>() || node_value->isa<ValueTuple>()) {
|
||||
// If the value node is a tensor or a value tuple, copy it to the device
|
||||
CopyValueNodeTensorToDevice(value_node, device_context);
|
||||
} else if (node_value->isa<StringImm>()) {
|
||||
// If the value node is a string, copy it to the device
|
||||
CopyValueNodeStringToDevice(value_node, device_context);
|
||||
} else {
|
||||
// If the value node is of an unknown type, log a warning message
|
||||
MS_LOG(WARNING) << "Unknown value node type:" << value_node->DebugString();
|
||||
}
|
||||
}
|
||||
|
||||
// Log a debug message indicating the end of the function
|
||||
MS_LOG(DEBUG) << "End";
|
||||
}
|
||||
|
||||
// Function to copy parameter data to a device
|
||||
void CopyParameterDataToDevice(const std::vector<AnfNodePtr> &input_nodes,
|
||||
const std::vector<tensor::TensorPtr> &input_tensors,
|
||||
const device::DeviceContext *device_context) {
|
||||
// Log a debug message indicating the start of the function
|
||||
MS_LOG(DEBUG) << "Start";
|
||||
|
||||
// Get the size of the input nodes vector
|
||||
auto input_size = input_nodes.size();
|
||||
|
||||
// Iterate over each input node
|
||||
for (size_t i = 0; i < input_size; ++i) {
|
||||
// Check if the input tensor is null
|
||||
MS_EXCEPTION_IF_NULL(input_tensors[i]);
|
||||
|
||||
// Check if the input tensor needs to be synchronized from host to device immediately
|
||||
if (input_tensors[i]->NeedSyncHostToDeviceImmediately()) {
|
||||
// Copy the tensor data to the device
|
||||
CopyTensorDataToDevice(input_tensors[i], input_nodes[i], device_context);
|
||||
|
||||
// Set the sync status of the input tensor to indicate that synchronization is not needed
|
||||
input_tensors[i]->set_sync_status(kNoNeedSync);
|
||||
}
|
||||
}
|
||||
|
||||
// Log a debug message indicating the end of the function
|
||||
MS_LOG(DEBUG) << "End";
|
||||
}
|
||||
|
||||
// This function updates the size of the output device addresses for a given node in the computation graph.
|
||||
// It takes in two parameters:
|
||||
// - node: a reference to the AnfNodePtr object representing the node in the computation graph
|
||||
// - runtime_info: a shared pointer to the OpRuntimeInfo object containing runtime information for the node
|
||||
|
||||
void UpdateOutputAddrSize(const AnfNodePtr &node, const std::shared_ptr<OpRuntimeInfo> &runtime_info) {
|
||||
// Check if the runtime_info pointer is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(runtime_info);
|
||||
|
||||
// Get the number of output tensors for the node from the runtime_info object
|
||||
auto output_size = runtime_info->GetOutputSize();
|
||||
|
||||
// Iterate over each output tensor
|
||||
for (size_t i = 0; i < output_size; ++i) {
|
||||
// Get the device address of the output tensor from the runtime_info object
|
||||
auto output_address = runtime_info->GetOutputDeviceAddress(i);
|
||||
|
||||
// Check if the output_address pointer is null, and throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(output_address);
|
||||
|
||||
// Get the memory size of the output tensor from the AnfAlgo utility function
|
||||
auto output_addr_size = AnfAlgo::GetOutputTensorMemSize(node, i);
|
||||
|
||||
// Check if the output_addr_size is different from the current size of the output_address
|
||||
if (output_addr_size != output_address->GetSize()) {
|
||||
// If the sizes are different, update the size of the output_address to match the output_addr_size
|
||||
output_address->SetSize(output_addr_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to allocate memory for kernel input
|
||||
bool MallocForKernelInput(const std::shared_ptr<OpRuntimeInfo> &runtime_info,
|
||||
const device::DeviceContext *device_context) {
|
||||
// Check if runtime_info and device_context are not null
|
||||
MS_EXCEPTION_IF_NULL(runtime_info);
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
// Get the size of the input
|
||||
auto input_size = runtime_info->GetInputSize();
|
||||
|
||||
// Iterate over each input
|
||||
for (size_t i = 0; i < input_size; ++i) {
|
||||
// Get the device address of the input
|
||||
auto input_address = runtime_info->GetInputDeviceAddress(i);
|
||||
MS_EXCEPTION_IF_NULL(input_address);
|
||||
|
||||
// Check if the input address is null and allocate memory if needed
|
||||
if (input_address->GetPtr() == nullptr &&
|
||||
!device_context->AllocateMemory(input_address.get(), input_address->GetSize())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true to indicate successful memory allocation for all inputs
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to allocate memory for kernel output
|
||||
bool MallocForKernelOutput(const std::shared_ptr<OpRuntimeInfo> &runtime_info, const AnfNodePtr &node,
|
||||
const device::DeviceContext *device_context) {
|
||||
// Check if the runtime info is null
|
||||
MS_EXCEPTION_IF_NULL(runtime_info);
|
||||
|
||||
// Check if the node is null
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
// Check if the device context is null
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
auto kernel_mod = AnfAlgo::GetKernelMod(node);
|
||||
MS_EXCEPTION_IF_NULL(kernel_mod);
|
||||
auto output_size = runtime_info->GetOutputSize();
|
||||
auto kernel_out_size_list = kernel_mod->GetOutputSizeList();
|
||||
auto kernel_mod = AnfAlgo::GetKernelMod(node); // Get the kernel module associated with the node
|
||||
MS_EXCEPTION_IF_NULL(kernel_mod); // Throw an exception if the kernel module is null
|
||||
|
||||
auto output_size = runtime_info->GetOutputSize(); // Get the number of outputs of the node
|
||||
auto kernel_out_size_list = kernel_mod->GetOutputSizeList(); // Get the list of output sizes from the kernel module
|
||||
|
||||
// Check if the number of outputs from the kernel module matches the expected output size
|
||||
if (kernel_out_size_list.size() != output_size) {
|
||||
MS_LOG(ERROR) << "Node " << node->fullname_with_scope() << " output num is:" << output_size
|
||||
<< " but kernel_mod output num:" << kernel_out_size_list.size();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate over each output of the node
|
||||
for (size_t i = 0; i < output_size; ++i) {
|
||||
auto device_address = runtime_info->GetOutputDeviceAddress(i);
|
||||
MS_EXCEPTION_IF_NULL(device_address);
|
||||
// For example, we need to call cudnnGetRNNTrainingReserveSize to get real output size in LstmGpuKernelMod!
|
||||
auto device_address = runtime_info->GetOutputDeviceAddress(i); // Get the device address of the output
|
||||
MS_EXCEPTION_IF_NULL(device_address); // Throw an exception if the device address is null
|
||||
|
||||
// Check if the output size from the kernel module matches the device address size
|
||||
if (kernel_out_size_list[i] != device_address->GetSize()) {
|
||||
// If the format of the DeviceAddress is different, then the size is originally different.
|
||||
// Such as NCHW(1,1,1,3) and NC1HWC0(1,1,1,1,16). So we don't need to update the size.
|
||||
|
|
@ -291,7 +553,7 @@ bool MallocForKernelOutput(const std::shared_ptr<OpRuntimeInfo> &runtime_info, c
|
|||
if (device_address->GetPtr() != nullptr) {
|
||||
MS_LOG(ERROR) << "kernel mod output " << i << " size:" << kernel_out_size_list[i]
|
||||
<< " not equal to device_address size:" << device_address->GetSize()
|
||||
<< ", but the device address is already have ptr";
|
||||
<< ", but the device address already has a pointer";
|
||||
return false;
|
||||
}
|
||||
device_address->SetSize(kernel_out_size_list[i]);
|
||||
|
|
@ -306,154 +568,294 @@ bool MallocForKernelOutput(const std::shared_ptr<OpRuntimeInfo> &runtime_info, c
|
|||
return true;
|
||||
}
|
||||
|
||||
// Function to allocate memory for kernel workspace
|
||||
bool MallocForKernelWorkspace(const std::shared_ptr<OpRuntimeInfo> &runtime_info,
|
||||
const device::DeviceContext *device_context) {
|
||||
// Check if runtime_info and device_context are not null
|
||||
MS_EXCEPTION_IF_NULL(runtime_info);
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
// Get the size of the workspace from runtime_info
|
||||
auto workspace_size = runtime_info->GetWorkspaceSize();
|
||||
|
||||
// Loop through each workspace element
|
||||
for (size_t i = 0; i < workspace_size; ++i) {
|
||||
// Get the device address for the workspace element
|
||||
auto device_address = runtime_info->GetWorkspaceDeviceAddress(i);
|
||||
MS_EXCEPTION_IF_NULL(device_address);
|
||||
|
||||
// Check if the device address pointer is null and if memory allocation fails
|
||||
if (device_address->GetPtr() == nullptr &&
|
||||
!device_context->AllocateMemory(device_address.get(), device_address->GetSize())) {
|
||||
// Log an error message and return false if memory allocation fails
|
||||
MS_LOG(ERROR) << "Allocate workspace memory failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true to indicate successful memory allocation for all workspace elements
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to create a list of kernel input addresses based on the given runtime information
|
||||
kernel::AddressPtrList CreateKernelInputAddress(const std::shared_ptr<OpRuntimeInfo> &runtime_info) {
|
||||
// Check if the runtime_info pointer is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(runtime_info);
|
||||
|
||||
// Get the size of the input from the runtime_info
|
||||
auto input_size = runtime_info->GetInputSize();
|
||||
|
||||
// Create an empty list to store the kernel input addresses
|
||||
kernel::AddressPtrList inputs;
|
||||
|
||||
// Iterate over the input size
|
||||
for (size_t i = 0; i < input_size; ++i) {
|
||||
// Get the device address for the input at index i from the runtime_info
|
||||
auto device_address = runtime_info->GetInputDeviceAddress(i);
|
||||
|
||||
// Throw an exception if the device_address pointer is null
|
||||
MS_EXCEPTION_IF_NULL(device_address);
|
||||
|
||||
// Create a new kernel::Address object using the device address's mutable pointer and size
|
||||
inputs.emplace_back(std::make_shared<kernel::Address>(device_address->GetMutablePtr(), device_address->GetSize()));
|
||||
|
||||
// Log the address and size of the current input
|
||||
MS_LOG(DEBUG) << "input[" << i << "]:" << inputs.back()->addr << " size:" << inputs.back()->size;
|
||||
}
|
||||
|
||||
// Return the list of kernel input addresses
|
||||
return inputs;
|
||||
}
|
||||
|
||||
// Function to create a list of kernel workspace addresses
|
||||
kernel::AddressPtrList CreateKernelWorkspaceAddress(const std::shared_ptr<OpRuntimeInfo> &runtime_info) {
|
||||
// Check if the runtime_info pointer is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(runtime_info);
|
||||
|
||||
// Get the size of the workspace from the runtime_info object
|
||||
auto workspace_size = runtime_info->GetWorkspaceSize();
|
||||
|
||||
// Create an empty list to store the workspace addresses
|
||||
kernel::AddressPtrList workspaces;
|
||||
|
||||
// Iterate over the workspace size
|
||||
for (size_t i = 0; i < workspace_size; ++i) {
|
||||
// Get the device address for the workspace at index i
|
||||
auto device_address = runtime_info->GetWorkspaceDeviceAddress(i);
|
||||
|
||||
// Throw an exception if the device address is null
|
||||
MS_EXCEPTION_IF_NULL(device_address);
|
||||
|
||||
// Create a new kernel::Address object using the device address's mutable pointer and size
|
||||
workspaces.emplace_back(
|
||||
std::make_shared<kernel::Address>(device_address->GetMutablePtr(), device_address->GetSize()));
|
||||
|
||||
// Log the workspace address and size for debugging purposes
|
||||
MS_LOG(DEBUG) << "workspace[" << i << "]:" << workspaces.back()->addr << " size:" << workspaces.back()->size;
|
||||
}
|
||||
|
||||
// Return the list of workspace addresses
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
// Function to create a list of kernel output addresses based on the provided runtime information
|
||||
kernel::AddressPtrList CreateKernelOutputAddress(const std::shared_ptr<OpRuntimeInfo> &runtime_info) {
|
||||
|
||||
// Get the size of the output from the runtime information
|
||||
auto output_size = runtime_info->GetOutputSize();
|
||||
|
||||
// Create an empty list to store the output addresses
|
||||
kernel::AddressPtrList outputs;
|
||||
|
||||
// Iterate over each output
|
||||
for (size_t i = 0; i < output_size; ++i) {
|
||||
|
||||
// Get the device address for the current output
|
||||
auto device_address = runtime_info->GetOutputDeviceAddress(i);
|
||||
|
||||
// Create a new kernel address using the device address's mutable pointer and size
|
||||
outputs.emplace_back(std::make_shared<kernel::Address>(device_address->GetMutablePtr(), device_address->GetSize()));
|
||||
|
||||
// Log the address and size of the current output
|
||||
MS_LOG(DEBUG) << "output[" << i << "]:" << outputs.back()->addr << " size:" << outputs.back()->size;
|
||||
}
|
||||
|
||||
// Return the list of output addresses
|
||||
return outputs;
|
||||
}
|
||||
|
||||
// Host to Device or Device to Host
|
||||
// Function to copy data from host to device or device to host
|
||||
void CopyDataToDevice(const KernelGraphPtr &graph, const std::vector<tensor::TensorPtr> &input_tensors,
|
||||
const device::DeviceContext *device_context) {
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
|
||||
// Copy data of value nodes from host to device
|
||||
CopyValueNodeDataToDevice(graph, device_context);
|
||||
|
||||
// Copy data of input parameters from host to device
|
||||
CopyParameterDataToDevice(graph->input_nodes(), input_tensors, device_context);
|
||||
}
|
||||
|
||||
// kernel_mode launch
|
||||
// Function to launch kernels in kernel mode
|
||||
void LaunchKernels(const KernelGraphPtr &graph, const device::DeviceContext *device_context) {
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
MS_EXCEPTION_IF_NULL(device_context);
|
||||
|
||||
// Log a debug message indicating the start of kernel launch
|
||||
MS_LOG(DEBUG) << "Start";
|
||||
}
|
||||
|
||||
// Get device address from OpRuntimeInfo
|
||||
const auto &execution_order = graph->execution_order();
|
||||
for (auto const &node : execution_order) {
|
||||
// Get the execution order of nodes in the graph
|
||||
const auto &execution_order = graph->execution_order();
|
||||
|
||||
// Iterate over each node in the execution order
|
||||
for (auto const &node : execution_order) {
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
// Check if the node has dynamic shape
|
||||
auto is_dynamic_shape = common::AnfAlgo::IsDynamicShape(node);
|
||||
|
||||
// Get the runtime information associated with the node
|
||||
auto runtime_info = node->user_data<runtime::OpRuntimeInfo>();
|
||||
|
||||
// Throw an exception if the runtime information is null
|
||||
MS_EXCEPTION_IF_NULL(runtime_info);
|
||||
|
||||
// Check if memory allocation for kernel input failed
|
||||
if (!MallocForKernelInput(runtime_info, device_context)) {
|
||||
MS_LOG(EXCEPTION) << "Malloc for kernel input failed, Memory isn't enough, node:" << node->fullname_with_scope();
|
||||
// If allocation failed, throw an exception with an error message including the name of the node
|
||||
MS_LOG(EXCEPTION) << "Malloc for kernel input failed, Memory isn't enough, node:" << node->fullname_with_scope();
|
||||
}
|
||||
|
||||
// Create kernel input addresses
|
||||
auto inputs = CreateKernelInputAddress(runtime_info);
|
||||
|
||||
// Check if the shape is dynamic
|
||||
if (is_dynamic_shape) {
|
||||
|
||||
// If the shape is dynamic, call the UpdateDynamicShape function of the device context
|
||||
device_context->UpdateDynamicShape(node);
|
||||
}
|
||||
|
||||
// Check if memory allocation for kernel workspace was successful
|
||||
if (!MallocForKernelWorkspace(runtime_info, device_context)) {
|
||||
MS_LOG(EXCEPTION) << "Malloc for kernel workspace failed, Memory isn't enough, node:"
|
||||
<< node->fullname_with_scope();
|
||||
// If memory allocation failed, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Malloc for kernel workspace failed, Memory isn't enough, node:"
|
||||
<< node->fullname_with_scope();
|
||||
}
|
||||
|
||||
// Create kernel workspace addresses
|
||||
auto workspaces = CreateKernelWorkspaceAddress(runtime_info);
|
||||
|
||||
// Check if memory allocation for kernel output failed
|
||||
if (!MallocForKernelOutput(runtime_info, node, device_context)) {
|
||||
MS_LOG(EXCEPTION) << "Malloc for kernel output failed, Memory isn't enough, node:" << node->fullname_with_scope();
|
||||
}
|
||||
auto outputs = CreateKernelOutputAddress(runtime_info);
|
||||
if (!device_context->LaunchKernel(node, inputs, workspaces, outputs, is_dynamic_shape)) {
|
||||
MS_LOG(EXCEPTION) << "Launch kernel failed, name:" << node->fullname_with_scope();
|
||||
// If allocation failed, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Malloc for kernel output failed, Memory isn't enough, node:" << node->fullname_with_scope();
|
||||
}
|
||||
|
||||
if (is_dynamic_shape) {
|
||||
UpdateOutputAddrSize(node, runtime_info);
|
||||
// Create kernel output addresses
|
||||
auto outputs = CreateKernelOutputAddress(runtime_info);
|
||||
|
||||
// Launch the kernel with the given inputs, workspaces, outputs, and dynamic shape flag
|
||||
if (!device_context->LaunchKernel(node, inputs, workspaces, outputs, is_dynamic_shape)) {
|
||||
// If kernel launch failed, throw an exception with an error message
|
||||
MS_LOG(EXCEPTION) << "Launch kernel failed, name:" << node->fullname_with_scope();
|
||||
}
|
||||
}
|
||||
MS_LOG(DEBUG) << "End";
|
||||
|
||||
// Check if the shape is dynamic
|
||||
if (is_dynamic_shape) {
|
||||
// If the shape is dynamic, update the output address size
|
||||
UpdateOutputAddrSize(node, runtime_info);
|
||||
}
|
||||
|
||||
// Log a debug message indicating the end of the function
|
||||
MS_LOG(DEBUG) << "End";
|
||||
}
|
||||
|
||||
// A function to wait for the completion of communication for a vector of input tensors
|
||||
void WaitCommunicationFinish(const std::vector<tensor::TensorPtr> &input_tensors) {
|
||||
|
||||
// Iterate over each input tensor in the vector
|
||||
for (auto &input_tensor : input_tensors) {
|
||||
|
||||
// Check if the input tensor is null
|
||||
MS_EXCEPTION_IF_NULL(input_tensor);
|
||||
|
||||
// Check if the input tensor needs to wait for the device
|
||||
if (input_tensor->NeedWaitDevice()) {
|
||||
|
||||
// Wait for the device associated with the input tensor to finish communication
|
||||
input_tensor->WaitDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A function to release kernel resources for a given KernelGraphPtr
|
||||
void ReleaseKernelResource(const KernelGraphPtr &graph) {
|
||||
// Check if the graph pointer is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
|
||||
// Get the execution order of the kernels in the graph
|
||||
const auto &kernels = graph->execution_order();
|
||||
|
||||
// Iterate over each kernel in the execution order
|
||||
for (const auto &kernel : kernels) {
|
||||
// Check if the kernel pointer is null, throw an exception if it is
|
||||
MS_EXCEPTION_IF_NULL(kernel);
|
||||
|
||||
// Check if the name of the kernel is in the OpCacheBlackList
|
||||
if (kOpCacheBlackList.find(common::AnfAlgo::GetCNodeName(kernel)) != kOpCacheBlackList.end()) {
|
||||
// Get the kernel module associated with the kernel
|
||||
auto kernel_mod = AnfAlgo::GetKernelMod(kernel);
|
||||
|
||||
// If the kernel module exists, release its resources
|
||||
if (kernel_mod) {
|
||||
kernel_mod->ReleaseResource();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Determine the address of the graph and do not change the address in subsequent executions
|
||||
// End of the namespace
|
||||
|
||||
// Function to update the device address of a given graph and its tensors
|
||||
void UpdateDeviceAddress(const KernelGraphPtr &graph, const std::vector<tensor::TensorPtr> &tensors_without_value_mask,
|
||||
const device::DeviceContext *device_context) {
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
|
||||
// Log a debug message indicating the start of the function
|
||||
MS_LOG(DEBUG) << "Start";
|
||||
|
||||
// Get the input nodes of the graph
|
||||
const auto &input_nodes = graph->input_nodes();
|
||||
|
||||
// Update the device address of the input tensors from the device
|
||||
UpdateInputTensorFromDevice(input_nodes, tensors_without_value_mask, device_context);
|
||||
|
||||
// Update the device address of the input nodes
|
||||
UpdateInputNodeDeviceAddress(input_nodes, tensors_without_value_mask, device_context);
|
||||
|
||||
// Update the device address of the output tensors of reference nodes
|
||||
UpdateRefNodeOutputDeviceAddress(graph);
|
||||
|
||||
// Log a debug message indicating the end of the function
|
||||
MS_LOG(DEBUG) << "End";
|
||||
}
|
||||
|
||||
// A function to run a single operation graph
|
||||
void RunSingleOpGraph(const KernelGraphPtr &graph, const std::vector<tensor::TensorPtr> &input_tensors,
|
||||
const device::DeviceContext *device_context) {
|
||||
WaitCommunicationFinish(input_tensors);
|
||||
CopyDataToDevice(graph, input_tensors, device_context);
|
||||
LaunchKernels(graph, device_context);
|
||||
ReleaseKernelResource(graph);
|
||||
// Wait for any ongoing communication to finish before proceeding
|
||||
WaitCommunicationFinish(input_tensors);
|
||||
|
||||
// Copy the input data to the device
|
||||
CopyDataToDevice(graph, input_tensors, device_context);
|
||||
|
||||
// Launch the kernels to perform the computation
|
||||
LaunchKernels(graph, device_context);
|
||||
|
||||
// Release the resources used by the kernels
|
||||
ReleaseKernelResource(graph);
|
||||
}
|
||||
} // namespace mindspore::runtime
|
||||
|
||||
// End of the namespace mindspore::runtime
|
||||
Loading…
Reference in New Issue