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

Optimization of inlined code doesn't always reduce Some allocations for optional arguments #6532

Closed
dsyme opened this issue Apr 14, 2019 · 0 comments
Labels
Milestone

Comments

@dsyme
Copy link
Contributor

dsyme commented Apr 14, 2019

Summary

Optimization of inlined code doesn't always reduce Some allocations implied by optional arguments even when all consumption points in inlined code are match Some/None on the argument.

Description

One of the reasons the F# compiler includes an optimizer is to allow "inlined" code to be reduced via the use of known information at each callsite. For example, this applies particularly when an "inline" method includes optional arguments.

This "code+optimization" pattern is used in Fabulous to make the compiler pre-compute the size of property bags and reduce away zillions of un-filled attributes.

For example, consider this:

type A() = class end

type C() = 
    static member inline F (?x1: A, ?x2: A) = 
        let count = 0
        let count = match x1 with None -> count | Some _ -> count + 1
        let count = match x2 with None -> count | Some _ -> count + 1
        let attribs = ResizeArray<_>(count)
        match x1 with None -> () | Some v1 -> attribs.Add(v1)
        match x2 with None -> () | Some v2 -> attribs.Add(v2)
        attribs

Now, consider when this code is called and no value is given for either argument:

let test() = 
    C.F ()

In this case the code is correctly reduced to the equivalent of:

        let x = A()
        let d = ResizeArray<_>(0)
        d

So the F# compiler optimizer has correctly counted "zero attributes" and reduced away all implied Some values for the optional arguments.

However, consider what happens when one or two values are specified (and the values are not simple constants like 1 or a string):

let test2() = 
    C.F (x=A())

We would expect this code to be inlined and reduced to the equivalent of

        let x = A()
        let d = ResizeArray<_>(1)
        d.Add(a)
        d

However, this doesn't happen perfectly, and instead we get a residual Some value:

        let x = Some(A())
        let d = ResizeArray<_>(1)
        d.Add(a.Value)
        d

Repro steps

Provide the steps required to reproduce the problem

  1. Compile the code above

  2. Observe the IL code for test2

Expected behavior

Expected: no allocations of Some values and well-optimized code is produced

Actual behavior

One residual Some allocation

Known workarounds

Ignore and accept the allocation as a fact of life

Related information

VS2019 F# master

@dsyme dsyme changed the title Optimization of inlined code doesn't always reduce optional arguments Optimization of inlined code doesn't always reduce Some allocations for optional arguments Apr 14, 2019
@cartermp cartermp added this to the 16.1 milestone Apr 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants