Применение метода MethodImplOptions.AggressiveInlining к функциям F #

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

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

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

Обратите внимание на флаг "агрессивный".

Однако этот код 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
}

Нет "агрессивно". Я также попытался применить атрибут к статическим и нестационарным методам правильных классов (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
}

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

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

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

Ответы

Ответ 1

@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 initial commit. Я думаю, что, вероятно, неправильно отделять MethodImpl особенно для AggressiveInlining, который, я считаю, влияет на JIT: ing, поэтому он должен быть в сборке.

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