forked from jiuyuan/InfiniTensor
Tensor hash and inferShape (#4)
* Refactor: operator hash and inferShape * Add: hash without shape * Add: inferShape interface for given input tensors * Add: construct outputs in op ctor * Add: comments for matmul * Add: opType in AttrVector and WorkloadVector * Chore: _graph -> graph in Op ctor * Chore: change the "Node" suffix to "Obj" Co-authored-by: Liyan Zheng <liyan-zheng@outlook.com>
This commit is contained in:
parent
eda41b06a7
commit
a26890abce
|
@ -16,6 +16,7 @@
|
||||||
namespace infini {
|
namespace infini {
|
||||||
using std::list;
|
using std::list;
|
||||||
using std::map;
|
using std::map;
|
||||||
|
using std::optional;
|
||||||
using std::pair;
|
using std::pair;
|
||||||
using std::set;
|
using std::set;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
@ -27,7 +28,7 @@ using std::vector;
|
||||||
|
|
||||||
// Aliases
|
// Aliases
|
||||||
using dtype = float;
|
using dtype = float;
|
||||||
using HashType = size_t; // compatible with std::hash
|
using HashType = uint64_t; // compatible with std::hash
|
||||||
|
|
||||||
// Metaprogramming utilities
|
// Metaprogramming utilities
|
||||||
#define _CAT(A, B) A##B
|
#define _CAT(A, B) A##B
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
// TODO: graph should be attached to a context
|
// TODO: graph should be attached to a context
|
||||||
class GraphNode : public Object {
|
class GraphObj : public Object {
|
||||||
protected:
|
protected:
|
||||||
TensorVec tensors;
|
TensorVec tensors;
|
||||||
TensorVec inputs;
|
TensorVec inputs;
|
||||||
|
@ -16,7 +16,28 @@ class GraphNode : public Object {
|
||||||
// Graph(OpVec oplist);
|
// Graph(OpVec oplist);
|
||||||
string toString() const override;
|
string toString() const override;
|
||||||
|
|
||||||
void addOp(Operator op) { ops.push_back(op); };
|
Tensor addTensor(Shape dim, DataType dtype = DataType::Int32);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add an operator and create its outputs. Output tensor arguments
|
||||||
|
* should be empty Refs (e.g., nullptr).
|
||||||
|
*/
|
||||||
|
template <typename T, typename... Args> Ref<T> addOp(Args &&...args) {
|
||||||
|
Ref<T> op = make_ref<T>(this, std::forward<Args>(args)...);
|
||||||
|
ops.push_back(op);
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add an operator with its outputs specified.
|
||||||
|
*/
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
Ref<T> addOpWithOutputs(Args &&...args) {
|
||||||
|
Ref<T> op = make_ref<T>(nullptr, std::forward<Args>(args)...);
|
||||||
|
ops.push_back(op);
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
const TensorVec &getTensors() const { return tensors; }
|
const TensorVec &getTensors() const { return tensors; }
|
||||||
const TensorVec &getInputs() const { return inputs; }
|
const TensorVec &getInputs() const { return inputs; }
|
||||||
const TensorVec &getOutputs() const { return outputs; }
|
const TensorVec &getOutputs() const { return outputs; }
|
||||||
|
@ -24,12 +45,6 @@ class GraphNode : public Object {
|
||||||
// TensorVec &getInputs();
|
// TensorVec &getInputs();
|
||||||
// TensorVec &getOutputs();
|
// TensorVec &getOutputs();
|
||||||
|
|
||||||
Tensor addTensor(Shape dim, DataType dtype = DataType::Int32) {
|
|
||||||
Tensor tensor = make_ref<TensorNode>(dim, dtype);
|
|
||||||
tensors.emplace_back(tensor);
|
|
||||||
return tensor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dataMalloc();
|
void dataMalloc();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "core/common.h"
|
||||||
|
|
||||||
|
namespace infini {
|
||||||
|
|
||||||
|
inline HashType hashAppend(HashType a, HashType b) {
|
||||||
|
return (a * 10000019 + b * 10000079) % 2147483647;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inline HashType hashPack(HashType x) { return (x * 10000103) % 2147483647; }
|
||||||
|
|
||||||
|
template <typename T> inline HashType hashVector(const vector<T> &vec) {
|
||||||
|
HashType ret = 0;
|
||||||
|
for (auto v : vec)
|
||||||
|
ret = hashAppend(ret, v);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace infini
|
|
@ -127,9 +127,7 @@ struct OpPerfKey {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class OperatorNode : public Object {
|
class OperatorObj : public Object {
|
||||||
friend class Kernel;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
OpType type;
|
OpType type;
|
||||||
TensorVec inputs;
|
TensorVec inputs;
|
||||||
|
@ -138,10 +136,24 @@ class OperatorNode : public Object {
|
||||||
// vector<WRef<Operator>> successors;
|
// vector<WRef<Operator>> successors;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OperatorNode(OpType opType, TensorVec inputs, TensorVec outputs)
|
OperatorObj(OpType opType, TensorVec inputs, TensorVec outputs)
|
||||||
: type(opType), inputs(inputs), outputs(outputs) {}
|
: type(opType), inputs(inputs), outputs(outputs) {}
|
||||||
virtual vector<Shape> computeShape() const = 0;
|
virtual optional<vector<Shape>>
|
||||||
virtual OpPerfKey getOpPerfKey() const = 0;
|
inferShape(const TensorVec &inputs) const = 0;
|
||||||
|
/**
|
||||||
|
* @brief Constructs outputs (if requried) and check whether the operator is
|
||||||
|
* valid.
|
||||||
|
*
|
||||||
|
* @param graph If graph is not nullptr, outputs should be created in this
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
bool checkValid(GraphObj *graph);
|
||||||
|
OpPerfKey getOpPerfKey() const;
|
||||||
|
/**
|
||||||
|
* @brief Hash operator attributes. Input and output shapes are not
|
||||||
|
* considered.
|
||||||
|
*/
|
||||||
|
HashType hash() const;
|
||||||
|
|
||||||
public: // check Op type
|
public: // check Op type
|
||||||
bool isLinearOp() const;
|
bool isLinearOp() const;
|
||||||
|
@ -167,8 +179,22 @@ class OperatorNode : public Object {
|
||||||
|
|
||||||
virtual int numInputs() const = 0;
|
virtual int numInputs() const = 0;
|
||||||
virtual int numOutputs() const = 0;
|
virtual int numOutputs() const = 0;
|
||||||
virtual HashType hash() const { IT_TODO_HALT(); }
|
|
||||||
virtual HashType hashWithShape() const { IT_TODO_HALT(); }
|
protected:
|
||||||
|
optional<vector<Shape>> inferShape() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief The returned vector includes operator attributes, such as paddings
|
||||||
|
* in Conv and transpose in Matmul. However, the input and output shapes are
|
||||||
|
* not taken into consideration.
|
||||||
|
*/
|
||||||
|
virtual vector<int> getOpAttrVector() const { IT_TODO_HALT(); }
|
||||||
|
/**
|
||||||
|
* @brief Besides operator attributes, the returned vector includes input
|
||||||
|
* and output shapes.
|
||||||
|
*/
|
||||||
|
virtual vector<int> getWorkloadVector() const { IT_TODO_HALT(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace infini
|
} // namespace infini
|
||||||
|
|
|
@ -6,13 +6,13 @@ namespace infini {
|
||||||
// TODO: how to deal with this
|
// TODO: how to deal with this
|
||||||
using ShapeElem = int;
|
using ShapeElem = int;
|
||||||
using Shape = vector<ShapeElem>;
|
using Shape = vector<ShapeElem>;
|
||||||
class TensorNode : public TensorBaseNode {
|
class TensorObj : public TensorBaseObj {
|
||||||
private:
|
private:
|
||||||
Shape shape;
|
Shape shape;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TensorNode(const Shape &shape, DataType dtype);
|
TensorObj(const Shape &shape, DataType dtype);
|
||||||
virtual ~TensorNode() {}
|
virtual ~TensorObj() {}
|
||||||
string toString() const override;
|
string toString() const override;
|
||||||
|
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
|
@ -21,7 +21,7 @@ class TensorNode : public TensorBaseNode {
|
||||||
Shape getDims() const { return shape; }
|
Shape getDims() const { return shape; }
|
||||||
|
|
||||||
size_t getOffset(const Shape &ds) const;
|
size_t getOffset(const Shape &ds) const;
|
||||||
using TensorBaseNode::getData;
|
using TensorBaseObj::getData;
|
||||||
VType getData(const Shape &pos) const;
|
VType getData(const Shape &pos) const;
|
||||||
void copyData(VType *dptr);
|
void copyData(VType *dptr);
|
||||||
void printData() const;
|
void printData() const;
|
||||||
|
|
|
@ -5,15 +5,15 @@
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
// class Tensor;
|
// class Tensor;
|
||||||
class TensorBaseNode;
|
class TensorBaseObj;
|
||||||
class TensorNode;
|
class TensorObj;
|
||||||
class OperatorNode;
|
class OperatorObj;
|
||||||
class GraphNode;
|
class GraphObj;
|
||||||
|
|
||||||
using TensorBase = Ref<TensorBaseNode>;
|
using TensorBase = Ref<TensorBaseObj>;
|
||||||
using Tensor = Ref<TensorNode>;
|
using Tensor = Ref<TensorObj>;
|
||||||
using Operator = Ref<OperatorNode>;
|
using Operator = Ref<OperatorObj>;
|
||||||
using Graph = Ref<GraphNode>;
|
using Graph = Ref<GraphObj>;
|
||||||
|
|
||||||
using TensorVec = vector<Tensor>;
|
using TensorVec = vector<Tensor>;
|
||||||
using OpVec = vector<Operator>;
|
using OpVec = vector<Operator>;
|
||||||
|
@ -25,7 +25,7 @@ enum class DataType {
|
||||||
Int32,
|
Int32,
|
||||||
};
|
};
|
||||||
|
|
||||||
class TensorBaseNode : public Object {
|
class TensorBaseObj : public Object {
|
||||||
public:
|
public:
|
||||||
// enum TensorType {
|
// enum TensorType {
|
||||||
// Input,
|
// Input,
|
||||||
|
@ -38,8 +38,8 @@ class TensorBaseNode : public Object {
|
||||||
int dim;
|
int dim;
|
||||||
|
|
||||||
DataType dtype;
|
DataType dtype;
|
||||||
vector<WRef<TensorBaseNode>> inputOf;
|
vector<WRef<TensorBaseObj>> inputOf;
|
||||||
WRef<TensorBaseNode> outputOf;
|
WRef<TensorBaseObj> outputOf;
|
||||||
// TODO: Ref<void> -> Ref<Blob>
|
// TODO: Ref<void> -> Ref<Blob>
|
||||||
Ref<VType[]> data;
|
Ref<VType[]> data;
|
||||||
// ComputeState computed;
|
// ComputeState computed;
|
||||||
|
@ -47,8 +47,8 @@ class TensorBaseNode : public Object {
|
||||||
// static bool random_inited;
|
// static bool random_inited;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TensorBaseNode(int dim, DataType dtype);
|
TensorBaseObj(int dim, DataType dtype);
|
||||||
virtual ~TensorBaseNode() {}
|
virtual ~TensorBaseObj() {}
|
||||||
|
|
||||||
Ref<VType[]> getDataPtr() const { return data; }
|
Ref<VType[]> getDataPtr() const { return data; }
|
||||||
VType getData(size_t offset) const;
|
VType getData(size_t offset) const;
|
||||||
|
|
|
@ -3,26 +3,37 @@
|
||||||
|
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
class MatmulNode : public OperatorNode {
|
class MatmulObj : public OperatorObj {
|
||||||
private:
|
private:
|
||||||
// InfiniTensor assume a row-major tensor layout. transA=false means default
|
// InfiniTensor assumes a row-major tensor layout. `transA`=false means
|
||||||
// dims, true means A should be transposed before matmul. This is in
|
// default dims, true means A should be transposed before matmul. This is in
|
||||||
// oppsite to column-major BLAS.
|
// oppsite to the column-major BLAS.
|
||||||
bool transA, transB;
|
bool transA, transB;
|
||||||
ActType act;
|
ActType act;
|
||||||
|
|
||||||
// Auxiliary attributes
|
// Auxiliary attributes which are not a part of operator attributes.
|
||||||
int b, m, n, k;
|
int b, m, n, k;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MatmulNode(Tensor A, Tensor B, Tensor C, bool transA = false,
|
/**
|
||||||
bool transB = false, Tensor bias = nullptr,
|
* @brief This comments show how operators is defined in InfiniTensor. The
|
||||||
ActType act = ActType::None);
|
* constructor can create output tensors for the operator or not, which
|
||||||
|
* depends on `graph`.
|
||||||
|
*
|
||||||
|
* @param graph If graph is not empty, create outputs in the constructor.
|
||||||
|
* Otherwise, check the provided shape with the results of `inferShape` in
|
||||||
|
* `checkValid`.
|
||||||
|
* @param C C is the output of Matmul. If outputs are going to be created in
|
||||||
|
* the constructor, C should be an empty Ref.
|
||||||
|
*/
|
||||||
|
MatmulObj(GraphObj *graph, Tensor A, Tensor B, Tensor C,
|
||||||
|
bool transA = false, bool transB = false, Tensor bias = nullptr,
|
||||||
|
ActType act = ActType::None);
|
||||||
|
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
vector<Shape> computeShape() const override;
|
optional<vector<Shape>> inferShape(const TensorVec &inputs) const override;
|
||||||
|
|
||||||
int numInputs() const override { return 2; }
|
int numInputs() const override { return 3; }
|
||||||
int numOutputs() const override { return 1; }
|
int numOutputs() const override { return 1; }
|
||||||
|
|
||||||
Tensor getBias() const { return inputs[2]; }
|
Tensor getBias() const { return inputs[2]; }
|
||||||
|
@ -34,14 +45,9 @@ class MatmulNode : public OperatorNode {
|
||||||
int getN() const { return n; }
|
int getN() const { return n; }
|
||||||
int getK() const { return k; }
|
int getK() const { return k; }
|
||||||
|
|
||||||
HashType hashWithShape() const override;
|
|
||||||
OpPerfKey getOpPerfKey() const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Q: whether to check the output? Since we can build an Op first and then
|
vector<int> getWorkloadVector() const override;
|
||||||
// assure output.
|
vector<int> getOpAttrVector() const override;
|
||||||
// Fix 1: make shape inference a static method. But OpPerfKey are required.
|
|
||||||
bool checkValid(const TensorVec &inputs) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace infini
|
} // namespace infini
|
||||||
|
|
|
@ -2,19 +2,25 @@
|
||||||
|
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
void GraphNode::updateConnection() { IT_TODO_HALT(); }
|
void GraphObj::updateConnection() { IT_TODO_HALT(); }
|
||||||
|
|
||||||
string GraphNode::toString() const {
|
string GraphObj::toString() const {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "GraphNode operators:\n";
|
oss << "Graph operators:\n";
|
||||||
for (const auto &op : ops)
|
for (const auto &op : ops)
|
||||||
oss << op << "\n";
|
oss << op << "\n";
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphNode::dataMalloc() {
|
void GraphObj::dataMalloc() {
|
||||||
for (auto &tensor : tensors)
|
for (auto &tensor : tensors)
|
||||||
tensor->dataMalloc();
|
tensor->dataMalloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tensor GraphObj::addTensor(Shape dim, DataType dtype) {
|
||||||
|
Tensor tensor = make_ref<TensorObj>(dim, dtype);
|
||||||
|
tensors.emplace_back(tensor);
|
||||||
|
return tensor;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace infini
|
} // namespace infini
|
|
@ -1,32 +1,77 @@
|
||||||
#include "core/operator.h"
|
#include "core/operator.h"
|
||||||
|
#include "core/graph.h"
|
||||||
|
#include "core/hash.h"
|
||||||
|
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
bool OperatorNode::isLinearOp() const {
|
bool OperatorObj::isLinearOp() const {
|
||||||
return enum_to_underlying(type) >= 100 && enum_to_underlying(type) < 200;
|
return enum_to_underlying(type) >= 100 && enum_to_underlying(type) < 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OperatorNode::isElementWiseOp() const {
|
bool OperatorObj::isElementWiseOp() const {
|
||||||
return enum_to_underlying(type) >= 200 && enum_to_underlying(type) < 300;
|
return enum_to_underlying(type) >= 200 && enum_to_underlying(type) < 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OperatorNode::isSplitOp() const { return type == OpType::Split; }
|
bool OperatorObj::isSplitOp() const { return type == OpType::Split; }
|
||||||
|
|
||||||
bool OperatorNode::isConcatOp() const { return type == OpType::Concat; }
|
bool OperatorObj::isConcatOp() const { return type == OpType::Concat; }
|
||||||
|
|
||||||
bool OperatorNode::isComputeOp() const {
|
bool OperatorObj::isComputeOp() const {
|
||||||
return type == OpType::Conv || type == OpType::Matmul ||
|
return type == OpType::Conv || type == OpType::Matmul ||
|
||||||
type == OpType::ConvTrans || type == OpType::G2BMM ||
|
type == OpType::ConvTrans || type == OpType::G2BMM ||
|
||||||
type == OpType::GBMML;
|
type == OpType::GBMML;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OperatorNode::isTransposeOp() const { return type == OpType::Transpose; }
|
bool OperatorObj::isTransposeOp() const { return type == OpType::Transpose; }
|
||||||
|
|
||||||
bool OperatorNode::isReshapeOp() const { return type == OpType::Reshape; }
|
bool OperatorObj::isReshapeOp() const { return type == OpType::Reshape; }
|
||||||
|
|
||||||
bool OperatorNode::isMemBoundOp() const {
|
bool OperatorObj::isMemBoundOp() const {
|
||||||
return type == OpType::MemBound || type == OpType::Activation ||
|
return type == OpType::MemBound || type == OpType::Activation ||
|
||||||
type == OpType::Transpose;
|
type == OpType::Transpose;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpPerfKey OperatorObj::getOpPerfKey() const {
|
||||||
|
auto workloadVector = getWorkloadVector();
|
||||||
|
// Calculate hash of workload, i.e. hash with shape. This is different from
|
||||||
|
// Operator::hash, which hashes operator attributes and ignores tensor
|
||||||
|
// shapes.
|
||||||
|
HashType hash = 0;
|
||||||
|
hash = hashAppend(hash, enum_to_underlying(type));
|
||||||
|
hash = hashAppend(hash, hashVector(workloadVector));
|
||||||
|
return OpPerfKey(hash, type, workloadVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashType OperatorObj::hash() const {
|
||||||
|
HashType hash = 0;
|
||||||
|
hash = hashAppend(hash, enum_to_underlying(type));
|
||||||
|
hash = hashAppend(hash, hashVector(getOpAttrVector()));
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OperatorObj::checkValid(GraphObj *graph) {
|
||||||
|
auto optShapes = inferShape();
|
||||||
|
if (!optShapes) // shape inference failed
|
||||||
|
return false;
|
||||||
|
const vector<Shape> &shapes = *optShapes;
|
||||||
|
if (shapes.size() != outputs.size())
|
||||||
|
return false;
|
||||||
|
if (graph) { // if graph != nullptr, outputs should be created
|
||||||
|
for (size_t i = 0; i < outputs.size(); i++) {
|
||||||
|
IT_ASSERT(!outputs[i]);
|
||||||
|
outputs[i] = graph->addTensor(shapes[i]);
|
||||||
|
}
|
||||||
|
} else { // if graph is not empty, check outputs match inferred shapes
|
||||||
|
for (size_t i = 0; i < shapes.size(); ++i) {
|
||||||
|
if (shapes[i] != outputs[i]->getDims())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<vector<Shape>> OperatorObj::inferShape() const {
|
||||||
|
return inferShape(inputs);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace infini
|
} // namespace infini
|
|
@ -1,24 +1,22 @@
|
||||||
#include <core/tensor.h>
|
#include <core/tensor.h>
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
TensorNode::TensorNode(const Shape &shape, DataType dtype)
|
TensorObj::TensorObj(const Shape &shape, DataType dtype)
|
||||||
: TensorBaseNode(shape.size(), dtype), shape(shape) {}
|
: TensorBaseObj(shape.size(), dtype), shape(shape) {}
|
||||||
|
|
||||||
void TensorNode::dataMalloc() {
|
void TensorObj::dataMalloc() {
|
||||||
IT_ASSERT(data == nullptr);
|
IT_ASSERT(data == nullptr);
|
||||||
// initialized to zero
|
// initialized to zero
|
||||||
data.reset(reinterpret_cast<VType *>(calloc(size(), sizeof(VType))));
|
data.reset(reinterpret_cast<VType *>(calloc(size(), sizeof(VType))));
|
||||||
}
|
}
|
||||||
|
|
||||||
VType TensorNode::getData(const Shape &pos) const {
|
VType TensorObj::getData(const Shape &pos) const {
|
||||||
return getData(getOffset(pos));
|
return getData(getOffset(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
string TensorNode::toString() const {
|
string TensorObj::toString() const { return "Tensor " + std::to_string(guid); }
|
||||||
return "TensorNode " + std::to_string(guid);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TensorNode::getOffset(const Shape &pos) const {
|
size_t TensorObj::getOffset(const Shape &pos) const {
|
||||||
auto nDim = pos.size();
|
auto nDim = pos.size();
|
||||||
IT_ASSERT(shape.size() == nDim);
|
IT_ASSERT(shape.size() == nDim);
|
||||||
if (pos.empty())
|
if (pos.empty())
|
||||||
|
@ -32,14 +30,14 @@ size_t TensorNode::getOffset(const Shape &pos) const {
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TensorNode::size() const {
|
size_t TensorObj::size() const {
|
||||||
size_t ret = 1;
|
size_t ret = 1;
|
||||||
for (const auto &d : shape)
|
for (const auto &d : shape)
|
||||||
ret *= d;
|
ret *= d;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TensorNode::copyData(VType *dptr) {
|
void TensorObj::copyData(VType *dptr) {
|
||||||
IT_ASSERT(data != nullptr);
|
IT_ASSERT(data != nullptr);
|
||||||
size_t sz = size();
|
size_t sz = size();
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
|
@ -48,7 +46,7 @@ void TensorNode::copyData(VType *dptr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TensorNode::printData() const {
|
void TensorObj::printData() const {
|
||||||
IT_ASSERT(data != nullptr);
|
IT_ASSERT(data != nullptr);
|
||||||
std::cout << "Tensor: " << guid << std::endl;
|
std::cout << "Tensor: " << guid << std::endl;
|
||||||
auto numDims = shape.size();
|
auto numDims = shape.size();
|
||||||
|
@ -75,7 +73,7 @@ void TensorNode::printData() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TensorNode::equalData(const Tensor &rhs) const {
|
bool TensorObj::equalData(const Tensor &rhs) const {
|
||||||
IT_ASSERT(data != nullptr);
|
IT_ASSERT(data != nullptr);
|
||||||
IT_ASSERT(rhs->data != nullptr);
|
IT_ASSERT(rhs->data != nullptr);
|
||||||
if (shape != rhs->getDims())
|
if (shape != rhs->getDims())
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include <core/tensor_base.h>
|
#include <core/tensor_base.h>
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
TensorBaseNode::TensorBaseNode(int dim, DataType dtype)
|
TensorBaseObj::TensorBaseObj(int dim, DataType dtype)
|
||||||
: dim(dim), dtype(dtype) {}
|
: dim(dim), dtype(dtype) {}
|
||||||
|
|
||||||
VType TensorBaseNode::getData(size_t offset) const { return data[offset]; }
|
VType TensorBaseObj::getData(size_t offset) const { return data[offset]; }
|
||||||
|
|
||||||
}; // namespace infini
|
}; // namespace infini
|
|
@ -5,7 +5,7 @@ namespace infini {
|
||||||
|
|
||||||
template <typename T> class NaiveMatmul : public Kernel {
|
template <typename T> class NaiveMatmul : public Kernel {
|
||||||
void compute(const Operator &_op, const PerfRecord &record) const override {
|
void compute(const Operator &_op, const PerfRecord &record) const override {
|
||||||
auto op = as<MatmulNode>(_op);
|
auto op = as<MatmulObj>(_op);
|
||||||
T *A = reinterpret_cast<T *>(op->getInputs(0)->getDataPtr().get());
|
T *A = reinterpret_cast<T *>(op->getInputs(0)->getDataPtr().get());
|
||||||
T *B = reinterpret_cast<T *>(op->getInputs(1)->getDataPtr().get());
|
T *B = reinterpret_cast<T *>(op->getInputs(1)->getDataPtr().get());
|
||||||
T *C = reinterpret_cast<T *>(op->getOutput()->getDataPtr().get());
|
T *C = reinterpret_cast<T *>(op->getOutput()->getDataPtr().get());
|
||||||
|
|
|
@ -2,19 +2,17 @@
|
||||||
|
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
vector<Shape> MatmulNode::computeShape() const { return {{b, m, n}}; }
|
MatmulObj::MatmulObj(GraphObj *graph, Tensor A, Tensor B, Tensor C, bool transA,
|
||||||
|
bool transB, Tensor bias, ActType act)
|
||||||
MatmulNode::MatmulNode(Tensor A, Tensor B, Tensor C, bool transA, bool transB,
|
: OperatorObj(OpType::Matmul, {A, B, bias}, {C}), transA(transA),
|
||||||
Tensor bias, ActType act)
|
|
||||||
: OperatorNode(OpType::Matmul, {A, B, bias}, {C}), transA(transA),
|
|
||||||
transB(transB), act(act), b(A->getDims()[0]),
|
transB(transB), act(act), b(A->getDims()[0]),
|
||||||
m(transA ? A->getDims()[2] : A->getDims()[1]),
|
m(transA ? A->getDims()[2] : A->getDims()[1]),
|
||||||
n(transB ? B->getDims()[1] : B->getDims()[2]),
|
n(transB ? B->getDims()[1] : B->getDims()[2]),
|
||||||
k(transA ? A->getDims()[1] : A->getDims()[2]) {
|
k(transA ? A->getDims()[1] : A->getDims()[2]) {
|
||||||
IT_ASSERT(checkValid(inputs));
|
IT_ASSERT(checkValid(graph));
|
||||||
}
|
}
|
||||||
|
|
||||||
string MatmulNode::toString() const {
|
string MatmulObj::toString() const {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << "Matmul([" << (transA ? "A^T" : "A") << "," << (transB ? "B^T" : "B")
|
os << "Matmul([" << (transA ? "A^T" : "A") << "," << (transB ? "B^T" : "B")
|
||||||
<< ",act=" << enum_to_underlying(act) << "],A=" << inputs[0]->getGuid()
|
<< ",act=" << enum_to_underlying(act) << "],A=" << inputs[0]->getGuid()
|
||||||
|
@ -23,34 +21,29 @@ string MatmulNode::toString() const {
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MatmulNode::checkValid(const TensorVec &inputs) const {
|
optional<vector<Shape>> MatmulObj::inferShape(const TensorVec &inputs) const {
|
||||||
auto A = inputs[0], B = inputs[1];
|
auto A = inputs[0], B = inputs[1];
|
||||||
// if (A->getType() == Tensor::Weight && B->getType() == Tensor::Weight)
|
// if (A->getType() == Tensor::Weight && B->getType() == Tensor::Weight)
|
||||||
// return false;
|
// return false;
|
||||||
IT_ASSERT(A->getDims().size() == 3 && B->getDims().size() == 3);
|
if (!(A->getDims().size() == 3 && B->getDims().size() == 3))
|
||||||
IT_ASSERT(A->getDims()[0] == B->getDims()[0]);
|
return {};
|
||||||
IT_ASSERT((transA ? A->getDims()[1] : A->getDims()[2]) ==
|
if (!(A->getDims()[0] == B->getDims()[0]))
|
||||||
(transB ? B->getDims()[2] : B->getDims()[1]));
|
return {};
|
||||||
// if (A->getDims().size() != 3 || B->getDims().size() != 3) {
|
if (!((transA ? A->getDims()[1] : A->getDims()[2]) ==
|
||||||
// return false;
|
(transB ? B->getDims()[2] : B->getDims()[1])))
|
||||||
// }
|
return {};
|
||||||
// if (A->getDims()[0] != B->getDims()[0]) {
|
int b(A->getDims()[0]), m(transA ? A->getDims()[2] : A->getDims()[1]),
|
||||||
// return false;
|
n(transB ? B->getDims()[1] : B->getDims()[2]);
|
||||||
// }
|
return {{{b, m, n}}};
|
||||||
// if ((args.transA ? A->getDims()[1] : A->getDims()[2]) !=
|
|
||||||
// (args.transB ? B->getDims()[2] : B->getDims()[1])) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HashType MatmulNode::hashWithShape() const {
|
vector<int> MatmulObj::getWorkloadVector() const {
|
||||||
// TODO: use a real hash
|
return {enum_to_underlying(type), b, m, n, k, transA, transB,
|
||||||
return b + m + n + k + transA + transB + enum_to_underlying(act);
|
enum_to_underlying(act)};
|
||||||
}
|
}
|
||||||
|
|
||||||
OpPerfKey MatmulNode::getOpPerfKey() const {
|
vector<int> MatmulObj::getOpAttrVector() const {
|
||||||
return OpPerfKey(hashWithShape(), type,
|
return {enum_to_underlying(type), transA, transB, enum_to_underlying(act)};
|
||||||
{b, m, n, k, transA, transB, enum_to_underlying(act)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace infini
|
} // namespace infini
|
|
@ -6,41 +6,41 @@
|
||||||
namespace infini {
|
namespace infini {
|
||||||
|
|
||||||
TEST(Graph, build_and_run) {
|
TEST(Graph, build_and_run) {
|
||||||
Graph g = make_ref<GraphNode>();
|
Graph g = make_ref<GraphObj>();
|
||||||
Tensor i0 = g->addTensor({1, 2, 3}, DataType::Int32);
|
Tensor i0 = g->addTensor({1, 2, 3}, DataType::Int32);
|
||||||
Tensor w0 = g->addTensor({1, 3, 4}, DataType::Int32);
|
Tensor w0 = g->addTensor({1, 3, 4}, DataType::Int32);
|
||||||
Tensor o0 = g->addTensor({1, 2, 4}, DataType::Int32);
|
Tensor o0 = g->addTensor({1, 2, 4}, DataType::Int32);
|
||||||
g->dataMalloc();
|
g->dataMalloc();
|
||||||
i0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
i0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
||||||
w0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
w0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
||||||
g->addOp(make_ref<MatmulNode>(i0, w0, o0));
|
g->addOpWithOutputs<MatmulObj>(i0, w0, o0);
|
||||||
RunEngine(Device::CPU).run(g);
|
RunEngine(Device::CPU).run(g);
|
||||||
// check answer
|
// check answer
|
||||||
auto ans = make_ref<TensorNode>(Shape{1, 2, 4}, DataType::Int32);
|
auto ans = make_ref<TensorObj>(Shape{1, 2, 4}, DataType::Int32);
|
||||||
ans->dataMalloc();
|
ans->dataMalloc();
|
||||||
ans->copyData(vector<VType>{38, 44, 50, 56, 83, 98, 113, 128}.data());
|
ans->copyData(vector<VType>{38, 44, 50, 56, 83, 98, 113, 128}.data());
|
||||||
EXPECT_TRUE(o0->equalData(ans));
|
EXPECT_TRUE(o0->equalData(ans));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Graph, perf_engine) {
|
TEST(Graph, perf_engine) {
|
||||||
Graph g = make_ref<GraphNode>();
|
Graph g = make_ref<GraphObj>();
|
||||||
Tensor i0 = g->addTensor({1, 2, 3}, DataType::Int32);
|
Tensor i0 = g->addTensor({1, 2, 3}, DataType::Int32);
|
||||||
Tensor w0 = g->addTensor({1, 3, 4}, DataType::Int32);
|
Tensor w0 = g->addTensor({1, 3, 4}, DataType::Int32);
|
||||||
Tensor o0 = g->addTensor({1, 2, 4}, DataType::Int32);
|
auto matmul = g->addOp<MatmulObj>(i0, w0, nullptr);
|
||||||
|
|
||||||
g->dataMalloc();
|
g->dataMalloc();
|
||||||
i0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
i0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
||||||
w0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
w0->copyData(vector<VType>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}.data());
|
||||||
g->addOp(make_ref<MatmulNode>(i0, w0, o0));
|
|
||||||
RunEngine(Device::CPU).run(g, true, true);
|
RunEngine(Device::CPU).run(g, true, true);
|
||||||
double perfTime = RunEngine(Device::CPU).getPerfTime(g);
|
double perfTime = RunEngine(Device::CPU).getPerfTime(g);
|
||||||
// The example matmul takes 0.0036ms with one core
|
// The example matmul takes 0.0036ms with one core
|
||||||
EXPECT_GT(perfTime, 0);
|
EXPECT_GT(perfTime, 0);
|
||||||
EXPECT_LT(perfTime, 0.01);
|
EXPECT_LT(perfTime, 0.01);
|
||||||
// check answer
|
// check answer
|
||||||
auto ans = make_ref<TensorNode>(Shape{1, 2, 4}, DataType::Int32);
|
auto ans = make_ref<TensorObj>(Shape{1, 2, 4}, DataType::Int32);
|
||||||
ans->dataMalloc();
|
ans->dataMalloc();
|
||||||
ans->copyData(vector<VType>{38, 44, 50, 56, 83, 98, 113, 128}.data());
|
ans->copyData(vector<VType>{38, 44, 50, 56, 83, 98, 113, 128}.data());
|
||||||
EXPECT_TRUE(o0->equalData(ans));
|
EXPECT_TRUE(matmul->getOutput()->equalData(ans));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace infini
|
} // namespace infini
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include "core/graph.h"
|
||||||
|
#include "core/run_enigne.h"
|
||||||
|
#include "operators/matmul.h"
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
namespace infini {
|
||||||
|
|
||||||
|
TEST(Hash, OperatorHash) {
|
||||||
|
OpPerfKey key1(0, OpType::Unknown), key2(0, OpType::Unknown);
|
||||||
|
{ // build with addOpWithOutputs
|
||||||
|
Graph g = make_ref<GraphObj>();
|
||||||
|
Tensor i0 = g->addTensor({1, 2, 3}, DataType::Int32);
|
||||||
|
Tensor w0 = g->addTensor({1, 3, 4}, DataType::Int32);
|
||||||
|
Tensor o0 = g->addTensor({1, 2, 4}, DataType::Int32);
|
||||||
|
auto matmul = g->addOpWithOutputs<MatmulObj>(i0, w0, o0);
|
||||||
|
key1 = matmul->getOpPerfKey();
|
||||||
|
EXPECT_NE(key1.hash, 0);
|
||||||
|
EXPECT_GT(key1.attrs.size(), 5);
|
||||||
|
}
|
||||||
|
{ // build with addOp
|
||||||
|
Graph g = make_ref<GraphObj>();
|
||||||
|
Tensor i0 = g->addTensor({2, 2, 3}, DataType::Int32);
|
||||||
|
Tensor w0 = g->addTensor({2, 3, 4}, DataType::Int32);
|
||||||
|
auto matmul = g->addOp<MatmulObj>(i0, w0, nullptr);
|
||||||
|
key2 = matmul->getOpPerfKey();
|
||||||
|
EXPECT_NE(key2.hash, 0);
|
||||||
|
}
|
||||||
|
EXPECT_NE(key1.hash, key2.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace infini
|
Loading…
Reference in New Issue