Domanda Applicazione di MethodImplOptions.AggressiveInlining alle funzioni F #


L'attributo System.Runtime.CompilerServices.MethodImplAttribute può essere usato per dare suggerimenti al compilatore JIT su come gestire il metodo decorato. In particolare, l'opzione MethodImplOptions.AggressiveInlining indica al compilatore di integrare il metodo interessato, se possibile. Sfortunatamente il compilatore F # sembra semplicemente ignorare questo attributo durante la generazione di IL.

Esempio: il seguente codice C #

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Inc(int x) => x + 1;

è tradotto a

.method public hidebysig static int32  Inc(int32 x) cil managed aggressiveinlining
{     
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldc.i4.1
    IL_0002:  add
    IL_0003:  ret
}

Notare la bandiera "aggressiveinlining".

Questo codice F # tuttavia

[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
let inc x = x + 1

diventa

.method public static int32  inc(int32 x) cil managed
{
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldc.i4.1
    IL_0003:  add
    IL_0004:  ret
}

No "aggressiveinlining". Ho anche provato ad applicare l'attributo a metodi statici e non statici di classi appropriate (type ...), ma il risultato è lo stesso.

Se comunque lo applico a un indicizzatore personalizzato, in questo modo

type Dummy =
    member self.Item
        with [<MethodImpl(MethodImplOptions.AggressiveInlining)>] get x = x + 1

l'IL risultante è

.method public hidebysig specialname instance int32 get_Item(int32 x) cil managed
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.MethodImplAttribute::.ctor(valuetype [mscorlib]System.Runtime.CompilerServices.MethodImplOptions) = ( 01 00 00 01 00 00 00 00 ) 
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldarg.1
    IL_0002:  ldc.i4.1
    IL_0003:  add
    IL_0004:  ret
}

... anche se non sono sicuro che sia equivalente al flag "aggressiveinling" generato dal compilatore C #.

Questo comportamento è desiderato / atteso? È un bug nel compilatore F #?

(Nota: sono a conoscenza del F # inline parola chiave, ma funziona solo con i client F # della mia libreria, non con i clienti C #).


14
2017-10-18 09:37


origine


risposte:


@kvb è corretto che sembra il F# il compilatore sembra spogliare il MethodImpl.

ComputeMethodImplAttribs in IlxGen.fs è chiamato per calcolare gli attributi del metodo.

and ComputeMethodImplAttribs cenv (_v:Val) attrs =
    let implflags = 
        match TryFindFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute attrs with
        | Some (Attrib(_,_,[ AttribInt32Arg flags ],_,_,_,_))  -> flags
        | _ -> 0x0

    let hasPreserveSigAttr = 
        match TryFindFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute attrs with
        | Some _ -> true
        | _ -> false

    // strip the MethodImpl pseudo-custom attribute    
    // The following method implementation flags are used here
    // 0x80 - hasPreserveSigImplFlag
    // 0x20 - synchronize
    // (See ECMA 335, Partition II, section 23.1.11 - Flags for methods [MethodImplAttributes]) 
    let attrs = attrs 
                    |> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not) 
                        |> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)
    let hasPreserveSigImplFlag = ((implflags &&& 0x80) <> 0x0) || hasPreserveSigAttr
    let hasSynchronizedImplFlag = (implflags &&& 0x20) <> 0x0
    let hasNoInliningImplFlag = (implflags &&& 0x08) <> 0x0
    hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, attrs

Guardando più da vicino la riga: 4990:

    let attrs = attrs 
                    |> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not) 
                        |> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)

Il primo filter filtra via MethodImplAttribute.

Ora, stavo cercando un po 'di trovare la logica, ma questo codice è tornato a latkin commit iniziale. Penso che sia probabilmente sbagliato togliere il MethodImpl specialmente per AggressiveInlining che credo influenzi il JIT: quindi deve essere presente nell'assemblea.

Consiglierei la registrazione di un problema. Forse puoi avere una spiegazione almeno.


6
2017-10-18 17:04