diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..ab1f4164 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/TinyInfiniTensor.iml b/.idea/TinyInfiniTensor.iml new file mode 100644 index 00000000..d6ebd480 --- /dev/null +++ b/.idea/TinyInfiniTensor.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..07115cdf --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..62de32f5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..67a1dcf4 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/include/core/allocator.h b/include/core/allocator.h index 002601d2..5867c79e 100644 --- a/include/core/allocator.h +++ b/include/core/allocator.h @@ -27,7 +27,7 @@ namespace infini { // TODO:可能需要设计一个数据结构来存储free block,以便于管理和合并 // HINT: 可以使用一个 map 来存储 free block,key 为 block 的起始/结尾地址,value 为 block 的大小 // =================================== 作业 =================================== - + std::map freeBlocksMap; public: Allocator(Runtime runtime); diff --git a/include/core/graph.h b/include/core/graph.h index c45580c2..4c534c8c 100644 --- a/include/core/graph.h +++ b/include/core/graph.h @@ -50,6 +50,7 @@ namespace infini * so the topological sorting fails. */ bool topo_sort(); + void reconstruct(Operator &op1, Operator &op2, Operator &op3); void optimize(); diff --git a/src/core/allocator.cc b/src/core/allocator.cc index ff593aef..80ff93c0 100644 --- a/src/core/allocator.cc +++ b/src/core/allocator.cc @@ -32,8 +32,31 @@ namespace infini // =================================== 作业 =================================== // TODO: 设计一个算法来分配内存,返回起始地址偏移量 // =================================== 作业 =================================== + used += size; + if(used > peak) + peak = used; - return 0; + if(freeBlocksMap.empty()){ + return peak - size; + } + + bool freeBlkOk = false; //是否存在足够大(>= size)的空闲内存块 + auto it = freeBlocksMap.begin(); + for(; it != freeBlocksMap.end(); it++){ + if(it->second >= size){ + freeBlkOk = true; + if(it->second > size) + freeBlocksMap.insert({it->first + size, it->second - size}); + break; + } + } + if(!freeBlkOk){ + it--; + size_t moreMemory = size - it->second; + peak += moreMemory; + } + freeBlocksMap.erase(it); + return it->first; } void Allocator::free(size_t addr, size_t size) @@ -44,6 +67,30 @@ namespace infini // =================================== 作业 =================================== // TODO: 设计一个算法来回收内存 // =================================== 作业 =================================== + auto it = freeBlocksMap.begin(); + int flag = 0; + for(; it != freeBlocksMap.end(); it++){ //遍历空闲内存块,检查是否能与待回收内存合并成一个大内存块 + if(it->first + it->second == addr){ //可以与待回收内存合并,该空闲块在前,待回收内存在后 + flag = -1; + break; + } + else if(it->first == addr + size){ //可以与待回收内存合并,待回收内存在前,该空闲块在后, + flag = 1; + break; + } + } + if(flag == -1){ + freeBlocksMap.insert({it->first, it->second + size}); //因为可以合并,插入合并后的大内存块,删去原来内存块 + freeBlocksMap.erase(it); + } + else if(flag == 0){ //标记为0,说明以上遍历时没有找到能合并的空闲块 + freeBlocksMap.insert({addr, size}); + } + else if(flag == 1){ + freeBlocksMap.insert({addr, size + it->second}); + freeBlocksMap.erase(it); + } + used -= size; } void *Allocator::getPtr() diff --git a/src/core/graph.cc b/src/core/graph.cc index 3a906370..92e0992d 100644 --- a/src/core/graph.cc +++ b/src/core/graph.cc @@ -2,10 +2,13 @@ #include #include #include +#include "operators/transpose.h" +#include "operators/matmul.h" +#include +#include namespace infini { - void GraphObj::addOperatorAndConnect(const Operator &op) { sorted = false; @@ -98,15 +101,128 @@ namespace infini return this->sorted = true; } - void GraphObj::optimize() - { + bool transposeOpsCancel(vector a, vector b){ + if(a.size() != b.size()) + return false; + for(int i = 0; i < (int)a.size(); i++){ + if(b[a[i]] != i) + return false; + } + return true; + } + + bool transOpCanIntegrateToMatmul(vector perm){ + int size = perm.size(); + for(int i = 0; i < size - 2; i++){ + if(perm[i] != i) + return false; + } + if(perm[size - 2] != size - 1 || perm[size - 1] != size - 2) + return false; + else + return true; + } + + void GraphObj::reconstruct(Operator &op1, Operator &op2, Operator &op3){ + Tensor input = op1->getInputs(0); + if(input) {input->addTarget(op3);} + if(op2 == nullptr){ + op3->replaceInput(op1->getOutput(), input); + op1->removeSuccessors(op3); + op3->removePredecessors(op1); + op1->getOutput()->removeTarget(op3); + } + else{ + op3->replaceInput(op2->getOutput(), input); + op3->removePredecessors(op2); + op1->removeSuccessors(op2); + op1->getOutput()->removeTarget(op2); + } + for(auto &pred: op1->getPredecessors()){ + op3->addPredecessors(pred); + pred->addSuccessors(op3); + } + } + +void GraphObj::optimize() +{ // =================================== 作业 =================================== // TODO: 设计一个算法来实现指定的图优化规则 // 图优化规则如下: // 1. 去除冗余的算子(例如,两个相邻的算子都是 transpose 算子,且做的是相反的操作,可以将其全部删除) // 2. 合并算子(例如,矩阵乘算子中含有属性transA、transB,如果其输入存在transpose,且对最后两个维度做交换,就可以将transpose融入到矩阵乘算子的属性中去) // =================================== 作业 =================================== + std::unordered_set toDelete; //待删除的算子 + std::shared_ptr emptyPtr; //有的情况下重构计算图时需要 + bool modified = true; + while(modified){ + modified = false; + for(auto &op: ops){ + if(toDelete.find(op.get()) != toDelete.end()){ //如果遍历到之前发现的待删除算子,略过 + continue; + } + OpType opType = op->getOpType(); + if(opType == OpType::Transpose){ + TransposeObj* transOp = dynamic_cast(op.get()); + Tensor input = op->getInputs(0); + for(auto &succ: op->getSuccessors()){ + if(succ->getOpType() == OpType::Transpose){ //优化情况1 去除冗余transpose算子:当两个相邻算子都是transpose且相反操作 + TransposeObj* transSucc = dynamic_cast(succ.get()); + bool cancelOut = false; //判断两个相邻transpose算子是否相反的操作,在transposeOpsCancel函数中进行 + if(transOp && transSucc){ + cancelOut = transposeOpsCancel(transOp->getPermute(), transSucc->getPermute()); + } + if(cancelOut){ + toDelete.insert(succ.get()); + removeTensor(succ->getOutput()); + for(auto &succ_succ: succ->getSuccessors()){ + reconstruct(op, succ, succ_succ); + } + if(op->getSuccessors().size() == 0){ //如果第1个transpose只有一个后继算子(即第2个transpose),那么可以删除第1个transpose + toDelete.insert(op.get()); + //删除第1个transpose时需要进行以下重构 + removeTensor(op->getOutput()); + if(input) {input->removeTarget(op);} + for(auto &pred: op->getPredecessors()) + pred->removeSuccessors(op); + } + modified = true; + } + } + else if(succ->getOpType() == OpType::MatMul){ //优化情况2 合并transpose与MatMul:当MatMul算子中含有属性transA、transB,且transpose对最后两个维度做交换 + bool ok = transOpCanIntegrateToMatmul(transOp->getPermute()); //判断能否合并 + if(ok){ //即使能够合并,也不一定就能删除transpose,除非transpose只有matMul唯一一个后继结点 + MatmulObj* matmulSucc = dynamic_cast(succ.get()); + if (matmulSucc){ + if(succ->getInputs(0)->getGuid() == op->getOutput()->getGuid()) + matmulSucc->setTransA(!(matmulSucc->getTransA())); + else + matmulSucc->setTransB(!(matmulSucc->getTransB())); + } + reconstruct(op, emptyPtr, succ); //合并transpose与matMul时需要进行重构:将transpose的输入作为matMul的输入 + if(op->getSuccessors().size() == 0){ //如果transpose只有一个后继算子(即matMul)那么可以删除transpose。 size()==0是因为前面一步重构时已删去了两算子间的边 + toDelete.insert(op.get()); + removeTensor(op->getOutput()); + if(input) {input->removeTarget(op);} + for(auto &pred: op->getPredecessors()) + pred->removeSuccessors(op); + } + modified = true; + } + } + } + } + } + } + //删除之前遍历计算图时发现的冗余算子 + for(int i = 0; i < (int)ops.size();){ + if(toDelete.find(ops[i].get()) != toDelete.end()) + ops.erase(ops.begin() + i); + else + i++; } + this->sorted = false; +} Tensor GraphObj::getTensor(int fuid) const { @@ -152,10 +268,50 @@ namespace infini // TODO:利用 allocator 给计算图分配内存 // HINT: 获取分配好的内存指针后,可以调用 tensor 的 setDataBlob 函数给 tensor 绑定内存 // =================================== 作业 =================================== + std::unordered_map tensorOffset; //tensor在内存中的偏移量。 tensor id作为key,偏移量作为value + std::unordered_map tensorRefNum; //tensor被引用次数,即被多少个算子使用。tensor id作为key,被引用次数作为value + //记录每个张量的被引用次数 + for(auto &tensor: tensors){ + tensorRefNum[tensor->getFuid()] = static_cast(tensor->getTargets().size()); + } + //为input分配内存 + for(auto &g_input : getInputs()){ + tensorOffset[g_input->getFuid()] = allocator.alloc(g_input->getBytes()); //if(!tensor->getSource()) + } + //遍历算子 + for(auto &op: ops){ + for(auto &output: op->getOutputs()){ + int id = output->getFuid(); + if(tensorOffset.find(id) == tensorOffset.end()) //确保不会重复分配 + tensorOffset[id] = allocator.alloc(output->getBytes()); + } + for(auto &input: op->getInputs()){ + int id = input->getFuid(); + if(tensorRefNum.find(id) != tensorRefNum.end()){ + tensorRefNum[id]--; + if(tensorRefNum[id] == 0){ //如果一个张量不再被使用((被引用次数是0),可以释放其内存 + auto it = tensorOffset.find(id); //释放前找到它在内存中的地址(偏移量) + if(it != tensorOffset.end()){ + allocator.free(tensorOffset[id], input->getBytes()); + //tensorOffset.erase(it); + } + tensorRefNum.erase(id); + } + } + } + } + void *memPtr = allocator.getPtr(); + for(auto &tensor: tensors){ + auto it = tensorOffset.find(tensor->getFuid()); + IT_ASSERT(it != tensorOffset.end()); + void *tensorPtr = static_cast(static_cast(memPtr) + it->second); + tensor->setDataBlob(make_ref(runtime, tensorPtr)); + } allocator.info(); } + Tensor GraphObj::addTensor(Shape dim, DataType dtype) { return tensors.emplace_back(make_ref(dim, dtype, runtime)); diff --git a/src/operators/concat.cc b/src/operators/concat.cc index d1963308..7408414f 100644 --- a/src/operators/concat.cc +++ b/src/operators/concat.cc @@ -17,7 +17,10 @@ optional> ConcatObj::inferShape(const TensorVec &inputs) { // TODO:修改 dims,返回正确的 concat 后的 shape // REF: https://onnx.ai/onnx/operators/onnx__Concat.html#concat-13 // =================================== 作业 =================================== - + int n = inputs.size(); + for(int i = 1; i < n; i++){ + dims[dim] += inputs[i]->getDims()[dim]; + } return {{dims}}; } diff --git a/src/operators/matmul.cc b/src/operators/matmul.cc index 7a16ca27..b57063a8 100644 --- a/src/operators/matmul.cc +++ b/src/operators/matmul.cc @@ -27,7 +27,27 @@ namespace infini // TODO:返回经过 matmul 操作后的 shape // REF: https://github.com/onnx/onnx/blob/main/docs/Operators.md#gemm // =================================== 作业 =================================== - return std::nullopt; + const auto A = inputs[0]; + auto a_dim = A->getDims(); + const auto B = inputs[1]; + auto b_dim = B->getDims(); + int size = a_dim.size(); + Shape res = a_dim; + //multi-dimensional broadcasting except for the last two dimensions + for(int i = 0; i < size - 2; i++){ + if(a_dim[i] < b_dim[i]) + res[i] = b_dim[i]; + } + + if(transA) + res[size - 2] = a_dim[size - 1]; + if(transB) + res[size - 1] = b_dim[size - 2]; + else + res[size - 1] = b_dim[size - 1]; + + std::vector vec {res}; + return vec; } } // namespace infini \ No newline at end of file diff --git a/src/operators/transpose.cc b/src/operators/transpose.cc index faab2b69..31a009f1 100644 --- a/src/operators/transpose.cc +++ b/src/operators/transpose.cc @@ -33,8 +33,11 @@ namespace infini // TODO:修改 output_dim,返回正确的 transpose 后的 shape // REF: https://onnx.ai/onnx/operators/onnx__Transpose.html#transpose-21 // =================================== 作业 =================================== - - return std::nullopt; + for (int i = 0; i < rank; i++){ + output_dim[i] = input_dim[transposePermute[i]]; + } + std::vector res {output_dim}; + return res; } std::string TransposeObj::toString() const diff --git a/src/operators/unary.cc b/src/operators/unary.cc index 3daad361..c7a8c928 100644 --- a/src/operators/unary.cc +++ b/src/operators/unary.cc @@ -39,7 +39,8 @@ namespace infini // TODO:返回经过 clip 操作后的 shape // REF: https://onnx.ai/onnx/operators/onnx__Clip.html#clip-13 // =================================== 作业 =================================== - return std::nullopt; + const auto A = inputs[0]; + return {{A->getDims()}}; } std::string ClipObj::toString() const @@ -66,7 +67,10 @@ namespace infini // REF_FILE: src/core/operator.cc // REF: https://onnx.ai/onnx/operators/onnx__Cast.html#cast-21 // =================================== 作业 =================================== - return {}; + DataType outputType = getOutputDataType(); + int num = inputs.size(); + std::vector res(num, outputType); + return res; } optional> CastObj::inferShape(const TensorVec &inputs) @@ -75,7 +79,8 @@ namespace infini // TODO:返回经过 cast 操作后的 shape // REF: https://onnx.ai/onnx/operators/onnx__Cast.html#cast-21 // =================================== 作业 =================================== - return std::nullopt; + const auto A = inputs[0]; + return {{A->getDims()}}; } std::string CastObj::toString() const diff --git a/src/utils/operator_utils.cc b/src/utils/operator_utils.cc index edbd2c82..37eab0d8 100644 --- a/src/utils/operator_utils.cc +++ b/src/utils/operator_utils.cc @@ -9,8 +9,32 @@ Shape infer_broadcast(const Shape &A, const Shape &B) { // TODO:对 A 和 B 进行双向广播,返回广播后的形状。 // REF: https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md // =================================== 作业 =================================== - - return {}; + Shape res; + long unsigned int i = 0; + if(A.size() > B.size()){ + long unsigned int diff = A.size() - B.size(); + for(; i < diff; i++) + res.push_back(A[i]); + for(; i < A.size(); i++){ + if(A[i] == 1) + res.push_back(B[i-diff]); + else + res.push_back(A[i]); + } + } + else{ + long unsigned int diff = B.size() - A.size(); + for(; i < diff; i++) + res.push_back(B[i]); + for(; i < B.size(); i++){ + if(B[i] == 1) + res.push_back(A[i-diff]); + else + res.push_back(B[i]); + } + } + + return res; } int get_real_axis(const int &axis, const int &rank) { diff --git a/test/kernels/nativecpu/test_nativecpu_concat.cc b/test/kernels/nativecpu/test_nativecpu_concat.cc index fc87fb19..c1b0fa81 100644 --- a/test/kernels/nativecpu/test_nativecpu_concat.cc +++ b/test/kernels/nativecpu/test_nativecpu_concat.cc @@ -18,7 +18,6 @@ TEST(Concat, NativeCpu) { t1->setData(IncrementalGenerator()); t2->setData(OneGenerator()); t3->setData(OneGenerator()); - runtime->run(g); EXPECT_TRUE(op->getOutput()->equalData( vector{0, 1, 2, 1, 1, 1, 3, 4, 5, 1, 1, 1,