2016-10-18 79 views
14

Za pomocą atrybutu System.Runtime.CompilerServices.MethodImplAttribute można podać wskazówki kompilatorowi JIT dotyczące sposobu obsługi dekorowanej metody. W szczególności opcja MethodImplOptions.AggressiveInlining instruuje kompilator, aby w razie potrzeby zainicjował metodę, której dotyczy problem. Niestety kompilator F # wydaje się po prostu ignorować ten atrybut podczas generowania IL.Zastosowanie MethodImplOptions.AggressiveInlining do funkcji F #

Przykład: Następujący kod C#

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

jest tłumaczony

.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 
} 

Uwaga "aggressiveinlining" Flag.

F # Kod ten jednak

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

staje

.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 
} 

Nie "aggressiveinlining". Próbowałem również zastosować ten atrybut do statycznych i niestatycznych metod właściwych klas (type ...), ale wynik jest taki sam.

Jeśli jednak mogę zastosować go do niestandardowych indekser, tak jak

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

otrzymany IL jest

.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 
} 

... choć nie jestem pewien, czy to jest równoznaczne z " agresywna flaga generowana przez kompilator C#.

Czy to pożądane zachowanie/oczekiwane? Czy jest to błąd w kompilatorze F #?

(Uwaga: Jestem świadomy F # inline hasła, ale to działa tylko dla F # klientów mojej bibliotece, a nie C# konsumentów.)

+0

AFAIK ten atrybut jest używany przez JIT: er, aby wskazać, że metoda powinna być inline. Jeśli tak, powinieneś sprawdzić wygenerowany kod maszyny.Uwaga; nie jest tak proste, jak przeglądanie demontażu w .NET, jak w przypadku debuggera z JIT: er jest znacznie mniej agresywny. – FuleSnabel

+0

Awww, rozumiem co masz na myśli. Wydaje się, że brakuje go w meta danych IL. Pozwól mi to sprawdzić później. – FuleSnabel

+3

Wygląda na to, że respektowane są tylko 'PreserveSig',' Synchronized' oraz 'NoInlining' - patrz' ComputeMethodImplAttribs' w [IlxGen.fs] (https://github.com/Microsoft/visualfsharp/blob/master/src/fsharp /IlxGen.fs) – kvb

Odpowiedz

6

@kvb jest, że wydaje się, że F# kompilator wydaje się rozebrać się z MethodImpl.

ComputeMethodImplAttribs w IlxGen.fs jest wywoływana w celu obliczenia atrybutów metody.

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 

Przyglądając się uważnie wokół rzędu: 4990:

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

Pierwsze filter filtry dala MethodImplAttribute.

Teraz szukałem sposobu na znalezienie uzasadnienia, ale ten kod pochodzi z latkin początkowego zatwierdzenia. Sądzę, że prawdopodobnie źle jest usunąć MethodImpl, szczególnie dla AggressiveInlining, co jak sądzę wpływa na JIT: dlatego też musi być w zespole.

Polecam rejestrację issue. Być może uda ci się przynajmniej wytłumaczyć.

+3

Filtrowanie atrybutu ma sens, ponieważ jest to naprawdę pseudo-niestandardowy atrybut i nie powinien być obecny jako rzeczywisty atrybut niestandardowy w metadanych (zobacz np. Http://weblog.ikvm.net/2008/ 11/25/PseudoCustomAttributes.aspx). – kvb

+0

Otwieram problem w repozytorium GitHub. – Frank

+2

c.f. https://github.com/Microsoft/visualfsharp/issues/1637 – Frank

Powiązane problemy