-
Notifications
You must be signed in to change notification settings - Fork 347
Introduce --dimParams option to set onnx.dim_params attrs of dynamic model inputs, and extend dynamic backend tests on NNPA with this option to utilze NNPA #2781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
(e.g. "0:a,1:b,2:c|0:a,1:b,2:c") Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
…_FORCE_DYNAMIC". Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some first comments.
docs/Testing.md
Outdated
``` | ||
IMPORTER_FORCE_DYNAMIC='-1:-1' all dimensions of all the inputs will be changed | ||
IMPORTER_FORCE_DYNAMIC='0:-1' all dimensions of the first input will be changed | ||
IMPORTER_FORCE_DYNAMIC='0:-1|1:0,1' all dimensions of the first input and the 1st and 2nd dimensions of the second input will be changed | ||
IMPORTER_FORCE_DYNAMIC='0:0:a,1:b,2:c|1:0:a,1:b,2:c' the first three dimensions of the first of and the second inputs are changed. And assume that the first dimensions of the first and second arguments are the same, and same for the the second and third dimensions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know by using :
we can pass dimIndex
directly to an argument attribute, but I recommend to use a different symbol rather than :
for dimIndex
, because :
has already been used to separate inputIndex
and dimString
. It confuses the parsers. How about -
or =
?
This example is quite trivial. Can we shuffle the symbols a bit, e.g. 0:0=a,1=b,2=c|1:0,1=c,2=b
, and we can have a mix of <index>
and <index> '=' <symbol>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tungld I changed the notation according to this comment. Thanks for the suggestions!
docs/Testing.md
Outdated
<index> ::= -1 | <number> | ||
<number> ::= <digit> | <digit><number> | ||
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ||
<symbol> ::= 'a', 'b', 'c', ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add 'z'
at the end to finish the list though it is still no perfect, e.g.
<symbol> ::= 'a', 'b', 'c', ..., 'z'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks.
docs/Testing.md
Outdated
@@ -76,15 +80,18 @@ func @main_graph(%arg0: tensor<3x4x5xf32>, %arg1: tensor<3x4x5xf32>) -> tensor<3 | |||
``` | |||
with `IMPORTER_FORCE_DYNAMIC='-1:-1'`, the result is: | |||
``` | |||
func @main_graph(%arg0: tensor<?x?x?xf32>, %arg1: tensor<?x?x?xf32>) -> tensor<?x?x?xf32> | |||
func @main_graph(%arg0: tensor<?x?x?xf32>{onnx.name = "x“, onnx.dim_params = "0:a,1:b,2:c"}, %arg1: tensor<?x?x?xf32>{onnx.name = "y“, onnx.dim_params = "0:a,1:b,2:c"}) -> tensor<?x?x?xf32> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1
for dimIndex
to say all arguments have the same dynamic dims does not make sense. It is too restricted.
Let's consider the dynamic tests for CPU where it supports broadcasting, e.g add(tensor<3x5xf32>, tensor<3x1xf32>
, using -1:-1
will force all dynamic dimensions being the same and the lowering will generate different code, not taking care of broadcasting.
In summary, I recommend to not support this, and let users specify the same dynamic dimensions manually for the cases they need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the assumption about default relationships among input data ranks, and specified the relationship manually in the test/accelerators/NNPA/backend/CMakeLists.txt file for each dynamic backend tests on NNPA.
Thanks for the suggestions.
Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some comments besides the main thing about introducing --dimParams
as we discussed locally.
} | ||
} else { | ||
getParamStr(envStr, paramStrVec[idx]); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we can simplify this to avoid calling getParamStr, e.g.
envStr = envStr.substr(pos + 1);
std::replace(envStr.begin(), envStr.end(), '=', ':');
if (idx >= 0) {
paramStrVec[idx] = envStr;
return;
}
// Idx < 0 means setting all arguments with the same value.
for (size_t i = 0; i < numOfArgs; i++)
paramStrVec[i] = envStr;
return;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks!
|
||
// Get named attributesfrom IMPORTER_FORCE_DYNAMIC environment. | ||
SmallVector<NamedAttribute> getNamedAttrFromImporterForceDynamic( | ||
func::FuncOp funcOp, size_t numOfArgs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid copying the result, it's better to pass argNamedAttrs
as an argument, e.g.SmallVectorImp<NamedAttribute> &argNamedAttrs
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was cleaned up, Thanks.
getInputDimParamStrVec( | ||
envInputString, funcOp, numOfArgs, inputDimParamStrVec); | ||
for (size_t i = 0; i < numOfArgs; ++i) { | ||
if (inputDimParamStrVec[i].length() != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really happened? e.g. input dim param is an empty string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was cleaned up. Thanks.
… tests on NNPA to utilze NNPA. Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the update! The PR is in a good shape. I just put some comments to make the explanation easier to follow.
void getInputDimParamsVecFromOption(std::string optionStr, size_t numOfArgs, | ||
SmallVector<std::string> ¶mStrVec) { | ||
std::stringstream paramStrStream; | ||
paramStrStream << optionStr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For brevity: std::stringstream paramStrStream(optionStr);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks!
@@ -490,6 +513,10 @@ class FrontendGenImpl { | |||
// See https://github.com/onnx/onnx/blob/main/docs/IR.md for more | |||
// information about dim_param. | |||
llvm::SmallVector<std::string, 4> inputDimParams, outputDimParams; | |||
size_t numOfArgs = graph.input().size(); | |||
llvm::SmallVector<std::string> inputDimParamsFromOption(numOfArgs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
numOfArgs
here may contain initializers also, so it does not count exactly the number of actual arguments to the main function. Please see inputIndex
below that correctly represents the number of actual arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the code not to use numOfArgs
in inputDimParamsFromOption
. Thanks!
@@ -55,6 +55,16 @@ struct ImportOptions { | |||
// - (arg0: tensor<3x4x5xf32>, arg1: tensor<10x5xf32>) | |||
// | |||
std::string shapeInformation = ""; | |||
// Custom onnx.dim_params attributes for the graph inputs for specifying | |||
// relationship among their ranks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
their ranks
looks confusing. To be precise, it would be their dynamic dimensions
, given that dim
in dim_params
means dimension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks!
// Its format is 'input_id:dim=sym,dim=sym,dim=sym|input_id:dim=sym,dim=sym..' | ||
// E.g. An ONNX model has two dynamic inputs | ||
// - (arg0: tensor<?x5xf32>, arg1: tensor<?x5xf32>) | ||
// If we want to specify that the first ranks of the first and second |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to specify that the first ranks of the first and second arguments are the same
=> If we want to specify that the first unknown dimension of arg0 and the first unknown dimension of arg1 are the same, we can assign the two dimensions to the same symbol "batch" as follows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks.
src/Compiler/CompilerOptions.cpp
Outdated
static llvm::cl::opt<std::string, true> dimParamsOpt("dimParams", | ||
llvm::cl::desc( | ||
"Custom onnx.dim_params attributes for the inputs of the ONNX model for" | ||
"specifying relationship among ranks of the inputs.\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
among ranks of
=> among dynamic dimensions of
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks.
# "zdnn_add" to check the function is called, (3) when dimParams is | ||
# "NO_DYNAMIC_SHAPE_TEST", backend test is skipped, otherwise the string is | ||
# passed as --dimParams option. "0:0=a,1=b,2=c|1:0=a,1=b,2=c" means that the | ||
# first ranks of the first, second and third ranks of the first and second |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make this explanation easier to follow? Don't use rank but dimension. Ranks in MLIR means the number of dimensions (in this case rank = 3).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comments. I fixed. I confirmed that all "rank" were removed modifications by this PR.
test/backend/common.py
Outdated
@@ -132,6 +139,7 @@ def compile_model(model, emit): | |||
"--constants-to-file-total-threshold=" | |||
+ str(args.constants_to_file_total_threshold) | |||
) | |||
command_list += get_compile_option(name + "_cpu") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have a if
directly here, it's easy to follow and faster:
if args.dynamic and test_name in variables.test_to_enable_dimparams_dict:
command_list.append("--dimParams=" + variables.test_to_enable_dimparams_dict[test_name])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks!
@@ -0,0 +1,23 @@ | |||
// RUN: onnx-mlir --EmitONNXBasic --dimParams="0:0=a,1=b,2=c|1:0=a,1=b,2=c" --printIR %s | FileCheck %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for adding this test! Really appreciate it.
@@ -55,6 +55,16 @@ struct ImportOptions { | |||
// - (arg0: tensor<3x4x5xf32>, arg1: tensor<10x5xf32>) | |||
// | |||
std::string shapeInformation = ""; | |||
// Custom onnx.dim_params attributes for the graph inputs for specifying | |||
// relationship among their ranks. | |||
// Its format is 'input_id:dim=sym,dim=sym,dim=sym|input_id:dim=sym,dim=sym..' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dim_id
would be better than dim
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
..
at the end would be three dots ...
? Also that ...
is for |
meaning multiple input_id:dim=sym,dim=sym,dim=sym
, or for ,
meaning multiple dim=sym,dim=sym
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Fixed.
src/Compiler/CompilerOptions.cpp
Outdated
"Custom onnx.dim_params attributes for the inputs of the ONNX model for" | ||
"specifying relationship among ranks of the inputs.\n" | ||
"\"value\" is in the format of " | ||
"\"INPUT_ID1:D1=S1,D2=S2,...,Dn=Sn|INPUT_ID2:D1=T1,D2=T2,...Dn=Tn\"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing a ,
before that last Dn
.
It's better to add | ...
at the end to denote multiple INPUT_ID, e.g.
INPUT_ID1:D1=S1,D2=S2,...,Dn=Sn | INPUT_ID2:D1=T1,D2=T2,...,Dn=Tn | ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks.
Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
"All dimensions of onnx.dim_params for a specified input index in " | ||
"the original onnx model are cleared and repalced by this option. " | ||
"onnx.dim_params for other input indices in the original onnx model " | ||
"are not cleared"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for adding the explanation!
paramStrForAllArgs = dimParamStr; | ||
else { | ||
while ((int)paramStrVec.size() <= idx) // Expand paramStrVec | ||
paramStrVec.emplace_back(""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the option definition you wrote onnx.dim_params for other input indices in the original onnx model are not cleared
, but It looks to me only inputs whose index is larger than the max idx
here are not cleared. And, the inputs whose index <= the max idx
will be cleared.
For example, we have 4 input tensors
arg0: tensor<?x?xf32> {onnx.dim_param=0:x},
arg1: tensor<?x?xf32> {onnx.dim_param=0:y},
arg2: tensor<?x?xf32> {onnx.dim_param=0:z}
arg3: tensor<?x?xf32> {onnx.dim_param=0:c,1:d}
with --dimParams="0:0=b,1=a|2:0=a,1=b"
, it becomes
arg0: tensor<?x?xf32> {onnx.dim_param=0:b,1:a},
arg1: tensor<?x?xf32>,
arg2: tensor<?x?xf32> {onnx.dim_param=0:a,1:b}
arg3: tensor<?x?xf32> {onnx.dim_param=0:c,1:d}
Is it correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tungld Thanks for the comment.
It is not correct. onnx.dim_param
of arg1 is not cleared with --dimParams="0:0=b,1=a|2:0=a,1=b"
.
// For each input tensor, use either all dimensions by the compiler
// option OR all dimensions in the original onnx model. Dimensions
// from the option and the model in a single input tensor are not
// merged.
if (inputIndex < (int)inputDimParamsFromOption.size() &&
!inputDimParamsFromOption[inputIndex].empty())
inputDimParams.emplace_back(inputDimParamsFromOption[inputIndex]);
else if (!inputDimParamsFromOptionForAllArgs.empty())
inputDimParams.emplace_back(inputDimParamsFromOptionForAllArgs);
else if (!dimParams.empty())
inputDimParams.emplace_back(dimParams);
The code checks if inputDimParamsFromOption[inputIndex]
is empty string, and if it's empty, it uses dimParams
(=onnx.dim_param in the original model), so that onnx.dim_param attr of arg1 in the original model is not cleared.
I added test/mlir/onnx/parse/dim_param_and_option.json
to verify this situation.
In the test, With --dimParams="1:1=b"
, onnx.dim_params="0:A,1:B" of the first input argument in the original model, is not cleared with the option.
Original model
module {
func.func @main_graph(%arg0: tensor<?x?xf32> {onnx.dim_params = "0:A,1:B", onnx.name = "X"}, %arg1: tensor<?x?xf32> {onnx.dim_params = "0:A,1:B", onnx.name = "Y"}) -> (tensor<?x?xf32> {onnx.dim_params = "0:A,1:B", onnx.name = "Z"}) {
%0 = "onnx.Add"(%arg0, %arg1) : (tensor<?x?xf32>, tensor<?x?xf32>) -> tensor<?x?xf32>
onnx.Return %0 : tensor<?x?xf32>
}
"onnx.EntryPoint"() {func = @main_graph} : () -> ()
}
Imported model
module {
func.func @main_graph(%arg0: tensor<?x?xf32> {onnx.dim_params = "0:A,1:B", onnx.name = "X"}, %arg1: tensor<?x?xf32> {onnx.dim_params = "1:b", onnx.name = "Y"}) -> (tensor<?x?xf32> {onnx.dim_params = "0:A,1:B", onnx.name = "Z"}) {
%0 = "onnx.Add"(%arg0, %arg1) : (tensor<?x?xf32>, tensor<?x?xf32>) -> tensor<?x?xf32>
onnx.Return %0 : tensor<?x?xf32>
}
"onnx.EntryPoint"() {func = @main_graph} : () -> ()
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Thank you for the confirmation! Just a suggestion: if you use a map instead of vector for inputDimParamsFromOption
, the code is easier to read and you don't need to expand the vector and check if (inputIndex < (int)inputDimParamsFromOption.size() && !inputDimParamsFromOption[inputIndex].empty())
. Vector data structure looks not ideal here. It's a bit tricky to use an empty string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the code to use std::map<int, std::string>
instead of SmallVector<std::string>
Thanks for the suggestion. It makes the code simpler.
Signed-off-by: Yasushi Negishi <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
…rc/Builder/FrontendDialectTransformer.cpp Signed-off-by: Yasushi Negishi <[email protected]>
Signed-off-by: Yasushi Negishi <[email protected]>
Jenkins Linux ppc64le Build #13764 [push] Introduce --dimParams op... started at 22:48 |
Jenkins Linux amd64 Build #14739 [push] Introduce --dimParams op... started at 21:36 |
Jenkins Linux s390x Build #14769 [push] Introduce --dimParams op... started at 22:36 |
Jenkins Linux amd64 Build #14739 [push] Introduce --dimParams op... passed after 1 hr 5 min |
Jenkins Linux s390x Build #14769 [push] Introduce --dimParams op... passed after 1 hr 29 min |
Jenkins Linux ppc64le Build #13764 [push] Introduce --dimParams op... passed after 2 hr 3 min |
@hamptonm1 @Sunny-Anand Suggestion is to create issues so that the root cause of the model features will be evaluated with the goal of increasing our share of model zoo models successfully running with / without dynamic bounds. |
===== 2024/04/19 updated according to local discussions =====
This PR support the following two features.
(1) This PR introduces
--dimParams
option for onnx-mlir to setonnx.dim_params
attrs of dynamic model inputs, which specifies relationships among dimensions of model inputs. For example, onnx-mlir cannot utilize NNPA for the following dynamic model, because broadcasting of %arg0 or %arg1 may be required and broadcasting is not supported by NNPA.module {
func.func @main_graph(%arg0: tensor<?x?x?xf32> {onnx.name = "x"}, %arg1: tensor<?x?x?xf32> {onnx.name = "y"})
-> (tensor<3x4x5xf32> {onnx.name = "sum"}) {
%0 = "onnx.Add"(%arg0, %arg1) : (tensor<?x?x?xf32>, tensor<?x?x?xf32>) -> tensor<?x?x?xf32>
onnx.Return %0 : tensor<?x?x?xf32>
}
"onnx.EntryPoint"() {func = @main_graph} : () -> ()
}
We can use the
--dimParams
option to specify that all corresponding dimensions between two dynamic inputs %arg0 and %arg1 are the same by--dimParams="0:0=a,1=b,2=c|1:0=a,1=b,2=c"
. Accordingly compiler can know no broadcasting is required and can utilize NNPA in this case.module {
func.func @main_graph(%arg0: tensor<?x?x?xf32> {onnx.dim_params = "0:a,1:b,2:c", onnx.name = "x"}, %arg1: tensor<?x?x?xf32> {onnx.dim_params = "0:a,1:b,2:c", onnx.name = "y"}) -> (tensor<?x?x?xf32> {onnx.name = "sum"}) {
%0 = "onnx.Add"(%arg0, %arg1) : (tensor<?x?x?xf32>, tensor<?x?x?xf32>) -> tensor<?x?x?xf32>
onnx.Return %0 : tensor<?x?x?xf32>
}
"onnx.EntryPoint"() {func = @main_graph} : () -> ()
}
(2) This PR also extends functions of dynamic backend tests for NNPA by using the
--dimParams
optionIn backend tests on NNPA, support new notations for setting arguments of the option in the NNPA_TEST_LIST variable in the test/accelerators/NNPA/backend/CMakeLists.txt. The following examples mean as follows.