Применение MethodImplOptions.AggressiveInlining для функций F#
атрибут System.Runtime.CompilerServices.MethodImplAttribute
можно использовать, чтобы дать подсказки компилятору JIT о том, как обрабатывать оформленный метод. В частности, опция MethodImplOptions.AggressiveInlining
указывает компилятору встроить затронутый метод, если это возможно. К сожалению, компилятор F#, похоже, просто игнорирует этот атрибут при генерации IL.
пример: следующий код 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
}
... хотя я не уверен, эквивалентно ли это флагу" aggressiveinling", генерируемому C# компилятор.
это поведение желательно / ожидается? Это ошибка в компиляторе F#?
(примечание: Я знаю о F# inline
ключевое слово, но это работает только для клиентов F# моей библиотеки, а не для потребителей C#.)
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
начальный коммит. Я думаю, что это, вероятно, неправильно уберите MethodImpl
специально для AggressiveInlining
который, я считаю, влияет на JIT: ing, поэтому он должен быть в сборке.
Я бы рекомендовал зарегистрировать вопрос. По крайней мере, вы можете получить объяснение.