Влияет ли использование непроверенного контекста на производительность или переносимость в C #?

#c# #.net #performance #portability #unchecked

#c# #.net #Производительность #переносимость #непроверенный

Вопрос:

Я хочу реализовать быструю хеш-функцию, которая будет использовать int тип данных и полагаться на переполнение целых чисел. В MSDN говорится, что для гарантии того, что переполнения не вызывают исключений, я должен использовать unchecked блоки для этого кода.

Предположим, я помещаю только это вычисление в unchecked блок. Будут ли у моего кода какие-либо проблемы с производительностью или переносимостью из-за этого?

Комментарии:

1. @ Marc Gravell: Да, я это знаю, но есть вероятность, что мой код будет повторно использован в проекте с checked принудительным применением, и я хочу, чтобы мой код работал правильно.

2. @ Marc Gravell: На самом деле меня беспокоит то, что у меня останутся все приятные преимущества комбинации MSIL JIT, если я использую unchecked ?

3.вы правы, добавляя unchecked к этому; я пытался подчеркнуть, что он будет вести себя точно так же, как и раньше — да, вы получаете то же поведение MSIL JIT. Действительно, это помогает гарантировать, что вы получите то, что хотите. Как уже отмечалось, теоретически возможно, что какой-то процессор (возможно, где-то на микро-платформе) должен выполнять больше работы в случае checked , но это a: вряд ли будет проблемой, и b: на самом деле не влияет на ваши unchecked вещи — и это полностью деталь реализации.

4. острозубый, похоже, ты путаешь unchecked unsafe . Использование unchecked по-прежнему безопасно и оказывает очень незначительное влияние.

Ответ №1:

проверено добавление только одной инструкции процесса:

 checked
{
    int y = x * x;
05297978  mov         eax,dword ptr [ebp-10h]  
0529797B  imul        eax,dword ptr [ebp-10h]  
0529797F  jno         05297986  //if not overflow: goto 05297986
05297981  call        72A29522  //invoke exception
05297986  mov         dword ptr [ebp-14h],eax  
}

unchecked
{
    int yy = xx * xx;
0529799E  mov         eax,dword ptr [ebp-18h]  
052979A1  imul        eax,dword ptr [ebp-18h]  
052979A5  mov         dword ptr [ebp-1Ch],eax  
}
  

Ответ №2:

Технически checked замедляться должны только блоки. Поэтому я не думаю unchecked , что блок (блок, в котором фреймворк должен выполнять меньше проверок) может замедлиться. Это не переключение контекста или что-то подобное. JIT просто не выдает инструкции для проверки переполнения / переполнения. Теперь ясно, что если бы кто-то создал «специальный» процессор, в котором необходимо имитировать переполнение, и перенес на него Mono, или где переполнение приводит к другим результатам, чем на процессорах Intel, unchecked блок был бы медленнее (потому что JIT должен был бы «имитировать» его). Но обратите внимание, что базовые типы .NET определены в стандарте ECMA. Подписанные целые числа должны быть основаны, например, на двухкомпонентности, а их размер должен составлять 8, 16, 32, 64 бита. Для «странных» процессоров, использующих 36-битные целые числа, не так много места.

Ответ №3:

Как правило, вы можете ожидать, что непроверенная арифметика будет немного быстрее, но практически никогда не стоит задавать противоположный вопрос вашему (а именно: «повлияет ли использование checked на мою производительность?»).

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

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

Ответ №4:

Я создал два метода, один с помощью checked , а другой с помощью unchecked . При изучении IL только одно отличие — mul операция (которая выполняет операцию умножения), для checked mul.ovf генерируется, а для unchecked — mul .

Подводя итог, я считаю, что разница в одной операции процессора не оказывает никакого влияния на производительность, единственное отличие будет в случае использования переполнения checked — в этом случае будет сгенерировано исключение OverflowException, которое, очевидно, замедляет выполнение.

MSDN:

Следующие инструкции Microsoft intermediate language (MSIL) вызывают исключение OverflowException:

  • mul.ovf.
 [Test]
public void Checked()
{
    checked
    {
        int i = int.MaxValue;
        i = i * 100;
        Debug.WriteLine(i);
    }
}

[Test]
public void UnChecked()
{
    unchecked
    {
        int i = int.MaxValue;
        i = i * 100;
        Debug.WriteLine(i);
    }            
}
  

А затем с помощью ILDASM см. IL:

ПРОВЕРЕНО ():

 // Code size       27 (0x1b)
  .maxstack  2
  .locals init ([0] int32 i)
  IL_0000:  nop
  IL_0001:  nop
  IL_0002:  ldc.i4     0x7fffffff
  IL_0007:  stloc.0
  IL_0008:  ldloc.0
  IL_0009:  ldc.i4.s   100
  **IL_000b:  mul.ovf** !!!
  IL_000c:  stloc.0
  IL_000d:  ldloc.0
  IL_000e:  box        [mscorlib]System.Int32
  IL_0013:  call       void [System]System.Diagnostics.Debug::WriteLine ...
  

НЕПРОВЕРЕННЫЙ ():

   // Code size       27 (0x1b)
  .maxstack  2
  .locals init ([0] int32 i)
  IL_0000:  nop
  IL_0001:  nop
  IL_0002:  ldc.i4     0x7fffffff
  IL_0007:  stloc.0
  IL_0008:  ldloc.0
  IL_0009:  ldc.i4.s   100
  **IL_000b:  mul** !!!
  IL_000c:  stloc.0
  IL_000d:  ldloc.0
  IL_000e:  box        [mscorlib]System.Int32
  IL_0013:  call       void [System]System.Diagnostics.Debug::WriteLine(...)
  

Комментарии:

1. Я бы назвал махинации с декомпиляцией. Вместо этого посмотрите на IL — обычно это просто небольшая разница в коде операции. Кроме того, использование byte в одном и int в другом вводит в заблуждение. Если я использую Int32 код для обоих, единственная разница — add vs add.ovf

2. @Marc Gravell ♦: да, я просто изменил это, чтобы оно было более актуальным, в любом случае спасибо за nte!

3. Я считаю, что нам нужно более глубокое описание для обоих mul / mul.ovf относительно того, как они генерируются в инструкциях процессора, и только тогда мы сможем сравнить. Но в целом я считаю, что это показывает, что разница очень мала, одна инструкция на одну операцию — это не так уж много