2016-10-18 3 views
14

Атрибут System.Runtime.CompilerServices.MethodImplAttribute может использоваться для предоставления подсказок компилятору JIT о том, как обрабатывать декорированный метод. В частности, опция MethodImplOptions.AggressiveInlining инструктирует компилятор, если это возможно, встроить метод, на который это возможно. К сожалению, компилятор F #, кажется, просто игнорирует этот атрибут при генерации IL.Применение методаImplOptions.AggressiveInlining к функциям F #

Пример: Следующий код C#

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

переведен на

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

Обратите внимание на "aggressiveinlining" флага.

Это F # код однако

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

становится

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

Нет "aggressiveinlining". Я также попытался применить атрибут к статическим и нестационарным методам правильных классов (type ...), но результат тот же.

Если же я применить его к пользовательскому индексатор, например, так

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

полученный IL является

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

... хотя я не уверен в том, что эквивалентно " агрессивный ", сгенерированный компилятором C#.

Является ли это поведение желаемым/ожидаемым? Это ошибка в компиляторе F #?

(Примечание: Я знаю о F # inline ключевого слова, но это работает только для F # клиентов моей библиотеки, а не C# потребителей.)

+0

AFAIK Этот атрибут используется JIT: er, чтобы намекнуть, что метод должен быть встроен. Поэтому, если это правильно, вы должны проверить генерирующий машинный код.Заметка; это не так просто, как просмотр разборки в .NET, поскольку с отладчиком, работающим с JIT: er гораздо менее агрессивным. – FuleSnabel

+0

Awww, я понимаю, что вы имеете в виду. По-видимому, в мета-данных IL отсутствует. Позвольте мне проверить это позже. – FuleSnabel

+3

Похоже, что соблюдаются только 'PreserveSig',' Synchronized' и 'NoInlining' - см.' ComputeMethodImplAttribs' в [IlxGen.fs] (https://github.com/Microsoft/visualfsharp/blob/master/src/fsharp /IlxGen.fs) – kvb

ответ

6

@kvb правильно, что кажется F# компилятора кажется раздеться из MethodImpl.

ComputeMethodImplAttribs в IlxGen.fs вызывается для вычисления атрибутов метода.

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 

присмотревшись вокруг строки: 4990:

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

Первые filter фильтры прочь MethodImplAttribute.

Теперь я немного искал логическое обоснование, но этот код восходит к latkin первоначальному фиксации. Я действительно думаю, что, вероятно, неправильно снимать MethodImpl, особенно для AggressiveInlining, который, я считаю, влияет на JIT: ing, поэтому он должен быть в сборке.

Я бы порекомендовал регистрировать issue. Возможно, вы получите хотя бы объяснение.

+3

Фильтрация атрибута имеет смысл, потому что это действительно псевдо-пользовательский атрибут и не должен присутствовать как фактический пользовательский атрибут в метаданных (см., Например, http://weblog.ikvm.net/2008/ 11/25/PseudoCustomAttributes.aspx). – kvb

+0

Я открою вопрос о репо GitHub. – Frank

+2

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

Смежные вопросы