Skip to content
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

Loop operator #853

Merged
merged 466 commits into from
Sep 16, 2021
Merged

Loop operator #853

merged 466 commits into from
Sep 16, 2021

Conversation

scxiao
Copy link
Contributor

@scxiao scxiao commented Jun 9, 2021

This PR is for the implementation of the Loop operator for opset version 13.
Notes: 1) Default max iteration number is 10 if no max iteration number is provided
2) To change the max iter number, a user can set the max_loop_iterations in the onnx_option struct when parsing a model.
3) The returned shape of the scan output is from the max_loop_iterations even the actual loop num is less than that. This issue also applies to other operators like NonZero and NonMaxSuppression. A issue #948 is created to track this and to be resolved later.

pfultz2 and others added 30 commits May 6, 2021 12:33
…phX into inline_subgraph_try_removing_subgraph

void append(const std::vector<argument>& iter_state,
const std::vector<argument>& concatenated_outputs,
const int iter) const
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont make a value parameter const.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

module_ref&, const std::unordered_map<std::string, argument>&)>& run)
{
auto get_output_index = [](const std::string& name) {
std::string out_prefix = "#output_";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming of output indices as #output_ is specific to the GPU backend. Other backends could name them differently. Why do you need to check for the output params? Maybe this should added to the loop_model, maybe a function like std::map<std::string, int> get_output_params(const module& m)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this to the gpu implementation

if(loc != std::string::npos)
{
int index = std::stoi(name.substr(loc + out_prefix.size()));
return index;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It says there is no coverage here. There are other places where there is no coverage in this file we should probably add unit tests for. I assume there is no coverage because output params are only used by the GPU backend. Perhaps, we can create mock loop_model and some fake output params to check with the unit tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this part of code only applied to the GPU implementation.

pmod_insts_bkup[ins_p.first] = instructions[ins_p.first];
}
instructions[ins_p.first] = ins_p.second;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this doing? Is this to handle parameter shadowing? I thought shadowing is not permitted by ONNX spec from Nodes:

The graph MUST use single static assignment for all node outputs, which means that all node output names MUST be unique within a graph. In the case of a nested subgraph, a node output name MUST be distinct from the names from the outer scopes that are visible in the nested subgraph.

Is that an incorrect interpretation? Do you see onnx models that are shadowing params and constants?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have another scenario that subgraph and parent graph have parameters with the same name. In this case, when we parse the subgraph, the instruction (stored in the mem var instructions corresponding to the parameter in the subgraph will overwrite that of the parent graph. After parsing subgraph returns, if we do not restore the parameter of the parent graph and and the parent graph uses this parameter as input for some other operators, inputs of the operator are the parameter of the subgraph.

In this scenario, the name is not output of any node, so the above specification does not apply, and there is no specification saying parent graph and subgraph cannot use the same name as parameter names. The only thing I read from the notes is:

In nested subgraphs used as attribute values, users MUST NOT use the same name as both a subgraph initializer and subgraph input unless the corresponding op's specification explicitly allows it.

but this does not apply, either.

I got the example with the above scenario some time ago online, but I cannot find it anymore. Also, when I created the onnx file from the script, no error is reported.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pfultz2 Changed accordingly and added a unit test for the run loop. Could you please take a look again? Thanks

namespace gpu {
namespace device {

void fill(hipStream_t stream, const argument& result, const unsigned long& val)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass val by value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

auto cpu_cond =
mod->insert_instruction(ins, make_op("hip::copy_from_gpu"), inputs.at(1));
auto synced_max_iter =
mod->insert_instruction(ins, make_op("hip::sync_stream"), cpu_max_iter);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this take both cpu_max_iter and cpu_cond as inputs? So it will sync the stream after copying both variables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -2439,6 +2439,100 @@ TEST_CASE(logsoftmax_test_axis_3)
EXPECT(migraphx::verify_range(results_vector, s));
}

TEST_CASE(loop_test)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put the ref loop unit tests in a seperate cpp file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

}

return res;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to make this a regular function and make all the test cases seperate TEST_CASE cases in the test suite. This will make it easier to debug in the future as we can just run one test case(instead of all of them).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

if(contains(instructions, name))
{
MIGRAPHX_THROW("module \"" + mod->name() + "\" has parameter name \"" + name +
"\" existing in paraent graph!");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, fixed. Thanks!

return migraphx::shape(ins_out_shapes);
}

struct test_loop
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this inherit from ref_loop then just override get_output_params?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

@causten causten merged commit a275f59 into develop Sep 16, 2021
@causten causten deleted the loop_operator branch September 16, 2021 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants