-
```C++ // 导入系统头文件 #include #include // 导入.h头文件 #include "minddata/dataset/core/tensor_helpers.h" // 双重命名空间 namespace mindspore { namespace dataset { void IndexGeneratorHelper(int8_t depth, std::vector *numbers, const std::vector &slice_list, std::vector> *matrix) { // 如果它是索引而不是切片对象,则 for 循环会发生变化 if (depth > 0) { // size_t是为了增强程序的可移植性 dsize_t new_depth = depth - 1; dsize_t curr_ind = numbers->size() - depth; if (slice_list[curr_ind].slice_.valid()) { dsize_t increment = slice_list[curr_ind].slice_.step_; if (increment > 0) { // 遍历循环 for (int i = slice_list[curr_ind].slice_.start_; i slice_list[curr_ind].slice_.stop_; i = i + slice_list[curr_ind].slice_.step_) { (*numbers)[curr_ind] = i; IndexGeneratorHelper(new_depth, numbers, slice_list, matrix); } } else { // 遍历循环 for (int i = slice_list[curr_ind].slice_.start_; i > slice_list[curr_ind].slice_.stop_; i = i + slice_list[curr_ind].slice_.step_) { (*numbers)[curr_ind] = i; IndexGeneratorHelper(new_depth, numbers, slice_list, matrix); } } } else { // 遍历循环 for (int i = 0; i slice_list[curr_ind].indices_.size(); i++) { (*numbers)[curr_ind] = slice_list[curr_ind].indices_[i]; IndexGeneratorHelper(new_depth, numbers, slice_list, matrix); } } } else { // 尾部插入 (*matrix).emplace_back((*numbers)); } } // 用于生成切片索引 std::vector> IndexGenerator(const std::vector &slice_list) { // int8_t : typedef signed char; int8_t depth = slice_list.size(); // vector是向量类型,可以容纳许多类型的数据,因此也被称为容器 // C++ vector类为内置数组提供了一种替代表示 // 与string类一样 vector 类是随标准 C++引入的标准库的一部分,使用时需包含头文件 #include std::vector numbers(depth, 0); std::vector> matrix(0, std::vector(depth, 0)); IndexGeneratorHelper(depth, &numbers, slice_list, &matrix); return matrix; } } // 命名空间 dataset } // 命名空间 mindspore ```
-
```C++ // 导入系统自带的.h头文件 #include "minddata/dataset/core/global_context.h" #include "minddata/dataset/core/device_tensor.h" // 判断宏ENABLE_ACL是否被定义,若已定义,执行随后的语句 #ifdef ENABLE_ACL #include "minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h" #endif #include "minddata/dataset/util/status.h" // 双重命名空间 namespace mindspore { namespace dataset { DeviceTensor::DeviceTensor(const TensorShape &shape, const DataType &type) : Tensor(shape, type) { // 从全局上下文中获取内存池并为字符数据区创建分配器 /* shared_ptr在boost中地位相当重要,其行为接近原始指针,但又比指针更加安全,甚至还能提供基本的线程安全保证。 它基本上解决了在使用c++开发过程中不可避免的使用指针而遇到的许多问题 常见的毫无疑问是内存泄漏和内存的提前释放,还有一些关于指针内存申请而产生的异常问题等 */ std::shared_ptr global_pool = GlobalContext::Instance()->mem_pool(); data_allocator_ = std::make_unique>(global_pool); device_data_type_ = type; // 赋值操作 host_data_tensor_ = nullptr; } Status DeviceTensor::CreateEmpty(const TensorShape &shape, const DataType &type, std::shared_ptr *out) { // 判断是否形状无效 CHECK_FAIL_RETURN_UNEXPECTED(shape.known(), "Invalid shape."); // 判断是否是无效的数据类型 CHECK_FAIL_RETURN_UNEXPECTED(type != DataType::DE_UNKNOWN, "Invalid data type."); const DeviceTensorAlloc *alloc = GlobalContext::Instance()->device_tensor_allocator(); // 常量指针 *out = std::allocate_shared(*alloc, shape, type); // 如果它是一个字符串张量并且它没有元素,只需初始化形状和类型 if (!type.IsNumeric() && shape.NumOfElements() == 0) { return Status::OK(); // 返回是对是错 } // 元素数不为 0。类型应为数字 CHECK_FAIL_RETURN_UNEXPECTED(type.IsNumeric(), "Number of elements is not 0. The type should be numeric."); int64_t byte_size = (*out)->SizeInBytes(); // int64_t : typedef signed long long // 如果我们有一个没有元素的张量,就不要分配 if (byte_size != 0) { RETURN_IF_NOT_OK((*out)->AllocateBuffer(byte_size)); } return Status::OK(); } Status DeviceTensor::CreateFromDeviceMemory(const TensorShape &shape, const DataType &type, uint8_t *data_ptr, const uint32_t &dataSize, const std::vector &attributes, std::shared_ptr *out) { CHECK_FAIL_RETURN_UNEXPECTED(shape.known(), "Invalid shape."); // 形状无效 CHECK_FAIL_RETURN_UNEXPECTED(type != DataType::DE_UNKNOWN, "Invalid data type."); // 无效的数据类型 CHECK_FAIL_RETURN_UNEXPECTED(data_ptr != nullptr, "Data pointer is NULL"); // 数据指针为NULL CHECK_FAIL_RETURN_UNEXPECTED(dataSize > 0, "Invalid data size"); // 无效的数据大小 const DeviceTensorAlloc *alloc = GlobalContext::Instance()->device_tensor_allocator(); // 常量指针 *out = std::allocate_shared(*alloc, shape, type); // 如果它是一个字符串张量并且它没有元素,只需初始化形状和类型。 if (!type.IsNumeric() && shape.NumOfElements() == 0) { return Status::OK(); } // 元素数不为 0。类型应为数字。 CHECK_FAIL_RETURN_UNEXPECTED(type.IsNumeric(), "Number of elements is not 0. The type should be numeric."); int64_t byte_size = (*out)->SizeInBytes(); // int64_t : typedef signed long long // 如果我们有一个没有元素的张量,就不要分配。 if (byte_size != 0) { RETURN_IF_NOT_OK((*out)->AllocateBuffer(byte_size)); } CHECK_FAIL_RETURN_UNEXPECTED( // 无法为 DeviceTensor 设置属性” (*out)->SetAttributes(data_ptr, dataSize, attributes[0], attributes[1], attributes[2], attributes[3]), "Fail to set attributes for DeviceTensor"); return Status::OK(); } const unsigned char *DeviceTensor::GetHostBuffer() { #ifdef ENABLE_ACL Status rc = DataPop_(&host_data_tensor_); if (!rc.IsOk()) { // 将设备数据弹出到主机失败,将返回一个 nullptr MS_LOG(ERROR) "Pop device data onto host fail, a nullptr will be returned"; return nullptr; // 返回空 } #endif if (!host_data_tensor_) { return nullptr; } return host_data_tensor_->GetBuffer(); } // 实例化函数 const uint8_t *DeviceTensor::GetDeviceBuffer() { return device_data_; } uint8_t *DeviceTensor::GetDeviceMutableBuffer() { return device_data_; } DataType DeviceTensor::DeviceDataType() const { return device_data_type_; } uint32_t DeviceTensor::DeviceDataSize() { return size_; } Status DeviceTensor::SetYuvStrideShape_(const uint32_t &width, const uint32_t &widthStride, const uint32_t &height, const uint32_t &heightStride) { YUV_shape_ = {width, widthStride, height, heightStride}; return Status::OK(); } std::vector DeviceTensor::GetYuvStrideShape() { return YUV_shape_; } Status DeviceTensor::SetAttributes(uint8_t *data_ptr, const uint32_t &dataSize, const uint32_t &width, const uint32_t &widthStride, const uint32_t &height, const uint32_t &heightStride) { device_data_ = data_ptr; // 获取设备数据失败 CHECK_FAIL_RETURN_UNEXPECTED(device_data_ != nullptr, "Fail to get the device data."); // 判断结果是否ok RETURN_IF_NOT_OK(SetSize_(dataSize)); RETURN_IF_NOT_OK(SetYuvStrideShape_(width, widthStride, height, heightStride)); return Status::OK(); } Status DeviceTensor::SetSize_(const uint32_t &new_size) { size_ = new_size; return Status::OK(); } #ifdef ENABLE_ACL Status DeviceTensor::DataPop_(std::shared_ptr *host_tensor) { void *resHostBuf = nullptr; // 空 APP_ERROR ret = aclrtMallocHost(&resHostBuf, this->DeviceDataSize()); if (ret != APP_ERR_OK) { // 无法从主机 ret分配内存 MS_LOG(ERROR) "Failed to allocate memory from host ret = " ret; return Status(StatusCode::kMDNoSpace); } std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); auto processedInfo_ = outBuf; //Memcpy 从设备到主机的输出数据 ret = aclrtMemcpy(outBuf.get(), this->DeviceDataSize(), this->GetDeviceBuffer(), this->DeviceDataSize(), ACL_MEMCPY_DEVICE_TO_HOST); if (ret != APP_ERR_OK) { // 无法将内存从设备复制到主机 MS_LOG(ERROR) "Failed to copy memory from device to host, ret = " ret; return Status(StatusCode::kMDOutOfMemory); } auto data = std::static_pointer_cast(processedInfo_); unsigned char *ret_ptr = data.get(); mindspore::dataset::dsize_t dvppDataSize = this->DeviceDataSize(); const mindspore::dataset::TensorShape dvpp_shape({dvppDataSize, 1, 1}); // uint32_t : typedef unsigned int uint32_t _output_width_ = this->GetYuvStrideShape()[0]; uint32_t _output_widthStride_ = this->GetYuvStrideShape()[1]; uint32_t _output_height_ = this->GetYuvStrideShape()[2]; uint32_t _output_heightStride_ = this->GetYuvStrideShape()[3]; const mindspore::dataset::DataType dvpp_data_type(mindspore::dataset::DataType::DE_UINT8); mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, host_tensor); (*host_tensor)->SetYuvShape(_output_width_, _output_widthStride_, _output_height_, _output_heightStride_); if (!(*host_tensor)->HasData()) { return Status(StatusCode::kMCDeviceError); } // 成功将设备传感器数据弹出到主机中 MS_LOG(INFO) "Successfully pop DeviceTensor data onto host"; return Status::OK(); } #endif } // 命名空间 dataset } // 命名空间 mindspore ```
-
**function_block 1** **对ccsrc\pipeline\jit\parse\function_block.h的部分注释** 知识浅薄,还有许多未理解之处,欢迎各位纠正、讨论。 相对路径:mindspore\ccsrc\pipeline\jit\parse\function_block.h function_block是功能块(组件),介绍了pipeline(管道)方法中解析部分的功能块,function_block.h则先进行了函数声明,宏定义,函数原型这一部分。 ```cpp h文件是C语言和【C++】语言的头文件,一般在【.h】类的头文件里面只放入函数声明,宏定义,函数原型,而具体的实现在【.cpp】文件里面。 C程序的定义文件以.c为后缀,C++程序的定义文件通常以.cpp为后缀(也有一些系统以.cc或.cxx为后缀) ``` ```cpp #ifndef MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_FUNCTION_BLOCK_H_//防止双重定义 #define MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_FUNCTION_BLOCK_H_//预编译命令 //导入系统库 #include #include #include #include #include //无序图库 #include #include ////定义标准模块库(STL)类型.函数和运算符 //引用自定义头文件 #include "pipeline/jit/parse/parse_base.h" #include "utils/log_adapter.h" #include "utils/ordered_set.h" namespace mindspore {//创建一个名为mindspore的空间,并在其中嵌套一个名为parse解析器(作语法分析)的空间 namespace parse { class Parser;//解析类 class NameSpace;//命名空间类 class Symbol;//符号类 class FunctionBlock;//功能块类 using FunctionBlockPtr = std::shared_ptr;//使用功能块类这个空间 // A function block is a straight-line code sequence with no branches, every block has one one exit point // which is return. When parsing function, loop or branch , we use function block to track the structure of // the original source code. /*功能块是一个没有分支的直线代码序列,每个功能块都有一个出口点 这就是回报。在解析函数、循环或分支时,我们使用函数块跟踪原始源代码。*/ class FunctionBlock : public std::enable_shared_from_this {//从该站点启用共享 public: explicit FunctionBlock(const Parser &parser); virtual ~FunctionBlock() {} FuncGraphPtr func_graph() { return func_graph_; }//获取函数图 void WriteVariable(const std::string &var_name, const AnfNodePtr &node);//写入变量 AnfNodePtr ReadVariable(const std::string &var_name);//读变量 void AddPrevBlock(const FunctionBlockPtr &block);//添加块 void SetPhiArgument(const ParameterPtr φ);//设置φ参数 bool CollectRemovablePhi(const ParameterPtr φ);//判断是否可收集可移除的PHI // A block is matured if all its predecessors is generated //如果一个块的所有前辈(前面的操作)都已生成,则该块已成熟 void Mature(); CNodePtr ForceToBoolNode(const AnfNodePtr &cond);//强制布尔节点 CNodePtr ForceToWhileCond(const AnfNodePtr &cond);//强制至毫秒 void Jump(const FunctionBlockPtr &block, const AnfNodePtr &node);//跳转操作 AnfNodePtr SearchReplaceNode(const std::string &var, const ParameterPtr φ);//搜索替换节点 void ConditionalJump(AnfNodePtr condNode, const FunctionBlockPtr &trueBlock, const FunctionBlockPtr &falseBlock, bool unroll_loop = true); //条件跳转 // Create cnode for the assign statement like self.target = source. //为assign语句创建cnode,如self.target=source。 void SetStateAssign(const AnfNodePtr &target, const AnfNodePtr &source);//设置状态分配 void AddGlobalVar(const std::string &var_name) { (void)global_vars_.insert(var_name); }//添加全局变量 bool IsGlobalVar(const std::string &var_name) { return global_vars_.find(var_name) != global_vars_.end(); }//判断是否是全局变量 AnfNodePtr MakeResolveAstOp(const py::object &op); AnfNodePtr MakeResolveClassMember(const std::string &attr);//使解析类成为成员 AnfNodePtr MakeResolveSymbol(const std::string &value);//生成解析符号 AnfNodePtr MakeResolveOperation(const std::string &value);//进行解析操作 AnfNodePtr MakeResolve(const std::shared_ptr &name_space, const std::shared_ptr &resolve_symbol); const std::unordered_map &removable_phis() const { return removable_phis_; } void FindIsolatedNodes();//查找被分离的节点 void AddIsolatedNode(const AnfNodePtr &target);//添加隔离节点 void AttachIsolatedNodesBeforeReturn();//在返回之前附加隔离节点 private: // Block graph //块图 FuncGraphPtr func_graph_; // Block parser //块分析器 const Parser &parser_; // A block is matured if all its prev_blocks is processed // 如果一个区块的所有上一个区块都被处理,则该区块将到期 bool matured_; // Store the nest-level block. // Refer to comments in Parser::func_block_list_; //存储嵌套级别的块。 //请参阅Parser::func\u block\u list\ux中的注释; std::vector prev_blocks_; // Store args and variable's node, use a bool flag to indicate if the variable is used. ////存储参数和变量的节点,使用bool标志指示是否使用了变量。 std::map> vars_; // Map the parameter node to variable, it can be resolved if the block's predecessors are processed // 将参数节点映射到变量,如果处理块的前置项,则可以解析该变量 std::map phi_nodes_; // Jumps map the successor block and the function call that perform jump // Refer to comments in Parser::func_block_list_ that how to break the cyclic reference //跳转映射后续块和执行跳转的函数调用 //请参阅Parser::func_block_list_中的注释,了解如何中断循环引用 std::map jumps_; // Keep all removable phis which will be removed in one pass. //保留所有将一次性删除的可移除的PHI。 std::unordered_map removable_phis_; // Keep the map for the resolve node to the removable phi node. // For the case that ReadVariable returns a phi node although this phi node // generated in the prev block is identified as removable. The other blocks // should find this phi node. /*保留解析节点到可移动phi节点的映射。对于Read变量返回phi节点的情况,尽管此phi节点 在prev块中生成的被标识为可移动。其他块区应该找到这个phi节点。*/ std::unordered_map resolve_to_removable_phis_; // Hold declared global variables in function //在函数中保留声明的全局变量 std::set global_vars_; // Keep new made resolve symbol for the variable not found in vars_. //为变量保留新生成的解析符号,但在变量中找不到。 std::unordered_map var_to_resolve_; // Isolated nodes. // 孤立节点。 OrderedSet isolated_nodes_;//包含孤立节点的有序集 }; } // namespace parse } // namespace mindspore #endif // MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_FUNCTION_BLOCK_H_ ``` 以上即为本篇的所有内容,因学识与能力有限,如有不足之处,请多多包涵与指教。
-
**对mindspore\ccsrc\pipeline\jit\parse\resolve.h的部分注释和对using namespace std的学习理解** 知识浅薄,还有许多未理解之处,欢迎各位纠正、讨论。 相对路径:mindspore\ccsrc\pipeline\jit\parse\resolve.h **C++命名空间(using namespace std)** namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。namespace是为了解决C++中的名字冲突而引入的。 C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。 由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择: 1. 直接指定标识符:例如std::iostream而不是iostream。完整语句如下: std::cout std::hex 3.4 std::endl; 2. 使用using关键字 using std::cout; using std::endl; using std::cin; 以上程序可以写成如下代码: ```cpp using std::cout 3.4 #include #include using namespace std; ``` 这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写: ```cpp cout hex 3.4 endl; ``` 因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。所以就有了和等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"实际上就是告诉编译器,你类型是什么,在哪能找到。常用的是using namespace std,就是说用C++的标准名字空间。 你也可以引用你自己的名字空间。 **using在C plusplus中的使用。** 1、引入命名空间 引入命名空间std的意思,这样在这个引入命名空间的范围内,使用函数时就不用再标明其出处,可以理解为告诉编译器,我从哪里来,你就到这个地方找我吧。这样就不会在调用多个同名函数的时候搞错了,A和B空间下的函数是不一样的,using A就去A下找吧,别犯错误迷路了去B下找这就错了。 2、类型别名 在C++11新标准中,采用using来给类型取一个别名,这样就好记了,避免有的名字过于繁琐在用的时候还要写原名。 3、在子类中引入基类的成员 继承构造 假如子类中有和基类同名的函数,这时还想用基类的函数,就可以使用using,不然就会隐藏基类的函数。 ```cpp //在头文件中使用#ifdef和#ifndef是非常重要的,可以防止双重定义的错误 #ifndef MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_RESOLVE_H_ #define MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_RESOLVE_H_//预编译命令 //用的多的函数放在.h头文件中定义声明。 //尽量不要在.h头文件中设置全局变量,或者静态全局变量。 //导入系统文件或者是自定义文件: #include #include #include "ir/anf.h" #include "ir/manager.h" #include "pipeline/jit/parse/python_adapter.h" #include "pipeline/jit/parse/parse_base.h" #include "abstract/abstract_value.h" #include "utils/log_adapter.h" // forward declaration of ResourceBase //资源库的正向声明 namespace mindspore {//命名创建一个名为mindspore的空间,并在其中嵌套一个名为pipeline(管道)的空间 namespace pipeline {//namespace,是指标识符的各种可见范围。 class ResourceBase;//资源库Ptr using ResourceBasePtr = std::shared_ptr;//引入使用这个空间 } // namespace pipeline } // namespace mindspore namespace mindspore { namespace parse { // NameSpace class for resolving python code. //用于解析python代码的命名空间类。 class NameSpace : public Named { public: NameSpace(const std::string &module, const py::object &obj) : Named(module), module_(module), obj_(obj) {} ~NameSpace() override = default;//将override赋为default 表示默认存在构造函数 MS_DECLARE_PARENT(NameSpace, Named);//对NameSpace, Named声明父项 py::object obj() { return obj_; }//获取目标对象 std::string module() { return module_; }//获取字符模块 abstract::AbstractBasePtr ToAbstract() override { return std::make_shared(shared_from_base(), std::make_shared()); } private://修饰成员变量和成员 被private修饰的成员只能在本类中访问。 // namespace of the module //模块的命名空间 std::string module_; // namespace object //名称空间对象 py::object obj_; }; using NameSpacePtr = std::shared_ptr;//引入使用NameSpace空间 // Symbol in NameSpace or Class which shall be resolved. //名称空间或类中应解析的符号。 class Symbol : public Named { public: explicit Symbol(const std::string &symbol) : Named(symbol), symbol_(symbol) {} explicit Symbol(const std::string &symbol, const std::string &name) : Named(name), symbol_(symbol) {} ~Symbol() override = default;//表示默认存在构造函数 MS_DECLARE_PARENT(Symbol, Named);//MS声明Symbol,和Named的父项关系 std::string symbol() { return symbol_; } abstract::AbstractBasePtr ToAbstract() override { return std::make_shared(shared_from_base(), std::make_shared()); } private://修饰成员变量和成员 被private修饰的成员只能在本类中访问。 std::string symbol_;//字符串符号 }; using SymbolPtr = std::shared_ptr;//引入使用这个Symbol空间 // PyObjectWrapper class wrappers resolved python object for further processing.\ //Py对象包装器类包装器解析python对象以供进一步处理。 class PyObjectWrapper : public Named {//Py对象包装器 public: explicit PyObjectWrapper(const py::object &obj, const std::string &name = "Python object") : Named(name), obj_(obj) {} ~PyObjectWrapper() override = default;//将override赋值为default 表示默认存在构造函数 MS_DECLARE_PARENT(PyObjectWrapper, Named);//MS声明PyObjectWrapper,和Named的父项关系 py::object obj() { return obj_; }//返回目标对象 private://修饰成员变量和成员 被private修饰的成员只能在本类中访问。 // the object that needs to be resolved //需要解决的对象 py::object obj_; }; // ClassObject class wrappers dataclass //类对象类包装器数据类 class ClassObject : public PyObjectWrapper {//对象类包装器 public: explicit ClassObject(const py::object &obj, const std::string &name = "Python dataclass") : PyObjectWrapper(obj, name) {} ~ClassObject() override = default;//将override赋为default 表示默认存在构造函数 MS_DECLARE_PARENT(ClassObject, PyObjectWrapper);//MS声明父项关系ClassObject,和PyObjectWrapper abstract::AbstractBasePtr ToAbstract() override;//修饰类和方法 /*1,abstract修饰类,会使这个类成为一个抽象类,这个类将不能生成对象实例,但可以做为对象变量声明的类型,也就是编译时类型,抽象类就像当于一类的半成品,需要子类继承并覆盖其中的抽象方法。 2,abstract修饰方法,会使这个方法变成抽象方法,也就是只有声明(定义)而没有实现,实现部分以";"代替。需要子类继承实现(覆盖)。 */ }; // ClassType class wrappers class name in python //类类型类包装器python中的类名 class ClassType : public PyObjectWrapper {//类型类包装器 public: explicit ClassType(const py::object &obj, const std::string &name = "Python class type") : PyObjectWrapper(obj, name) {} ~ClassType() override = default;//将override赋为default 表示默认存在构造函数 MS_DECLARE_PARENT(ClassType, PyObjectWrapper);//MS声明父项Classtype,PyObjectWrapper abstract::AbstractBasePtr ToAbstract() override; }; using ClassTypePtr = std::shared_ptr;//引入ClassType命名空间 // SymbolResolver class for resolving symbol extracted from AnfNode. //用于解析从节点提取的符号的符号解析器类。 class SymbolResolver {//解析符号 public: SymbolResolver(const NameSpacePtr &name_space, const SymbolPtr &symbol, const AnfNodePtr &node) : namespace_(name_space), symbol_(symbol), resolved_node_(node) {} ~SymbolResolver() = default;//将SymbolResolver赋为default 表示默认存在构造函数 // resolve symbol in namespace and save it in result_; //解析命名空间中的符号并将其保存在结果中 bool Resolve();//判断是否(bool)完成解析 NameSpacePtr get_namespace() { return namespace_; } //获取命名空间 SymbolPtr symbol() { return symbol_; } //获取符号标记 py::object &result() { return result_; } //目标结果 AnfNodePtr resolved_node() { return resolved_node_; } //解析节点 // Resolve result //解析结果 py::object result_; private: // namespace where the symbol locates //符号所在的命名空间 NameSpacePtr namespace_; // the symbol that needs to be resovled //需要解决的符号 SymbolPtr symbol_; // the node that has been resolved //已解析的节点 AnfNodePtr resolved_node_; }; using SymbolResolverPtr = std::shared_ptr; // Resolve symbol in namespace. //解析命名空间中的符号 AnfNodePtr ResolveSymbol(const FuncGraphManagerPtr &manager, const NameSpacePtr &name_space, const SymbolPtr &symbol, const AnfNodePtr &node); // Resolve Cell with attr name. //解析具有attr名称的单元格。 AnfNodePtr ResolveCellwithAttr(const FuncGraphManagerPtr &manager, const NameSpacePtr &name_space, const SymbolPtr &symbol, const AnfNodePtr &node, const std::string &attr); // Resolve one graph which normally is the root graph. FuncGraph shall be managed by res->manager(). //解析一个通常为根图的图。FuncGraph应由res管理器管理 bool ResolveFuncGraph(const FuncGraphPtr &func_graph, const pipeline::ResourceBasePtr &res, bool use_profile = true); // Resolve all graphs in manager which is defined outside of pipeline::Resource. // Mainly used for test cases or resolve graphs which will not be managed by manager. //解析在pipeline::Resource之外定义的管理器中的所有图形。 //主要用于不由manager管理的测试用例或解析图。 bool ResolveAll(const FuncGraphManagerPtr &manager);//判断是否(bool类型)解析所有函数图管理器 } // namespace parse } // namespace mindspore #endif // MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_RESOLVE_H_ ``` 以上即为本篇的所有内容,因学识与能力有限,如有不足之处,请多多包涵与指教。 学习的博客:https://blog.csdn.net/fsz520w/article/details/82561795
-
**对.clang-format的部分注释** 知识浅薄,还有许多未理解之处,欢迎各位纠正、讨论。 **clang-format简介** Clang-Format可用于格式化(排版)多种不同语言的代码。我们编写时需要注意代码的格式,通过该工具能够很好的管理代码格式。clang-format,它是基于clang的一个命令行工具,能够自动化格式:C、C++、Object-C代码,支持多种代码风格:Google、Chromium、LLVM、Mozilla、WebKit。也支持自定义风格(通过编写.clang-format文件)很方便的统一代码格式。 如果你使用Visual Studio Code编写代码(我当前用来编写C++),VSCode的C/C++插件自带了Clang-Format格式化工具,不仅拥有上述5种排版格式,还定义了自己的Visual Studio排版格式,且此格式是默认的排版格式(Ubuntu下格式化快捷键:Ctrl+Shift+I,Windows下格式化快捷键:Shift+Alt+F),VS Code格式化的具体内容可以参考官网:[链接](https://code.visualstudio.com/docs/languages/cpp#_editing-code "链接") **配置文件说明** ```cpp # 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto Language: Cpp # BasedOnStyle: Google # 访问说明符(public、private等)的偏移 AccessModifierOffset: -1 # 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行) AlignAfterOpenBracket: Align # 连续赋值时,对齐所有等号 AlignConsecutiveAssignments: false # 连续声明时,对齐所有声明的变量名 AlignConsecutiveDeclarations: false # 左对齐逃脱换行(使用反斜杠换行)的反斜杠 换行时反斜杠位置:左对齐 AlignEscapedNewlines: Left # 水平对齐二元和三元表达式的操作数 AlignOperands: true # 对齐连续的尾随的注释 AlignTrailingComments: true # 允许函数声明的所有参数在放在下一行 AllowAllParametersOfDeclarationOnNextLine: true # 允许短的块放在同一行 AllowShortBlocksOnASingleLine: false # 允许短的case标签放在同一行 AllowShortCaseLabelsOnASingleLine: false # 允许短的函数放在同一行: None,InlineOnly(定义在类中),empty(空函数),Inline(定义在类中,空函数),all AllowShortFunctionsOnASingleLine: All # 允许短的if语句保持在同一行 AllowShortIfStatementsOnASingleLine: true # 允许短的循环保持在同一行 AllowShortLoopsOnASingleLine: true # 总是在返回类型后换行(deprecated) AlwaysBreakAfterDefinitionReturnType: None # 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数), # AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义) AlwaysBreakAfterReturnType: None # 总是在多行string字面量前换行 AlwaysBreakBeforeMultilineStrings: true # 总是在template声明后换行 AlwaysBreakTemplateDeclarations: Yes # false表示函数实参要么都在同一行,要么都各自一行 BinPackArguments: true # false表示所有形参要么都在同一行,要么都各自一行 BinPackParameters: true # 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效 # 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似), # Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似), # Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom # 注:这里认为语句块也属于函数 BraceWrapping: # class定义后面 AfterClass: false # 控制语句后面 AfterControlStatement: false # enum定义后面 AfterEnum: false # 函数定义后面 (OC无效) AfterFunction: false # 命名空间定义后面 AfterNamespace: false # ObjC定义后面 AfterObjCDeclaration: false # Struct结构体定义后面 AfterStruct: false # Union定义后面 AfterUnion: false # Extern 定义后面 AfterExternBlock: false # Catch之前 BeforeCatch: false # Else之前 BeforeElse: false # 缩进大括号 IndentBraces: false # false 时,空方法体 {} 放在一行 SplitEmptyFunction: true # false 时,空记录(例如,类,结构或联合){} 放在一行 SplitEmptyRecord: true # false 且 AfterNamespace == true 时 空命名空间体可放到一行: {} SplitEmptyNamespace: true # 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行) BreakBeforeBinaryOperators: None # 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似), # Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似), # Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom # 注:这里认为语句块也属于函数 BreakBeforeBraces: Attach # 继承列表的逗号前换行 BreakBeforeInheritanceComma: false # 在构造函数的初始化列表的逗号前换行 BreakInheritanceList: BeforeColon # 在三元运算符前换行 BreakBeforeTernaryOperators: true # 在构造函数的初始化列表的逗号前换行 BreakConstructorInitializersBeforeComma: false # 初始化列表前换行 BreakConstructorInitializers: BeforeColon # Java注解后换行 在Java文件中的字段上的每个注释之后中断。 BreakAfterJavaFieldAnnotations: false # 允许打破当格式化字符串。 BreakStringLiterals: true # Java注解后换行 每行字符的限制,0表示没有限制 ColumnLimit: 120 # 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变 CommentPragmas: '^ IWYU pragma:' # 紧凑 命名空间 CompactNamespaces: false # 构造函数的初始化列表要么都在同一行,要么都各自一行 ConstructorInitializerAllOnOneLineOrOnePerLine: true # 构造函数的初始化列表的缩进宽度 ConstructorInitializerIndentWidth: 4 # 延续的行的缩进宽度 ContinuationIndentWidth: 2 # 去除C++11的列表初始化的大括号{后和}前的空格 Cpp11BracedListStyle: true # 继承最常用的指针和引用的对齐方式 DerivePointerAlignment: false # 关闭格式化 DisableFormat: false # 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental) ExperimentalAutoDetectBinPacking: false # 固定命名空间注释 FixNamespaceComments: true # 需要被解读为foreach循环而不是函数调用的宏 ForEachMacros: # - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve # 对#include进行排序,匹配了某正则表达式的#include拥有对应的优先级,匹配不到的则默认优先级为INT_MAX(优先级越小排序越靠前), # 可以定义负数优先级从而保证某些#include永远在最前面 # include 分组排序方式 Preserve(按组排序) Merge(合并成一组排序)Regroup(按 IncludeCategories 重新分组排序) IncludeCategories: - Regex: '^.*\.h>' Priority: 2 - Regex: '^.*\.h>' Priority: 1 - Regex: '^.*' Priority: 2 - Regex: '.*' Priority: 3 # 指定正则表达式file-to-main-include映射中允许的后缀。 IncludeIsMainRegex: '([-_](test|unittest))?$' # 缩进case标签 case 是否缩进一级 IndentCaseLabels: true # 预处理代码缩进样式。None(不缩进)AfterHash(缩进) IndentPPDirectives: None # 缩进宽度 IndentWidth: 2 # 函数返回类型换行时,缩进函数声明或函数定义的函数名 IndentWrappedFunctionNames: false # JS 引号样式 Leave("",'') Single("") Double("") JavaScriptQuotes: Leave # JS import 是否需要换行 JavaScriptWrapImports: true # 保留在块开始处的空行(OC 没用) KeepEmptyLinesAtTheStartOfBlocks: false # 开始一个块的宏的正则表达式 MacroBlockBegin: '' # 结束一个块的宏的正则表达式 MacroBlockEnd: '' # 连续空行的最大数量 MaxEmptyLinesToKeep: 1 # 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All NamespaceIndentation: None # OC 协议根据 ColumnLimit 长度换行 实现内部缩进宽度 ObjCBinPackProtocolList: Never # 使用ObjC块时缩进宽度 使用 OC block 函数实现内部缩进宽度 ObjCBlockIndentWidth: 2 # 在ObjC的@property后添加一个空格 ObjCSpaceAfterProperty: false # 在ObjC的protocol列表前添加一个空格 ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 1 # 在一个注释中引入换行的penalty PenaltyBreakComment: 300 # 第一次在前换行的penalty PenaltyBreakFirstLessLess: 120 # 在一个字符串字面量中引入换行的penalty PenaltyBreakString: 1000 # 对于每个在行字符数限制之外的字符的penalty PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 # 将函数的返回类型放到它自己的行的penalty PenaltyReturnTypeOnItsOwnLine: 200 # 指针和引用的对齐: Left, Right, Middle PointerAlignment: Right #RawStringFormats: # - Delimiter: pb # Language: TextProto # BasedOnStyle: google # 允许重新排版注释 RawStringFormats: # 允许排序#include - Language: Cpp Delimiters: - cc - CC - cpp - Cpp - CPP - 'c++' - 'C++' CanonicalDelimiter: '' BasedOnStyle: google - Language: TextProto Delimiters: - pb - PB - proto - PROTO EnclosingFunctions: - EqualsProto - EquivToProto - PARSE_PARTIAL_TEXT_PROTO - PARSE_TEST_PROTO - PARSE_TEXT_PROTO - ParseTextOrDie - ParseTextProtoOrDie CanonicalDelimiter: '' BasedOnStyle: google ReflowComments: true SortUsingDeclarations: true # 在C风格类型转换后添加空格 SpaceAfterCStyleCast: false # template 模板关键字后面添加空格 SpaceAfterTemplateKeyword: true # 在赋值运算符之前添加空格 SpaceBeforeAssignmentOperators: true # 开圆括号之前添加一个空格: Never, ControlStatements, Always 初始化 c++ 11 对象的前面空格 # 初始化 c++ 11 对象的前面空格 SpaceBeforeCpp11BracedList: false # 构造函数:前加空格 SpaceBeforeCtorInitializerColon: true # 继承的:前面加空格 SpaceBeforeInheritanceColon: true # 开圆括号之前添加一个空格: Never, ControlStatements, Always SpaceBeforeParens: ControlStatements # false 清除 for 循环:前面的空格 for (auto v : values) {} SpaceBeforeRangeBasedForLoopColon: true # 在空的圆括号中添加空格 SpaceInEmptyParentheses: false # 在尾随的评论前添加的空格数(只适用于//) SpacesBeforeTrailingComments: 2 # 在尖括号的后和>前添加空格 int > SpacesInAngles: false # 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格 # 快捷数组 内部加空格 [ 1, 2, 3 ]; :前加空格 f({a : 1, b : 2, c : 3}); SpacesInContainerLiterals: true # 在C风格类型转换的括号中添加空格 SpacesInCStyleCastParentheses: false # 在圆括号的(后和)前添加空格 SpacesInParentheses: false # 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响 SpacesInSquareBrackets: false # 标准: Cpp03, Cpp11, Auto Standard: Auto StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION # tab宽度 TabWidth: 2 # 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always UseTab: Never SortIncludes: false ``` **vscode clang-format插件的使用** - 首先安装Clang-Format插件。 - 在“首选项”->“设置”中修改Clang-format: Executable的位置为实际位置,一般插件放置于D:\Users\xxx\.vscode\extensions\ms-vscode.cpptools-x.xx.x\LLVM\bin目录下,这里需要指定到可执行文件,例如:D:\Users\jaron\.vscode\extensions\ms-vscode.cpptools-0.29.0\LLVM\bin\clang-format.exe - 设置C_Cpp: Clang_format_style和C_Cpp: Clang_format_fallback Style的值为file,可以指定从workspace目录中使用.clang-format文件。 - 将.clang-format文件拷贝到对应的工程目录下。 - 在打开的源代码下执行Alt + Shift + F即可格式化源代码,或者右键选择“格式化文档”。 **命令行** - 用自定义配置格式化代码 /clang-format.exe --assume-filename=E:/test/.clang-format -i E:/test/src/main.c,指定.clang-format格式文件 - 或 clang-format.exe -style=file -i E:/test/src/main.c,会从当前目录查找.clang-format格式文件,找不到就向上一层目录寻找,再上一层……直到找到为止。 - 用内置配置格式化代码,这里指定Webkit clang-format.exe -style=Webkit -i D:/test/src/main.c - 命令行导出特定风格的配置文件(以Webkit为例) clang-format.exe -dump-config -style=Webkit >.clang-format 以上即为本篇的所有内容,因学识与能力有限,如有不足之处,请多多包涵与指教!
-
- 本程序是对C++写的Cache admin的包装,用于对缓存服务器的操作 - c++代码在`minddata/dataset/engine/cache/cache_admin.cc`中 ```python """ This is the entrance to start the cache service """ import os import stat import subprocess import sys import mindspore def main(): """Entry point for cache service""" # 对路Cache径进行处理 cache_admin_dir = os.path.join(os.path.dirname(mindspore.__file__), "bin") os.chdir(cache_admin_dir) cache_admin = os.path.join(cache_admin_dir, "cache_admin") if not os.path.exists(cache_admin): raise RuntimeError("Dataset cache is not supported on your mindspore version.") cache_server = os.path.join(cache_admin_dir, "cache_server") os.chmod(cache_admin, stat.S_IRWXU) os.chmod(cache_server, stat.S_IRWXU) # 添加Cache文件权限 # set LD_LIBRARY_PATH for libpython*.so python_lib_dir = os.path.join(os.path.dirname(mindspore.__file__), "../../..") os.environ['LD_LIBRARY_PATH'] = python_lib_dir + ":" + os.environ.get('LD_LIBRARY_PATH') # LD_PRELOAD libnnacl.so nnacl_lib = os.path.join(os.path.dirname(mindspore.__file__), "lib/libnnacl.so") os.environ['LD_PRELOAD'] = nnacl_lib # 设置环境变量 # 调用C++的cache_admin用以进行操作 sys.exit(subprocess.call([cache_admin] + sys.argv[1:], shell=False, env=os.environ)) ```
-
- 本代码实现了对于数据集的迭代器类 - 首先实现了`Iterator`迭代器的基类,然后通过重写`_get_next`派生出返回形式不同的迭代器`DictIterator`与`TupleIterator` - 由于迭代器数据的产生是来自于c++的进程,需要调用`_c_dataengine`库来获取进程产生的数据 - 本代码还维护了一个全局的迭代器序列,由于是使用的弱引用,所以并不会影响迭代器的消亡以及垃圾回收 - 程序还实现了一个`DummyIterator`形式更加简单的迭代器 ```python # Copyright 2019 Huawei Technologies Co., Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """Built-in iterators. """ from abc import abstractmethod import os import signal import weakref import numpy as np from mindspore.common.tensor import Tensor import mindspore._c_dataengine as cde from mindspore import log as logger _ITERATOR_CLEANUP = False # 用于记录全局迭代器状态 def _set_iterator_cleanup(): global _ITERATOR_CLEANUP _ITERATOR_CLEANUP = True def _unset_iterator_cleanup(): global _ITERATOR_CLEANUP _ITERATOR_CLEANUP = False def check_iterator_cleanup(): global _ITERATOR_CLEANUP return _ITERATOR_CLEANUP ITERATORS_LIST = list() # 迭代器列表,由于是弱引用,其存在不会影响迭代器内容的销毁 def _cleanup(): """Release all the Iterator.""" _set_iterator_cleanup() for itr_ref in reversed(ITERATORS_LIST): # 从后往前销毁迭代器列表 itr = itr_ref() if itr is not None: itr.release() class Iterator: """ General Iterator over a dataset. 生成数据集的迭代器 Attributes: dataset: Dataset to be iterated over """ def __init__(self, dataset, num_epochs=-1, output_numpy=False, do_copy=True): self._col_names = None # create a copy of tree and work on it. self.__ori_dataset = dataset self.ir_tree, self.dataset = dataset.create_ir_tree() self._runtime_context = cde.PythonRuntimeContext() # 创建C++运行的的流水线 self._runtime_context.Init() consumer = cde.PythonIteratorConsumer(num_epochs) consumer.Init(self.ir_tree) self._runtime_context.AssignConsumer(consumer) self._iterator = self._runtime_context.GetConsumer() # 得到c++里面的迭代器 self._transform_tensor = lambda t: t.as_array() if not output_numpy: if do_copy: self._transform_tensor = lambda t: Tensor(t.as_array()) else: self._transform_tensor = lambda t: Tensor.from_numpy(t.as_array()) self.__index = 0 ITERATORS_LIST.append(weakref.ref(self)) # 向全局迭代器列表中添加迭代器 _unset_iterator_cleanup() # 设置迭代器非空 def __iter__(self): return self def stop(self): """ Manually terminate Python iterator instead of relying on out of scope destruction. 刻意终止迭代器,而不是通过超出范围后停止 """ if hasattr(self, '_runtime_context') and self._runtime_context: # 销毁迭代器本身 if hasattr(self, '_iterator') and self._iterator: self._runtime_context.Terminate() # 如果由运行时内容,调用其终止函数 del self._iterator # 并销毁迭代器 del self._runtime_context del self.dataset # 销毁迭代器列表中的引用 # get weakref which is dead dead_iterator = [] for index, item in enumerate(ITERATORS_LIST): # item() == None indicate the object is dead # id(item()) == id(self) indicate del self if item() is None or id(item()) == id(self): # 在迭代器列表中找到自己这个迭代器 dead_iterator.append(index) # 加入消亡列表中 # del dead weakref for index in reversed(dead_iterator): ITERATORS_LIST.pop(index) # 将消亡列表中的元素从迭代器列表中排除 def release(self): self.stop() def __del__(self): self.release() @abstractmethod def _get_next(self): # get_next抽象方法,要求各个实例自己去实现 raise RuntimeError("Calling base class Iterator's get_next is invalid.") def __next__(self): if not self._runtime_context: logger.warning("Iterator does not have a running C++ pipeline." + "It might because Iterator stop() had been called, or C++ pipeline crashed silently.") raise RuntimeError("Iterator does not have a running C++ pipeline.") data = self._get_next() if not data: if self.__index == 0: logger.warning("No records available.") if self.__ori_dataset.dataset_size is None: self.__ori_dataset.dataset_size = self.__index raise StopIteration # 后继无人 self.__index += 1 return data def __deepcopy__(self, memo): return self def _getters(self): """ Get pipeline information. """ getter = cde.TreeGetters() #用cde获取流水线的相关信息 getter.Init(self.ir_tree) self._runtime_context.AssignConsumer(getter) self._col_names = getter.GetColumnNames() def get_col_names(self): """ Get names of the columns in the dataset """ if self._col_names is None: self._getters() return self._col_names class DictIterator(Iterator): """ The derived class of Iterator with dict type. """ def _get_next(self): """ Returns the next record in the dataset as dictionary Returns: Dict, the next record in the dataset. """ try: return {k: self._transform_tensor(t) for k, t in self._iterator.GetNextAsMap().items()} # 将结果以字典的形式返回,并张量化 except RuntimeError as err: ## maybe "Out of memory" / "MemoryError" error # 可能发生了内存越界,需要终止出错的进程 err_info = str(err) if err_info.find("Out of memory") >= 0 or err_info.find("MemoryError") >= 0: logger.critical("Memory error occurred, process will exit.") os.kill(os.getpid(), signal.SIGKILL) raise err class TupleIterator(Iterator): """ The derived class of Iterator with list type. """ def __init__(self, dataset, columns=None, num_epochs=-1, output_numpy=False, do_copy=True): if columns is not None: if not isinstance(columns, list): columns = [columns] dataset = dataset.project(columns) super().__init__(dataset, num_epochs, output_numpy, do_copy) def _get_next(self): """ Returns the next record in the dataset as a list Returns: List, the next record in the dataset. """ return [self._transform_tensor(t) for t in self._iterator.GetNextAsList()] # 返回列表形式并张量化的结果 class DummyIterator: """ A DummyIterator only work when env MS_ROLE="MS_PSERVER" or MS_ROLE="MS_SCHED" """ # 形式更加简单的迭代器 def __init__(self, dataset, mode): self.mode = mode self.shapes = dataset.output_shapes() self.types = dataset.output_types() self.fetched_first = False def __get_tensor(self): tensor_row = [] for np_shape, np_type in zip(self.shapes, self.types): input_np = np.zeros(np_shape, np_type) tensor = Tensor(input_np) tensor_row.append(tensor) return tensor_row def __iter__(self): return self def __next__(self): if self.mode == "tuple": if not self.fetched_first: self.fetched_first = True return self.__get_tensor() raise StopIteration() ```
-
一、typeinfo 运行时类型信息工具类namespace std { //含有某个类型的信息,由实现生成。这是 typeid 运算符所返回的类。 class type_info; //当 typeid 表达式中的实参为空值时抛出的异常 class bad_cast; 由非法的 dynamic_cast 表达式抛出的异常,即引用类型转型失败 class bad_typeid; }用途 typeid是C++的关键字之一,等同于sizeof这类的操作符,返回结果是名为type_info的标准库类型的对象的引用。类包含的参数和成员函数的功能: -> 创建一个引用对象 const type_info& t0 = typeid(int); -> 获得类型名称 const char* b=t0.name(); ->比较对象是否相等,仅支持2种比较符 t1 == t2 ,t1 != t2 二、inline (一)inline函数在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联。inline int min(int first, int secend) {/****/};inline函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数。与非inline函数不同的是,inline函数必须在调用该函数的每个文本文件中定义。当然,对于同一程序的不同文件,如果inline函数出现的话,其定义必须相同。对于由两个文件compute.C和draw.C构成的程序来说,程序员不能定义这样的min()函数,它在compute.C中指一件事情,而在draw.C中指另外一件事情。如果两个定义不相同,程序将会有未定义的行为.为保证不会发生这样的事情,建议把inline函数的定义放到头文件中。在每个调用该inline函数的文件中包含该头文件。这种方法保证对每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。(二)内联函数的编程风格(摘自高质量C++/C 编程指南)关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。如下风格的函数Foo 不能成为内联函数:inline void Foo(int x, int y); // inline 仅与函数声明放在一起 void Foo(int x, int y) { }而如下风格的函数Foo 则成为内联函数:void Foo(int x, int y); inline void Foo(int x, int y) // inline 与函数定义体放在一起 { }所以说,inline 是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的声明、定义体前面都加了inline 关键字,但我认为inline 不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C 程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。定义在类声明之中的成员函数将自动地成为内联函数,例如class A { public: void Foo(int x, int y) { } // 自动地成为内联函数 }将成员函数的定义体放在类声明之中虽然能带来书写上的方便,但不是一种良好的编程 风格,上例应该改成:// 头文件 class A { public: void Foo(int x, int y); } // 定义文件 inline void A::Foo(int x, int y) { }慎用内联 内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数? 如果所有的函数都是内联函数,还用得着“内联”这个关键字吗? 内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的 执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收 获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大, 消耗更多的内存空间。以下情况不宜使用内联: (1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。 (2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。 类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构 函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。 所以不要随便地将构造函数和析构函数的定义体放在类声明中。 一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明 了inline 不应该出现在函数的声明中)。/** * Copyright 2020 Huawei Technologies Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pipeline/pynative/pynative_execute_ge.h" #include <typeinfo> #include <map> #include <set> #include <unordered_set> #include "utils/any.h" #include "utils/utils.h" #include "utils/ms_context.h" #include "frontend/operator/ops.h" #include "pipeline/jit/parse/data_converter.h" #include "pipeline/jit/static_analysis/prim.h" #include "backend/session/session_factory.h" #include "pybind_api/ir/tensor_py.h" #include "transform/graph_ir/op_declare/array_ops_declare.h" const char SINGLE_OP_GRAPH[] = "single_op_graph"; using mindspore::tensor::TensorPy; namespace mindspore { namespace pynative { using MeTensor = mindspore::tensor::Tensor; using MeTensorPtr = mindspore::tensor::TensorPtr; using GeOperator = ge::Operator; using GeOperatorPtr = std::shared_ptr<GeOperator>; using transform::GraphRunner; using transform::GraphRunnerOptions; using transform::OperatorPtr; static std::shared_ptr<session::SessionBasic> session = nullptr; inline ValuePtr PyAttrValue(const py::object &obj) { //在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。 //栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。 //在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。 ValuePtr converted_ret = nullptr; bool converted = parse::ConvertData(obj, &converted_ret); if (!converted) { MS_LOG(EXCEPTION) << "Attribute convert error with type:" << std::string(py::str(obj)); } return converted_ret; } MeTensorPtr ConvertPyObjToTensor(const py::object &obj) { //将Py Obj转换为张量 MeTensorPtr me_tensor_ptr = nullptr; if (py::isinstance<MeTensor>(obj)) { me_tensor_ptr = py::cast<MeTensorPtr>(obj); } else if (py::isinstance<py::tuple>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::tuple>(obj)), nullptr); } else if (py::isinstance<py::float_>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::float_>(obj)), nullptr); } else if (py::isinstance<py::int_>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::int_>(obj)), nullptr); } else if (py::isinstance<py::list>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::list>(obj)), nullptr); } else if (py::isinstance<py::array>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::cast<py::array>(obj), nullptr); } else { MS_LOG(EXCEPTION) << "Run op inputs type is invalid!"; } return me_tensor_ptr; } bool SetInputsForSingleOpGraph(const OpExecInfoPtr &op_exec_info, const std::vector<GeTensorPtr> &inputs, const OperatorPtr &op, std::vector<GeOperator> *graph_input_nodes) { //设置单操作图的输入 MS_EXCEPTION_IF_NULL(op_exec_info); MS_EXCEPTION_IF_NULL(graph_input_nodes); auto op_inputs = op_exec_info->op_inputs; std::string op_name = op_exec_info->op_name; transform::OpAdapterPtr adapter = transform::DfGraphConvertor::FindAdapter(op_name, true); //Op适配器指针 if (adapter == nullptr) { return false; } int64_t op_input_idx = 1; size_t size = inputs.size(); for (size_t i = 0; i < size; i++) { if (inputs[i] == nullptr) { continue; } auto const_op = std::make_shared<transform::Constant>(); // make_shared函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr; //由于是通过shared_ptr管理内存,因此一种安全分配和使用动态内存的方法。 MS_EXCEPTION_IF_NULL(const_op); (void)const_op->set_attr_value(*inputs[i]); MeTensorPtr me_tensor_ptr = ConvertPyObjToTensor(op_inputs[i]); MS_EXCEPTION_IF_NULL(me_tensor_ptr); auto const_op_desc = transform::TransformUtil::GetGeTensorDesc(me_tensor_ptr->shape_c(), me_tensor_ptr->data_type(), kOpFormat_NCHW); if (const_op_desc == nullptr) { MS_LOG(ERROR) << "Create variable " << op_name << " output descriptor failed!"; return false; } auto pointer_cast_const_op = std::static_pointer_cast<transform::Constant>(const_op); // 返回正确类型的sp的副本,并将其存储的指针从U*静态转换为T*。 //如果sp不为空,则返回的对象共享sp资源的所有权,使用计数增加1。 //如果sp为空,则返回一个空的shared_ptr对象。 MS_EXCEPTION_IF_NULL(pointer_cast_const_op); (void)pointer_cast_const_op->update_output_desc_y(*const_op_desc); auto &input_map = adapter->getInputMap(); if (input_map.find(op_input_idx) == input_map.end()) { continue; } if (adapter->setInput(op, op_input_idx++, const_op)) { MS_LOG(ERROR) << "Failed to set params, index is " << op_input_idx; return false; } graph_input_nodes->push_back(*const_op); } return true; }
-
有业务需要在QT的开发环境中引入OBS服务,有没有相关的集成文档或指南提供,方便加快集成过程,谢谢
-
OpenCV用C++语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言接口。该库也有大量的Python、Java and MATLAB/OCTAVE(版本2.5)的接口。这些语言的API接口函数可以通过在线文档获得。如今也提供对于C#、Ch、Ruby的支持。所有新的开发和算法都是用C++接口。一个使用CUDA的GPU接口也于2010年9月开始实现。OpenCV可以在Windows,Android,Maemo,FreeBSD,OpenBSD,iOS,Linux 和Mac OS等平台上运行。使用者可以在 SourceForge 获得官方版本,或者从 SVN 获得开发版本。OpenCV也是用CMake。在Windows上编译OpenCV中与摄像输入有关部分时,需要DirectShow SDK中的一些基类。该SDK可以从预先编译的Microsoft Platform SDK(or DirectX SDK 8.0 to 9.0c / DirectX Media SDK prior to 6.0)的子目录Samples\Multimedia\DirectShow\BaseClasses获得。于2010年12月06日,OpenCV 2.2.0 正式版发布
-
【功能模块】FractionalMaxPool算子有两个attr是seed和seed2,用来产生随机数,在tensorflow中对seed和seed2的定义是:seed:可选的int,默认为0;如果seed或seed2被设置为非零,则随机数生成器由给定的seed生成,否则,它由随机种子生成.seed2:可选的int,默认为0;第二个seed,以避免发生seed碰撞.【操作步骤&问题现象】1、但是C++产生随机数只能调用一个seed,请问这个问题该怎么处理2、我用C++的随机数产生引擎,在ut测试时和tf算子传入相同的seed和seed2,但是计算得到的随机数不相同,导致结果数值不能匹配,但是三个输出的shape都匹配,请问这个问题该如何解决呢?【截图信息】下图是REG_OP(FractionMaxPool)下图是tensorflow官网的解释下图是tensorflow源码调用两个seed的过程下图是我调用C++随机数引擎的代码【日志信息】(可选,上传日志内容或者附件)
-
#include using namespace std;int main(){ int m=0,n,b; cin>>n; b=n; while(n!=0) { m=m*10+n%10;//这一步大家带上数算算看就会懂了 n=n/10; } cout<<m<<endl; return 0;}
-
#include<iostream> using namespace std;int main(){ int m=0,n,b; cin>>n; b=n; while(n!=0) { m=m*10+n%10;//这一步大家带上数算算看就会懂了 n=n/10; } cout<<m<<endl; return 0;}
-
在该课程中首先我们要具备一定的知识基础就像老师讲的很细心 但是没有基础的学起来也是有一定的困难 在语言方面应该对c和c++语言有一定的基础 ai cpu上也要有一定的知识基础 本节课程主要有五个章节环境准备:第一为环境准备 我们知道在一个项目或者一个软件在开发或者安装时最重要的就是环境所以没有环境来说对这个开发就毫无意义 首先要把基础打牢才能图长远aicpu实现:第二 前面我们提到过要有一定的c++基础为什么呢?在这里就会考一些基础的c++语法和c++的功底 熟话说就是用c++来实现AICPU算子算法如何实现算子的计算?就用到了具体的计算函数 名称为:Compute(CpuKernelContext&ctx)通俗点说这个函数搞明白了你的算子计算也就明白了我说的明不明白????切记在实现结束后:要用(算子类型,算子实现类名)来注册一下刚实现的算子 这样刚刚实现的算子才会有效哦!注意:编写的算子要尽可能的通用一些 兼容各种数据类型(不然会出现问题的哦!) 数据排布格式 形状等!算子ut测试第三:ut测试 学过软件工程的知道事单元测试 是在开发环境上测试我们编写的算子能否被正确编译以及测试算子的计算逻辑师范正确的一个过程在测试过程中 每一个测试用例所作的事情用伪码表示如下:创建算子实例 构造算子输入数据根据上一步的输入数据 用创建好的 标杆数据生成方法 计算一组正确的结果根据输入数据调用我们编写的算子计算一组实际的结果用正确的结果跟实际的结果比较 是否对应测试时首先要:创建测试用例实现 编写ut测试代码 定义一个create_nodedef宏实例化算子 在定义一个期望数据生成函数 或者一个期望数据生成脚本的场景过程定义具体测试用例(正确的测试用例和失败的测试用例)算子ST测试第四:st测试 st测试支持生成算子的st测试用例并在昇腾的硬件环境中执行 具体步骤如下:创建测试用例的配置文件 设置环境变量 用自动化工具执行st测试 这三步要牢记哦!开发完成后的操作第五:将开发后要合入&提交代码清晰的几步让昇腾的开发过程变得清晰易懂 而且老师讲课也极具魅力 希望有机会能多听听老师的课程
-
非static数据成员存在于类类型的每个对象中。static数据成员独立于该类的任意对象而存在。每个static数据成员是与类关联的对象,并不与该类的对象相关联。声明:在声明之前加上关键字static使用:使用 作用域运算符 :: 直接访问静态成员 r = Account::rate()可以使用类的对象或引用访问:r = ac1.rate()使用指针访问:r = ac2->rate()定义:在类的外部定义静态成员时,不能重复static关键字,该关键字只出现在类内部的声明语句。在类外部定义时不用加static。初始化:通常不在类的内部初始化,而是在定义时进行初始化,如:double Account::interestRate = initRate();如果一定要在类内定义,则要求必须是字面值常量类型的constexpr。练习
上滑加载中
推荐直播
-
GaussDB管理平台TPOPS,DBA高效运维的一站式解决方案
2024/12/24 周二 16:30-18:00
Leo 华为云数据库DTSE技术布道师
数据库的复杂运维,是否让你感到头疼不已?今天,华为云GaussDB管理平台将彻底来改观!本期直播,我们将深入探索GaussDB管理平台的TPOPS功能,带你感受一键式部署安装的便捷,和智能化运维管理的高效,让复杂的运维、管理变得简单,让简单变得可靠。
回顾中 -
DTT年度收官盛典:华为开发者空间大咖汇,共探云端开发创新
2025/01/08 周三 16:30-18:00
Yawei 华为云开发工具和效率首席专家 Edwin 华为开发者空间产品总监
数字化转型进程持续加速,驱动着技术革新发展,华为开发者空间如何巧妙整合鸿蒙、昇腾、鲲鹏等核心资源,打破平台间的壁垒,实现跨平台协同?在科技迅猛发展的今天,开发者们如何迅速把握机遇,实现高效、创新的技术突破?DTT 年度收官盛典,将与大家共同探索华为开发者空间的创新奥秘。
回顾中
热门标签