Почему не работают встроенные функции C# с параметрами структуры?
-
20-08-2019 - |
Вопрос
В http://blogs.msdn.com/ericgu/archive/2004/01/29/64717.aspx, мы узнаем, что C# не поддерживает встраивание методов со структурами в качестве формальных параметров.Связано ли это с потенциальной зависимостью от стека, например.для рекурсии?Если да, могу ли я получить потенциальную выгоду от преобразования параметров структуры в параметры ref, подобные этому?
public int Sum(int i)
{
return array1[i] + array2[i];
}
превращается в:
public int Sum(ref int i)
{
return array1[i] + array2[i];
}
Редактировать:Я пошел попробовать тест, но ничего не могу получить в режиме онлайн.Вот что я попробовал:
class Program
{
private static string result;
static void Main(string[] args)
{
Console.WriteLine(MethodBase.GetCurrentMethod().Name);
Console.WriteLine();
m1();
Console.WriteLine(result);
}
private static void m1()
{
result = MethodBase.GetCurrentMethod().Name;
}
}
Во второй строке он печатает «m1», что указывает на то, что она не была встроена.Я собрал сборку Release и запустил ее с помощью Ctrl-F5 (чтобы не подключать отладчик).Есть идеи?
Решение
Как сказал Джон, это очень старый пост.Я могу подтвердить это в следующем коде:
using System;
using System.Runtime.CompilerServices;
struct MyStruct
{
public MyStruct(int p)
{
X = p;
}
public int X;
// prevents optimization of the whole thing to a constant.
[MethodImpl(MethodImplOptions.NoInlining)]
static int GetSomeNumber()
{
return new Random().Next();
}
static void Main(string[] args)
{
MyStruct x = new MyStruct(GetSomeNumber());
// the following line is to prevent further optimization:
for (int i = inlinetest(x); i != 100 ; i /= 2) ;
}
static int inlinetest(MyStruct x)
{
return x.X + 1;
}
}
inlinetest
метод встроен.
Основной метод разборки:
; set up the stack frame:
00000000 push ebp
00000001 mov ebp,esp
; calls GetSomeNumber:
00000003 call dword ptr ds:[005132D8h]
; inlined function:
00000009 inc eax
; the dummy for loop:
0000000a cmp eax,64h
0000000d je 0000001B
0000000f sar eax,1
00000011 jns 00000016
00000013 adc eax,0
00000016 cmp eax,64h
00000019 jne 0000000F
0000001b pop ebp
0000001c ret
Я тестировал это на x86 .NET Framework 3.5 SP1 в Windows 7 x64 RC.
Поскольку я считал, что нет ничего плохого во встраивании методов с помощью struct
параметры.Вероятно, JIT в тот момент оказался недостаточно умным.
Другие советы
Немного уточнений.В этом блоге не обсуждается, что C# будет или не будет встроенным.Здесь обсуждается, что JITer будет или не будет встроен.
Я не знаю, почему JITer не встраивает методы со структурами в качестве формальных параметров.
Однако я относительно уверен, что если они не будут встраивать метод с параметром структуры, то создание структуры по ссылке не изменит это решение.
Мое первое предположение: это потребует изменения размера кадра стека, созданного для этого метода.При использовании ссылочного типа (класса) кадр стека должен позволять хранить указатель (на объект в куче), тогда как при использовании структуры ALL примитивные поля структур должны быть добавлены к отпечатку кадра стека.
Это проблема между типом значения и ссылочным типом, как вы заметили.Представьте, что у вас есть функция, имеющая аргумент типа значения (скажем, int), и вы измените эту переменную в функции.Очевидно, это имеет никаких побочных эффектов в вызывающей функции, поскольку вы передали по значению, а не по ссылке.
Теперь представьте, что вы встраиваете тот же код.Вдруг, ваша переменная меняется в вызывающей функции!Не то, что вы намеревались.
Вероятно, вы сможете убедить себя, продумав тот же сценарий со ссылочным типом, который встраивание не имеет проблем с параметрами по ссылке - изменение исходной функции имеет те же последствия, что и изменение встроенной версии этой функции.
Вот ПОЧЕМУ это не допускается, но я согласен, что у вас должен быть какой-то способ сообщить компилятору, что вы «гарантируете», что ваша функция не имеет побочных эффектов на аргументы типа значения.Во многих случаях это может быть хорошо по соображениям производительности.
Кстати, вы всегда можете объявить уровень модуля int и обойти аргумент целиком :) Гораздо уродливее, но в конечном итоге вы встроите рассматриваемую функцию.
Что бы вы ни решили сделать, удачи!
РЕДАКТИРОВАТЬ:Я только что вспомнил, что на Irix (операционная система SGI) существовал древний компилятор C, в котором действительно была опция компилятора для этого, позволяющая «форсировать» встраивание.Так что это можно сделать, но я согласен с сделанным здесь выбором, выбрав менее подверженное ошибкам значение по умолчанию.