-
Notifications
You must be signed in to change notification settings - Fork 997
interp: do not unroll loops #2561
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
|
I feel like there should be a threshold here. There certainly is init code with loops that makes sense to run that incidentally runs an instruction twice: say, a strconv or something that's looping over a string. For testing, it would probably be handy to have a verbosity flag that prints out when an init function is being move to runtime and control how many iterations is considered "looping". |
|
To clarify: this doesn't mean that an instruction being run twice will trigger a revert. This requires the instruction to be forced to runtime both times within the same function invocation. |
In both of these cases I think a revert is correct. |
|
Odd that the |
|
Anything stopping this from being merged now? |
|
I do not think so, but I still need a review. |
|
All tests passing with latest |
aykevl
left a comment
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.
Looks good to me. I'm only concerned about one possible slowdown.
interp/interpreter.go
Outdated
|
|
||
| // Track what blocks have run instructions at runtime. | ||
| // This is used to prevent unrolling. | ||
| runtimeBlocks := make(map[int]struct{}) |
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've noticed that heap allocations are a significant slowdown, so I think it would be a good idea to move the creation of the map to where anything is inserted. Like:
| runtimeBlocks := make(map[int]struct{}) | |
| var runtimeBlocks map[int]struct{} |
And then:
// Flag the last block as having run stuff at runtime.
if runtimeBlocks == nil {
runtimeBlocks = make(map[int]struct{})
}
runtimeBlocks[lastBB] = struct{}{}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 this case, the map will be used in any situation with multiple basic blocks. It might be more efficient for me to reuse these maps within an interp pass instead.
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.
Only when there are instructions run at runtime, right? That should not happen very often. Most functions should go through interp without any instructions emitted at runtime.
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.
Oh yeah, was just a bit confused coming back to this after a while.
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.
Done
This change triggers a revert whenever a basic block runs instructions at runtime twice. As a result, a loop body with runtime-only instructions will no longer be unrolled. This should help some extreme cases where loops can be expanded into hundreds or thousands of instructions.
|
Rebased and made the requested change |
|
Now merging, thank you very much @niaow |
This change triggers a revert whenever a basic block runs instructions at runtime twice.
As a result, a loop body with runtime-only instructions will no longer be unrolled.
This should help some extreme cases where loops can be expanded into hundreds or thousands of instructions.