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

[erlc] Very long compilation time for nested record accesses in guards #8203

Closed
RobinMorisset opened this issue Feb 28, 2024 · 1 comment · Fixed by #8206
Closed

[erlc] Very long compilation time for nested record accesses in guards #8203

RobinMorisset opened this issue Feb 28, 2024 · 1 comment · Fixed by #8206
Assignees
Labels
bug Issue is reported as a bug team:VM Assigned to OTP team VM

Comments

@RobinMorisset
Copy link
Contributor

Describe the bug
Many programs with nested record accesses in a guard take a surprisingly long time to compile (30+ seconds is not rare).
Additionally, that time is extremely sensitive to details of the code, making me suspect that a corner case in the compiler causes an exponential blow-up.

To Reproduce
Run erlc on the following code:

-export([f1/1]).
-record(r1, {rf0}).

f1(_V0) when
    (not #r1{
        rf0 =
            (((true andalso
                (true >
                    #r1{
                        rf0 =
                            (((((#r1{
                                rf0 =
                                    (((#r1{
                                        rf0 =
                                            is_function(
                                                [0, _V0],
                                                0
                                            )
                                    })#r1.rf0)#r1.rf0)#r1.rf0 xor
                                        (floor(0) band
                                            ((((#r1{
                                                rf0 =
                                                    (is_function(
                                                        <<>>,
                                                        1
                                                    )) orelse
                                                        ([])#r1.rf0
                                            })#r1.rf0 rem
                                                (bnot (((true andalso
                                                    #r1.rf0) andalso
                                                    ([])#r1.rf0#r1.rf0)#r1.rf0))) bxor
                                                (((false orelse
                                                    [])#r1.rf0)#r1.rf0)) bxor
                                                byte_size(
                                                    true andalso
                                                        <<>>
                                                )))
                            })#r1.rf0)#r1.rf0)#r1.rf0) andalso [])
                    }#r1.rf0)) andalso
                [])#r1.rf0)#r1.rf0
    }#r1.rf0)#r1.rf0
->
    0.0.

It takes about 10s to compile, which falls to 4s if for example the initial not is removed, to 2s if the _V0 is removed, etc..
I can easily provide many more such test cases, but they resist automated reduction and tend to be significantly larger than this one.

Expected behavior
Compile time which is roughly linear with code size (even quadratic would be fine)

Affected versions

Additional context
This is a low-priority issue since such code is unlikely to be written by humans. I'm only reporting it because it makes it impossible to fuzz record accesses in guards.

@RobinMorisset RobinMorisset added the bug Issue is reported as a bug label Feb 28, 2024
@frej
Copy link
Contributor

frej commented Feb 29, 2024

Running erlc +time it looks like it is mainly the ssa_opt_cse pass. With the initial not it accounts for 70% of the total runtime, without for 62% (of 8s and 3.5s respectively). The next most expensive pass is beam_ssa_bool which accounts for ~17% of the runtime in both cases.

@bjorng bjorng self-assigned this Feb 29, 2024
@bjorng bjorng added the team:VM Assigned to OTP team VM label Feb 29, 2024
bjorng added a commit to bjorng/otp that referenced this issue Feb 29, 2024
The code for nested record access is hugely expanded by the
`erl_expand_records` pass, which can cause the `beam_ssa_opt:cse/1`
pass to become really slow.

This commit applies several otptimizations to `cse/1` to somewhat
mitigate the slowness.

Closes erlang#8203
bjorng added a commit to bjorng/otp that referenced this issue Mar 1, 2024
The code for nested record access is hugely expanded by the
`erl_expand_records` pass, which can cause the `beam_ssa_opt:cse/1`
pass to become really slow. To a lesser extent, the `beam_ssa_bool`
pass can also get slower.

This commit tries to mitigate the issue like so:

* The `beam_ssa_bool` and `beam_ssa_opt:cse/1` passes will now
  evaluate guard BIFs with literal arguments. That will save
  compilation time by quickly discarding unreachable blocks.

* In `beam_ssa_opt:cse/1`, the handling of the maps that keep track of
  expressions previously has been optimized.

Closes erlang#8203
bjorng added a commit to bjorng/otp that referenced this issue Mar 2, 2024
The code for nested record access is hugely expanded by the
`erl_expand_records` pass, which can cause the `beam_ssa_opt:cse/1`
pass to become really slow. To a lesser extent, the `beam_ssa_bool`
pass can also get slower.

This commit tries to mitigate the issue like so:

* The `beam_ssa_bool` and `beam_ssa_opt:cse/1` passes will now
  evaluate guard BIFs with literal arguments. That will save
  compilation time by quickly discarding unreachable blocks.

* In `beam_ssa_opt:cse/1`, the handling of the maps that keep track of
  expressions previously has been optimized.

Closes erlang#8203
bjorng added a commit that referenced this issue Mar 5, 2024
…H-8203

Mitigate slow compilation for nested record access in guards
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue is reported as a bug team:VM Assigned to OTP team VM
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants