Skip to content

Commit 1cd4afb

Browse files
committed
[tmva][sofie] Add alias tensors
Add alias tensors to cope with identity operators. In this case just a pointer assignment is performed by the operator. Exclude this tensors in the allocation and take care of them in the dynamic memory pool Optimise Slice operator when slice is an identity and also ScatterElements
1 parent b95b9a7 commit 1cd4afb

File tree

6 files changed

+112
-27
lines changed

6 files changed

+112
-27
lines changed

tmva/sofie/inc/TMVA/RModel.hxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ private:
3030
std::unordered_map<std::string, DynamicTensorInfo> fDynamicTensorInfos;
3131
std::unordered_map<std::string, std::pair<std::vector<Dim>, bool>> fShapeTensors; // constant tensors describing a shape
3232
std::unordered_map<std::string, std::string> fShapeParams; // parameters defining the dynamic shape (e.g. batch size), store also its default value
33+
std::unordered_map<std::string, std::string> fAliasTensors; // list of alias tensors
3334
std::vector<std::string> fDimShapeNames; // parameter names used to define the shapes
3435
std::vector<std::string> fOutputTensorNames;
3536
std::vector<std::string> fInputTensorNames; // input tensor names using ONNX order
@@ -82,6 +83,8 @@ public:
8283
void AddConstantTensor(std::string tensor_name, ETensorType type, std::vector<std::size_t> shape,
8384
std::shared_ptr<void> data);
8485

86+
void AddAliasTensor(const std::string & tensor_name, const std::string & orig_tensor_name);
87+
8588

8689
template<class T>
8790
void AddConstantTensor(const std::string & name, const std::vector<size_t> & shape, const T * data) {
@@ -130,6 +133,8 @@ public:
130133
bool IsReadyInputTensor(const std::string &name) const;
131134
/// check if a tensor is a shape tensor
132135
bool IsShapeTensor(const std::string & name) const;
136+
/// check if a tensor is a alias tensor
137+
bool IsAliasTensor(const std::string & name) const;
133138

134139
// Add intermediate tensor
135140
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector<Dim> dim_shape);

tmva/sofie/inc/TMVA/ROperator_Constant.hxx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,12 @@ public:
123123
if (model.Verbose()) {
124124
std::cout << "adding constant tensor " << fNY << " with shape " << ConvertShapeToString(fShape)
125125
<< " and values [";
126-
for (auto v : fValues) std::cout << " " << v;
127-
std::cout << "]" << std::endl;
126+
if (!fIsConstantOfShape) {
127+
for (auto v : fValues) std::cout << " " << v;
128+
std::cout << "]" << std::endl;
129+
} else { // for constant of shape is enough to print one value
130+
std::cout << "... " << fValues[0] << " ....]" << std::endl;
131+
}
128132
}
129133
} else {
130134
model.AddIntermediateTensor(fNY, ConvertStringToType(TensorType<T>::Name()), fDimOutputShape);

tmva/sofie/inc/TMVA/ROperator_ScatterElements.hxx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,42 @@ public:
136136
return strst.str();
137137
};
138138

139+
auto tensorIndexOpt = [](const std::vector<std::string> & sdx, const std::vector<std::string> & idx) {
140+
std::stringstream strst;
141+
int dims = idx.size();
142+
for (int i = 0; i < dims-1; i++) {
143+
strst << sdx[i];
144+
strst << " + ";
145+
}
146+
strst << idx[dims-1];
147+
return strst.str();
148+
};
149+
139150

140151
// copy first input in output (maybe can be avoided??)
141152
out << SP << "std::copy(tensor_" << fNX << ", tensor_" << fNX << " + " << length << ", tensor_" << fNY << ");\n";
142153

143154
// loop on tensor rank
144155
int dims = fShapeY.size();
145156
std::vector<std::string> idx(dims);
157+
std::vector<std::string> sdx(dims); // stride for indices
146158
for (int i = 0; i < dims; i++) {
147159
idx[i] = std::string("i") + std::to_string(i);
160+
sdx[i] = std::string("s") + std::to_string(i);
148161
for (int j = 0; j <= i; j++) out << SP;
149162
out << "for (int " << idx[i] << " = 0; " << idx[i] << " < " << fShapeI[i] << "; " << idx[i] << "++) {\n";
163+
if (i < dims-1) {
164+
for (int j = 0; j <= i+1 ; j++) out << SP;
165+
if (strideI[i].GetVal() != "1")
166+
out << "int "<< sdx[i] << " = " << strideI[i] << " * " << idx[i] << ";\n";
167+
else
168+
out << "int "<< sdx[i] << " = " << idx[i] << ";\n";
169+
}
150170
}
151171
// correct index for specific axis
152172
for (int j = 0; j <= dims; j++) out << SP;
153-
out << "int updateIndex = " << tensorIndex(strideI,idx) << ";\n";
173+
// can use optimised formula for indices since the loop above is on fShapeI
174+
out << "int updateIndex = " << tensorIndexOpt(sdx,idx) << ";\n";
154175
for (int j = 0; j <= dims; j++) out << SP;
155176
out << "int iAxis = tensor_" << fNI << "[updateIndex];\n";
156177
for (int j = 0; j <= dims; j++) out << SP;

tmva/sofie/inc/TMVA/ROperator_Slice.hxx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ private:
2525
bool fIsStartUndef = false;
2626
bool fIsEndUndef = false;
2727
bool fIsStepUndef = false;
28+
bool fIdentitySlice = false;
2829
std::string fNData; // input data tensor name
2930
std::string fNOutput; // output data name
3031
std::vector<std::string> fNames; // tensor names for meta(axis) information
@@ -332,10 +333,27 @@ public:
332333
}
333334
}
334335
else {
336+
// check if Slice is just an Identity operator in case start = 0, end = input_shape and step=1
337+
size_t ndim = fShapeInput.size();
338+
fIdentitySlice = fShapeOutput.size() == ndim;
339+
// check also if input data is not input to the model. In that case we copy the data since we cannot just copy from the input pointer
340+
fIdentitySlice &= !model.IsReadyInputTensor(fNData);
341+
for (size_t idim = 0; idim < ndim; idim++) {
342+
if (!fIdentitySlice) break;
343+
fIdentitySlice &= (fStart[idim].GetVal() == "0");
344+
fIdentitySlice &= (fSteps[idim].GetVal() == "1");
345+
fIdentitySlice &= (fEnd[idim].GetVal() == fShapeOutput[idim].GetVal());
346+
}
347+
335348
model.AddIntermediateTensor(fNOutput, model.GetTensorType(fNData), fShapeOutput);
349+
if (fIdentitySlice) model.AddAliasTensor(fNOutput, fNData);
350+
336351
if (model.Verbose()) {
337352
std::cout << "Slice " << fNData << " " << ConvertShapeToString(fShapeInput)
338-
<< "---> " << fNOutput << " " << ConvertShapeToString(fShapeOutput) << std::endl;
353+
<< "---> " << fNOutput << " " << ConvertShapeToString(fShapeOutput);
354+
if (fIdentitySlice) std::cout << " (using alias tensor since slice is an identity) ";
355+
std::cout << std::endl;
356+
339357
}
340358
}
341359
}
@@ -351,8 +369,16 @@ public:
351369
out << "///------- Slice operator " << opName << "---> " << fNOutput << " "
352370
<< ConvertDimShapeToString(fShapeOutput) << "\n" << std::endl;
353371
if (fIsOutputConstant) return out.str(); //no op for constant tensors
354-
// loop on the dimensions depending no the orders
372+
355373
size_t ndim = fShapeInput.size();
374+
375+
if (fIdentitySlice) {
376+
out << "/// Slice is just an identity (copy pointers) \n";
377+
out << SP << "tensor_" << fNOutput << " = tensor_" << fNData << ";\n";
378+
return out.str();
379+
}
380+
381+
// loop on the dimensions depending no the orders
356382
auto strides = UTILITY::ComputeStrideFromShape(fShapeInput);
357383

358384

tmva/sofie/src/RModel.cxx

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ void RModel::AddOperator(std::unique_ptr<ROperator> op, int order_execution) {
167167
order_execution = fOperators.size()-1;
168168
}
169169

170-
// storing the last usage of tensors which are input to
171-
// operators (but are not inputs to the model or they are not initialized)
170+
// storing the last usage of tensors which are input to the operator
171+
// (excluding tensors which are inputs to the model or the initialized (weights) tensors)
172172
// We call this function during parsing so we don't have yet initialized the operators
173173
for(size_t index = 0; index<op_input_tensors.size() &&
174174
fInitializedTensors.find(UTILITY::Clean_name(std::string(op_input_tensors[index]))) == fInitializedTensors.end() &&
@@ -208,10 +208,24 @@ void RModel::AddShapeTensor(const std::string & name, const std::vector<Dim> & s
208208
fShapeTensors[tensor_name] = std::make_pair(shape_values, scalar);
209209
}
210210

211+
void RModel::AddAliasTensor(const std::string & name, const std::string & origin){
212+
// add an alias tensor to origin
213+
auto tensor_name = UTILITY::Clean_name(name);
214+
auto origin_name = UTILITY::Clean_name(origin);
215+
if (fAliasTensors.count(tensor_name) != 0) {
216+
throw std::runtime_error("TMVA-SOFIE: alias tensor with name " + tensor_name + " already exists \n");
217+
}
218+
fAliasTensors[tensor_name] = origin_name;
219+
}
220+
211221
bool RModel::IsShapeTensor(const std::string & tensor_name) const {
212222
return fShapeTensors.count(tensor_name) != 0;
213223
}
214224

225+
bool RModel::IsAliasTensor(const std::string & tensor_name) const {
226+
return fAliasTensors.count(tensor_name) != 0;
227+
}
228+
215229
const std::vector<Dim> & RModel::GetShapeTensorValues(const std::string & tensor_name) const {
216230
//if (!IsShapeTensor(tensor_name) ) return std::vector<Dim>{};
217231
return fShapeTensors.at(tensor_name).first;
@@ -356,6 +370,11 @@ std::string RModel::AllocateIntermediateMemory(std::span<const std::string_view>
356370
fDynamicTensorInfos.find(name) != fDynamicTensorInfos.end())
357371
continue;
358372

373+
// case of alias tensor
374+
if (IsAliasTensor(name)) {
375+
continue;
376+
}
377+
359378
auto tensor_size = GetTypeSize(GetTensorType(name)) * ConvertShapeToLength(GetTensorShape(name));
360379
// important fill the pair in the ordered output tensors with the string view and not the string
361380
TensorMemoryInfo tmi = {it, tensor_size};
@@ -435,9 +454,14 @@ void RModel::CheckAndFlushIntermediateMemory(std::span<const std::string_view> o
435454
chunk != fIntermediateMemoryInfo.available_stack.end(); chunk++) {
436455
if (fVerbose) std::cout << "-- free chunk " << chunk->first << " size = " << chunk->second << std::endl;
437456
}
438-
for (auto &it : op_input_tensors) {
457+
for (auto &iv : op_input_tensors) {
439458
// last occurrence of the tensor is reached => flush it from memory
440-
if (fVerbose) std::cout << ".. input tensors : " << it;
459+
if (fVerbose) std::cout << ".. input tensors : " << iv;
460+
461+
// for alias tensors replace name with its alias
462+
std::string it{iv}; // convert view to string
463+
if (IsAliasTensor(it))
464+
it = fAliasTensors[it];
441465
if (fIntermediateTensorFrequencyLookup[it] == op_idx) {
442466
if (fVerbose) std::cout << " flash condition is met - looping on chunks to find matching one \n";
443467
for (auto chunk = fIntermediateMemoryInfo.total_stack.begin();
@@ -623,6 +647,17 @@ void RModel::Initialize(const std::map<std::string, size_t> & inputParams, bool
623647
fUseWeightFile = false;
624648
}
625649

650+
// update fIntermediateTensorFrequencyLookup for alias tensors
651+
for (auto & it : fAliasTensors) {
652+
if (fIntermediateTensorFrequencyLookup.find(it.first) == fIntermediateTensorFrequencyLookup.end()) continue;
653+
if (fIntermediateTensorFrequencyLookup.find(it.second) == fIntermediateTensorFrequencyLookup.end() )
654+
fIntermediateTensorFrequencyLookup[it.second] = fIntermediateTensorFrequencyLookup[it.first];
655+
else {
656+
// take the largest one
657+
fIntermediateTensorFrequencyLookup[it.second] = std::max(fIntermediateTensorFrequencyLookup[it.second],fIntermediateTensorFrequencyLookup[it.first] );
658+
}
659+
}
660+
626661
fIsInitialized = true;
627662
}
628663

@@ -737,7 +772,8 @@ void RModel::GenerateIntermediateTensorInfo() {
737772
if (!fIntermediateTensorInfos.empty()) {
738773
std::string tensor_declaration_block = "";
739774
for (auto &i : fIntermediateTensorInfos) {
740-
if (i.second.type == ETensorType::BOOL) {
775+
bool is_alias = (IsAliasTensor(i.first));
776+
if (i.second.type == ETensorType::BOOL && !is_alias) {
741777
tensor_declaration_block += "std::vector<std::uint8_t> fTensor_" + i.first + " = std::vector<std::uint8_t>(" + std::to_string(ConvertShapeToLength(i.second.shape)) + ");\n";
742778
tensor_declaration_block += "std::uint8_t * tensor_" + i.first + " = fTensor_" + i.first + ".data();\n";
743779
continue;
@@ -748,7 +784,7 @@ void RModel::GenerateIntermediateTensorInfo() {
748784
bool not_in_output_names =
749785
(std::find(fOutputTensorNames.begin(), fOutputTensorNames.end(), i.first) == fOutputTensorNames.end());
750786

751-
if ((not_in_freq_map && not_in_output_names) || (!not_in_freq_map && !is_extended && not_in_output_names)) {
787+
if (((not_in_freq_map && not_in_output_names) || (!not_in_freq_map && !is_extended && not_in_output_names) ) && !is_alias) {
752788
size_t length = ConvertShapeToLength(i.second.shape);
753789

754790
if (i.second.type == ETensorType::FLOAT) {
@@ -767,6 +803,10 @@ void RModel::GenerateIntermediateTensorInfo() {
767803
fOtherTensorSize += 8 * length;
768804
}
769805
}
806+
if (is_alias) {
807+
tensor_declaration_block += ConvertTypeToString(i.second.type) + " * tensor_" + i.first + " = nullptr;\n";
808+
}
809+
770810
}
771811

772812
if (tensor_declaration_block.length()) {
@@ -777,19 +817,7 @@ void RModel::GenerateIntermediateTensorInfo() {
777817
if (!fDynamicTensorInfos.empty()) {
778818
fGC += "//--- declare the dynamic tensors\n";
779819
for (auto &i : fDynamicTensorInfos) {
780-
if (i.second.type == ETensorType::FLOAT) {
781-
//fGC += "std::vector<float> fTensor_" + i.first + ";\n";
782-
fGC += "float * tensor_" + i.first + " = nullptr;\n";
783-
} else if (i.second.type == ETensorType::DOUBLE) {
784-
//fGC += "std::vector<double> fTensor_" + i.first + ";\n";
785-
fGC += "double * tensor_" + i.first + " = nullptr;\n";
786-
} else if (i.second.type == ETensorType::INT64) {
787-
//fGC += "std::vector<int64_t> fTensor_" + i.first + ";\n";
788-
fGC += "int64_t * tensor_" + i.first + " = nullptr;\n";
789-
} else if (i.second.type == ETensorType::BOOL) {
790-
//fGC += "std::vector<uint8_t> fTensor_" + i.first + ";\n";
791-
fGC += "uint8_t * tensor_" + i.first + " = nullptr;\n";
792-
}
820+
fGC += ConvertTypeToString(i.second.type) + " * tensor_" + i.first + " = nullptr;\n";
793821
}
794822
fGC += "//--- dynamic tensors pool\n";
795823
fGC += "std::vector<char> fDynamicMemoryPool;\n";
@@ -835,9 +863,9 @@ void RModel::GenerateDynamicTensorInfo()
835863
auto op_ptr = op.get();
836864
std::cout << "Looping on operator " << op_index << " " << typeid(*op_ptr).name() << std::endl;
837865
}
838-
// check if is a dynamic tensor
866+
// check if is a dynamic tensor and not an alias tensor
839867
std::string name = std::string(it);
840-
if ( fDynamicTensorInfos.find(name) != fDynamicTensorInfos.end() ) {
868+
if ( fDynamicTensorInfos.find(name) != fDynamicTensorInfos.end() && !IsAliasTensor(name)) {
841869
auto tensor_size = ConvertDimShapeToLength(GetDimTensorShape(name));
842870
auto type = GetTensorType(name);
843871
size_t type_size = GetTypeSize(type);
@@ -873,6 +901,7 @@ void RModel::GenerateDynamicTensorInfo()
873901
// check that all dynamic tensors are covered
874902
bool missingTensor = false;
875903
for (auto &i : fDynamicTensorInfos) {
904+
if (IsAliasTensor(i.first)) continue;
876905
if (std::find(tensors.begin(), tensors.end(), std::pair<std::string,ETensorType>{i.first, i.second.type}) == tensors.end()) {
877906
std::cout << "Dynamic tensors " << i.first << " is not in list of operator input/output " << std::endl;
878907
missingTensor = true;

tmva/sofie/src/SOFIE_common.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ std::string ConvertDimShapeToString(const std::vector<Dim> & shape) {
132132
std::stringstream out;
133133
out << "{ ";
134134
for (size_t i = 0; i < shape.size(); i++) {
135-
out << shape[i].GetVal();
135+
out << shape[i];
136136
if (i < shape.size()-1) out << " , ";
137137
}
138138
out << " }";

0 commit comments

Comments
 (0)