Вопрос по performance, c#, .net – При равном сравнении буквального значения имеет значение порядок операндов?

6

Я привык писать код как (просто пример)

Request.QueryString["xxxx"] != null

Недавно кто-то сказал, что

null != Request.QueryString["xxxx"]

дает лучшую производительность.

Мне любопытно узнать, действительно ли это приносит какую-то разницу или нет, и если да, то как?

Примечание ~ Выше приведен только пример. Говоря в общем

Будь то

Constant [Operator] Actual Value (e.g. 1 == Convert.ToInt32(textbox1.text))

лучше, чем

Actual Value [Operator] Constant (e.g. Convert.ToInt32(textbox1.text) == 1)

Спасибо

Ваш Ответ

4   ответа
15

What

используемый для языков со многими неявными преобразованиями типов (дружелюбный по имени Йода из персонажа Звездных войн из-за егоOSV порядок слов). Иногда это даже предписывается и требуется руководящими принципами проекта для предотвращения каких-либо ошибок, вытекающих из опечаток. Разные языки имеют разныеvariations и расширения этого стиля, но я ограничу этот ответ C и C #.

Why Yes

Например, в C вы можете написать:

int a = CalculateValue();
if (a = CalculateAnotherValue()) {
    /* Do something */
}

Этот код будет назначенa значение, возвращаемое изCalculateValue() затем оно перезапишет значение с результатомCalculateAnotherValue() и если он не равен нулю, тогда он выполнит код. Возможно, это не то, что намеревалось сделать, а назначение вif выражение это ошибка. Из вашего примера сNULL тогда вы можете иметь:

if (pBuffer = NULL)

Опять же, вероятно, это не то, что вам нужно (и это довольно распространенная ошибка), вы назначите NULL для указателя, и условие всегда будет ложным. Если вы напишите:

if (NULL = pBuffer)

Он не скомпилируется (потому что вы не можете присвоить значение литералу), и вы получите ошибку во время компиляции.

Этот вид проверок времени компиляции - не единственная причина для использования этого стиля кодирования, посмотрите этот код C # (очень распространенный):

if (text != null && text.Equals("something", StringComparison.InvariantCulture)
    DoSomething();

Можно заключить договор на:

if ("something".Equals(text, StringComparison.InvariantCulture))
    DoSomething();
Why No

В C #usually это не имеет значения. Это практикаinherited из C / C ++. Потому что в C # выражения автоматически не преобразуются вbool затем следующий код не скомпилируется:

if (Request.QueryString["PartnerID"] = null)

Тогда эта практика бесполезна в C # (за исключением того, что @IlianPinzon указал в комментариях), ничего о производительности она не использовала только для того, чтобы избежать подобных ошибок.

О последнем примере вWhy yes В разделе проблема читаемости, чтобы написать"something".Equals(text) все равно, что сказать «счастлив ли парень» вместо "если парень счастлив".

Performance

Начиная с этих функций:

static bool TestRight(object value)
{ return value == null; }

static bool TestLeft(object value)
{ return null == value; }

Они производят следующие IL:

.maxstack 2
.locals init ([0] bool CS$1$0000)
L_0000: nop 
L_0001: ldnull 
L_0002: ldarg.0 
L_0003: ceq 
L_0005: stloc.0 
L_0006: br.s L_0008
L_0008: ldloc.0 
L_0009: ret 

Единственное различие заключается в строках L_0001 и L_0002, они просто меняются местами, но порядок их операндов не меняет поведениеceq, Даже если вы переопределитеEquals() Метод JIT-компилятор выдаст одинаковый ассемблерный код для обоих выражений (потому что сравнение всегда будет выполнятьсяEquals(), null являетсяtypeless).

Ситуация может быть более сложной, если сравнение включает в себя определяемый пользователем модуль сравнения равенства, в этом случае нет правила, и оно будет зависеть от эффективногоop_Equals реализация. Например это== реализация:

public static bool operator==(MyType lhs, MyType rhs)
{
    if (Object.ReferenceEquals(lhs, rhs))
        return true;

    if (Object.ReferenceEquals(lhs, null))
        return false;

    return lhs.Equals(rhs);
}

В этом случае, если первый операндnull выполнение будет немного быстрее (потому чтоMyType.Equals() не будет даже вызван), но это увеличение производительности очень мало: вы экономите одно сравнение, несколько переходов и вызов виртуальной функции. Более того, вы можете переписать функцию так, чтобы она была быстрее в противоположном случае (если вы действительно в этом заинтересованы).

Здесь небольшой тест, гдеMyType.Equals(object) просто возвращаетсяtrue для любого неnull параметр. Тест будет зацикленInt32.MaxValue раз:

Operation       Total time (ms)
lhs == null               10521
null == lhs                2346

Кажется, «оптимизирован» версия, которая позволяет избежать ненужного вызоваEquals() в пять раз быстрее, но обратите внимание, что число циклов очень велико и фактическая реализацияEquals() пуст, истинная реализация уменьшит относительные накладные расходы при вызове функции (и, вероятно, у вас будет что-то еще, кроме этогоmicro-optimization сделать). Для системных классов вы не можете полагаться на эту информацию, напримерString.Equals() всегда будет вызываться операторами (независимо от порядка), но вы не можете предполагать, что один путь кода быстрее, и этот факт не изменится в будущих версиях платформы (или для разных архитектур ЦП).

@ Маттен Я добавил несколько примеров.
@ IlianPinzon Вы правы, с bool это имеет смысл, но ... мне это очень не понравилось из-за читабельности (я знаю, что мне нравится), что я готов заплатить цену :)
Мне любопытно, я слышал об этом, но я не знаком с C / C ++. Почему часто было иметь постоянную на левой стороне?
+1 за замечание о перегруженных пользователем операторах сравнения.
@ Adriano: есть один сценарий, где вы все равно можете его укусить в C #. Попробуйте заменитьint сbool, Но да, условия Yoda вышли из моды в C #.
4

это не совсем так. Операторы сравнения должны оценивать обе их стороны, поэтому нет смысла размещать константы слева. Но их расположение слева (так называемый стиль Yoda) уменьшает количество ошибок кодирования в языках, где вам разрешено использовать оператор присваивания внутри условного выражения, и вы непреднамеренно ошибочно набрали оператор сравнения== как единый=:

// What you intended to write
if (a == 6) ...
// What you wrote instead
if (a = 6) ... // --> always true as the value of a is changed to 6
// What if Yoda style is used
if (6 = a) ... // --> compile time error
Это не единственная причина, по сравнению с нулем. В некоторых случаях это может помешать перегруженному оператору == попасть в бесконечный цикл при неправильном кодировании, особенно если класс принадлежит сторонней библиотеке, которой вы не можете доверять на 100%.
0

Constant [Operator] Actual Value известен какЙода Условия потому что это как & quot; если это синий & # x2013; это небо & quot; или & quot; если оно высокое & # x2013; это мужчина. & quot;

Использование этого стиля условий было популярно на C / C ++, где вы могли присвоить значение переменной при вводе= вместо==, В C # нет смысла использовать условия Yoda.

Также я сомневаюсь, что у него есть лучшие показатели. Даже если он имеет некоторое преимущество в производительности, он будет очень маленьким. Соответствие меньше, чем недостатки читабельности.

2

Это вопрос стиля. Кто-то предпочитает писать

if (N == var) ...

только потому, что если вы пропустили один= (общий случай для начинающих)

if (N = var)

гдеN постоянно, компилятор выдаст ошибку.

В случае

if (var = N)

он может дать вам предупреждение, а может и нет (зависит от компилятора и флагов). Так что иногда бывает достаточно сложно определить, в чем проблема.

Производительность обоих вариантов одинакова. Выберите свой стиль и следуйте ему.

Похожие вопросы