加入注释 #18

Open
Col_lin wants to merge 2 commits from Col_lin/mindspore2022:master into master
10 changed files with 3581 additions and 737 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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";
}
}

View File

@ -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

View File

@ -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