Может ли управляемый код повлиять на параллелизм на уровне команд? - PullRequest
1 голос
/ 07 октября 2011

Могу ли я повлиять на параллелизм на уровне команд при написании кода на C #?Другими словами, есть ли способ, которым я могу «помочь» компилятору генерировать код, который лучше всего использует ILP?Я спрашиваю об этом, потому что я пытаюсь абстрагироваться от нескольких концепций машинной архитектуры, и мне нужно знать, возможно ли это.Если нет, то я буду гарантированно абстрагироваться от ILP.

EDIT : вы заметите, что я не хочу использовать ILP с использованием C # в любом случае.Мой вопрос с точностью до наоборот.Перефразируя: «Я надеюсь, что нет никакого способа использовать ILP из C #»

Спасибо.

Ответы [ 4 ]

1 голос
/ 07 октября 2011

ILP - это особенность процессора. У тебя нет возможности это контролировать. Компиляторы стараются изо всех сил использовать его, разрывая цепочки зависимостей.

Это может включать в себя .Net JIT Compiler, однако у меня нет доказательств этого.

0 голосов
/ 18 декабря 2013

Вы можете использовать ILP в CLI.Итак, короткий ответ: Нет.

Немного длиннее:

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

«Короткий» пример:

static void Main( string[] args )
{
  const int ITERATION_NUMBER = 100;

  TimeSpan[] normal = new TimeSpan[ITERATION_NUMBER];
  TimeSpan[] ilp = new TimeSpan[ITERATION_NUMBER];

  int SIZE = 4000000;
  float[] data = new float[SIZE];
  float safe = 0.0f;

  //Normal for
  Stopwatch sw = new Stopwatch();

  for (int iteration = 0; iteration < ITERATION_NUMBER; iteration++)
  {
    //Initialization
    for (int i = 0; i < data.Length; i++)
    {
      data[i] = 1.0f;
    }

    sw.Start();
    for (int index = 0; index < data.Length; index++)
    {
      data[index] /= 3.0f * data[index] > 2.0f / data[index] ? 2.0f / data[index] : 3.0f * data[index];
    }
    sw.Stop();
    normal[iteration] = sw.Elapsed;

    safe = data[0];

    //Initialization
    for (int i = 0; i < data.Length; i++)
    {
      data[i] = 1.0f;
    }

    sw.Reset();

    //ILP For
    sw.Start();
    float ac1, ac2, ac3, ac4;
    int length = data.Length / 4;
    for (int i = 0; i < length; i++)
    {
      int index0 = i << 2;

      int index1 = index0;
      int index2 = index0 + 1;
      int index3 = index0 + 2;
      int index4 = index0 + 3;

      ac1 = 3.0f * data[index1] > 2.0f / data[index1] ? 2.0f / data[index1] : 3.0f * data[index1];

      ac2 = 3.0f * data[index2] > 2.0f / data[index2] ? 2.0f / data[index2] : 3.0f * data[index2];

      ac3 = 3.0f * data[index3] > 2.0f / data[index3] ? 2.0f / data[index3] : 3.0f * data[index3];

      ac4 = 3.0f * data[index4] > 2.0f / data[index4] ? 2.0f / data[index4] : 3.0f * data[index4];

      data[index1] /= ac1;
      data[index2] /= ac2;
      data[index3] /= ac3;
      data[index4] /= ac4;
    }
    sw.Stop();
    ilp[iteration] = sw.Elapsed;

    sw.Reset();
  }
  Console.WriteLine(data.All(item => item == data[0]));
  Console.WriteLine(data[0] == safe);
  Console.WriteLine();

  double normalElapsed = normal.Max(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("Normal Max.: {0}", normalElapsed));
  double ilpElapsed = ilp.Max(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("ILP    Max.: {0}", ilpElapsed));
  Console.WriteLine();
  normalElapsed = normal.Average(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("Normal Avg.: {0}", normalElapsed));
  ilpElapsed = ilp.Average(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("ILP    Avg.: {0}", ilpElapsed));
  Console.WriteLine();
  normalElapsed = normal.Min(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("Normal Min.: {0}", normalElapsed));
  ilpElapsed = ilp.Min(time => time.TotalMilliseconds);
  Console.WriteLine(String.Format("ILP    Min.: {0}", ilpElapsed));
}

Результаты (в .Net Framework 4.0 Профиль клиента, выпуск):

На виртуальной машине (я думаю, без ILP):

True True

Нор Макс .: 111,1894
НЛП Макс .: 106,886

Нор .: 78, 163619
ILP Avg .: 77,682513

Nor Min .: 58,3035
ILP Min .: 56,7672

на ксеноне :

True True

Nor Макс .: 40,5892
ILP Макс .: 30,8906

Nor Avg .: 35,637308
ILP Avg.: 25,45341

Nor Мин .: 34,4247
ILP Мин .: 23,7888

Объяснение результатов:

В Debug нетОптимизация применяется компилятором, но второй цикл for является более оптимальным, чем первый, поэтому существует существенная разница.

ThКажется, что ответ заключается в результатах выполнения сборок, собранных в режиме Release.Компилятор IL / JIT-er позволяют минимизировать снижение производительности (я думаю, даже ILP).Но если вы создаете код, подобный второму циклу for, вы можете достичь лучших результатов в особых случаях, и второй цикл может превзойти первый в некоторых архитектурах.Но

Вы находитесь во власти JIT

, как уже упоминалось, к сожалению.Грустная вещь, что нет упоминания о реализации, может определить больше оптимизации, как ILP (короткий параграф может быть помещен в спецификацию).Но они не могут перечислить все формы архитектурных вариантов кода, и CLI находится на более высоком уровне:

Это хорошо абстрагируется от языков .NET и IL.

Это очень сложная задача, ответить на нее только экспериментальным путем.Я не думаю, что мы могли бы получить гораздо более точный ответ таким образом.И я думаю, что Вопрос вводит в заблуждение, потому что это не зависит от C #, это зависит от реализации CLI.

Могло быть много влияющих факторов, и затрудняется правильно ответить на вопрос, подобный этому, думая оJIT, пока мы не воспринимаем его как «черный ящик».

На странице 512-513: http://www.ecma -international.org / публикации / файлы / ECMA-ST / ECMA я нашел информацию о векторизации и автопоточности-335.pdf

Я думаю, что они не указывают явно, как JIT-er должен вести себя в подобных случаях, и импелентеры могут выбрать способ оптимизации.Поэтому я думаю, что вы можете повлиять, если вы сможете написать более оптимальный код, и JIT попытается использовать ILP, если это возможно / реализовано.

Я думаю, потому что они не указывают, есть возможность.

Таким образом, ответ вроде бы Нет. Я полагаю, что вы не можете абстрагироваться от ILP в случае CLI, если в спецификации это не сказано.

Обновление :

Я уже нашел пост в блоге, но до сих пор его не нашел: http://igoro.com/archive/gallery-of-processor-cache-effects/ Пример четвертый содержит короткий, но правильный ответ на ваш вопрос.

0 голосов
/ 07 октября 2011

НЕТ явного или прямого способа повлиять или дать подсказку компилятору .NET в IL или C # для этого. Это полностью работа компиляторов.

Единственное влияние, которое вы можете оказать на это, состоит в том, чтобы структурировать вашу программу так, чтобы было бы более вероятно (хотя и не гарантировано) сделать это для вас, и было бы трудно узнать, действовало ли оно даже на структуру или не. Это хорошо абстрагируется от языков .NET и IL.

0 голосов
/ 07 октября 2011

Вы находитесь во власти JIT при получении параллелизма на уровне команд. Кто знает, что на самом деле делает оптимизация JIT? Я бы выбрал другой язык, например C ++, если мне это действительно нужно.

Чтобы лучше всего использовать ILP, вам нужно разорвать цепочки зависимостей. Это должно все еще применяться. Смотрите эту тему .

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

...