Почему не работают встроенные функции C# с параметрами структуры?

StackOverflow https://stackoverflow.com/questions/1049564

  •  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 в тот момент оказался недостаточно умным.

Другие советы

Здесьэто лучшая статья, описывающая, почему некоторые методы не будут встроены.И здесь — это запись обратной связи MS Connect с комментариями, включающими результаты тестов (FWIW).

Немного уточнений.В этом блоге не обсуждается, что C# будет или не будет встроенным.Здесь обсуждается, что JITer будет или не будет встроен.

Я не знаю, почему JITer не встраивает методы со структурами в качестве формальных параметров.
Однако я относительно уверен, что если они не будут встраивать метод с параметром структуры, то создание структуры по ссылке не изменит это решение.

Мое первое предположение: это потребует изменения размера кадра стека, созданного для этого метода.При использовании ссылочного типа (класса) кадр стека должен позволять хранить указатель (на объект в куче), тогда как при использовании структуры ALL примитивные поля структур должны быть добавлены к отпечатку кадра стека.

Это проблема между типом значения и ссылочным типом, как вы заметили.Представьте, что у вас есть функция, имеющая аргумент типа значения (скажем, int), и вы измените эту переменную в функции.Очевидно, это имеет никаких побочных эффектов в вызывающей функции, поскольку вы передали по значению, а не по ссылке.

Теперь представьте, что вы встраиваете тот же код.Вдруг, ваша переменная меняется в вызывающей функции!Не то, что вы намеревались.

Вероятно, вы сможете убедить себя, продумав тот же сценарий со ссылочным типом, который встраивание не имеет проблем с параметрами по ссылке - изменение исходной функции имеет те же последствия, что и изменение встроенной версии этой функции.

Вот ПОЧЕМУ это не допускается, но я согласен, что у вас должен быть какой-то способ сообщить компилятору, что вы «гарантируете», что ваша функция не имеет побочных эффектов на аргументы типа значения.Во многих случаях это может быть хорошо по соображениям производительности.

Кстати, вы всегда можете объявить уровень модуля int и обойти аргумент целиком :) Гораздо уродливее, но в конечном итоге вы встроите рассматриваемую функцию.

Что бы вы ни решили сделать, удачи!

РЕДАКТИРОВАТЬ:Я только что вспомнил, что на Irix (операционная система SGI) существовал древний компилятор C, в котором действительно была опция компилятора для этого, позволяющая «форсировать» встраивание.Так что это можно сделать, но я согласен с сделанным здесь выбором, выбрав менее подверженное ошибкам значение по умолчанию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top