Разница в производительности равенство? ((object) obj1 == (object) obj2) vs. object.ReferenceEquals(obj1, obj2)
Есть ли дополнительные накладные расходы при использовании стилей метода object.ReferenceEquals
, используя ((object)obj1 == (object)obj2)
?
В первом случае будет задействован вызов статического метода, и в обоих случаях будет задействована некоторая форма каста для объекта.
Даже если компилятор уравновешивает эти методы, как насчет неравенства?
(object)obj != null
по сравнению с...
!object.ReferenceEquals(obj,null)
Я предполагаю, что в какой-то момент произойдет логическое отрицание либо внутри оператора! =, либо применительно к результату метода ReferenceEquals. Как вы думаете?
Также есть проблема читаемости. ReferenceEquals кажется более четким при проверке равенства, но для неравенства можно пропустить !
, предшествующий object.ReferenceEquals
, тогда как !=
в первом варианте трудно упустить.
Ответы
Ответ 1
Есть ли дополнительные накладные расходы при использовании объекта. МетодReferenceEquals
Нет. Метод непосредственно содержит минимальное описание IL для выполнения контрольной проверки равенства (для записи: это эквивалентно оператору VB Is
) и часто будет встроен JIT (особенно при таргетинге на x64), поэтому существует no служебный.
О читаемости: Я лично считаю, что object.ReferenceEquals
потенциально более читабельна (даже в отрицательной форме), потому что она явно выражает свою семантику. Приведение к object
может смущать некоторых программистов.
Я только что нашел статью, обсуждающую это. Он предпочитает (object)x == y
, потому что след IL меньше. Он утверждает, что это могло бы облегчить вложение метода X
с использованием этого сравнения. Однако (без каких-либо подробных знаний JIT, но логически и интуитивно) я считаю, что это неправильно: если JIT ведет себя как оптимизирующий компилятор С++, он рассмотрит этот метод после наложения вызова на ReferenceEquals
, поэтому (ради метода inlining X
) объем памяти будет одинаковым в любом случае.
То есть: выбор одного способа над другим не будет иметь никакого влияния на JIT и, следовательно, на производительность.
Ответ 2
В отличие от ответов здесь я нашел (object) ==
быстрее, чем object.ReferenceEquals
. Что касается того, как быстрее, очень незначительно!
Испытательный стенд:
Я знаю, что вам нужна эталонная проверка равенства, но я включаю статический метод object.Equals(,)
, а также в случае классов, где его не переопределять.
Платформа: x86; Конфигурация: релиз сборки
class Person {
}
public static void Benchmark(Action method, int iterations = 10000)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < iterations; i++)
method();
sw.Stop();
MsgBox.ShowDialog(sw.Elapsed.TotalMilliseconds.ToString());
}
Тест:
Person p1 = new Person();
Person p2 = new Person();
bool b;
Benchmark(() =>
{
b = (object)p1 == (object)p2; //960 ~ 1000ms
b = object.ReferenceEquals(p1, p2); //~ 1250ms
b = object.Equals(p1, p2); //2100ms
b = EqualityComparer<Person>.Default.Equals(p1, p2); //~4000ms
}, 100000000);
Person p1 = new Person();
Person p2 = null;
bool b;
Benchmark(() =>
{
b = (object)p1 == (object)p2; //990 ~ 1000ms
b = object.ReferenceEquals(p1, p2); // 1230 ~ 1260ms
b = object.Equals(p1, p2); //1250 ~ 1300ms
b = EqualityComparer<Person>.Default.Equals(p1, p2); //~3100ms
}, 100000000);
Person p1 = null;
Person p2 = null;
bool b;
Benchmark(() =>
{
b = (object)p1 == (object)p2; //960 ~ 1000ms
b = object.ReferenceEquals(p1, p2); //1260 ~ 1270ms
b = object.Equals(p1, p2); //1180 ~ 1220ms
b = EqualityComparer<Person>.Default.Equals(p1, p2); //~3100ms
}, 100000000);
Person p1 = new Person();
Person p2 = p1;
bool b;
Benchmark(() =>
{
b = (object)p1 == (object)p2; //960 ~ 1000ms
b = object.ReferenceEquals(p1, p2); //1260 ~ 1280ms
b = object.Equals(p1, p2); //1150 ~ 1200ms
b = EqualityComparer<Person>.Default.Equals(p1, p2); //3700 ~ 3800ms
}, 100000000);
object.Equals(,)
вызывает ReferenceEquals
внутренне, и если они не равны, это вызовет переопределенный виртуальный Equals
метод класса, и, следовательно, вы увидите уведомление о разнице в скорости.
Результаты были согласованы в конфигурации Debug
тоже...
Как уже отмечалось, акцент должен делаться на удобочитаемости/значимости/выявлении намерений.
Ответ 3
Накладные расходы Object.ReferenceEquals находятся только в загрузке аргументов, которые будут удалены в большинстве сценариев. После этого оба объекта Object.ReferenceEquals и operator == сводятся к одному оператору IL ceq. В худшем случае разница будет незначительной.
Что еще более важно, Object.ReferenceEquals гораздо более интенсивно раскрывает, чем (object) o1 == (object) o2. Он четко указывает на код "Я тестирую для ссылочного равенства/идентичности", а не скрываю намерение под кучей отбросов.
Ответ 4
Добавление моих двух центов после многих поздних часов в первичном критическом коде на очень больших кодовых базах с иногда сумасшедшими глубокими глубинами вызовов.
Вне "микроэлемента" в реальном мире JIT имеет гораздо больше проблем и проблем, а niether обладает роскошью времени компиляции С++ WPO, а также простотой компиляторов С# более прямых переводов, и все же все проблемы, связанные с отсутствием всего контекста после компилятора С#.
"Педантичные" формы:
if ((object)a == (object)b) { } // ref equals
if (!((object)a == (object)b)) { } // ref not equals
Если у вас действительно есть перманентные вопросы от честного до бога, взвешенные и мешающие, или вам нужно оказать давление на JIT для нескольких действительно больших широко распространенных классов, это может помочь тонне. То же самое верно с NullOrEmpty vs '(object) str == null || str.Length == 0 '.
Не имея роскоши WPO и во всех случаях, когда многие не знают о том, какие сборки могут загружаться или выгружаться после того, как они взломали JITing, происходят нечеткие недетерминированные вещи в отношении того, что оптимизируется и как.
Это огромная тема, но вот несколько моментов:
-
JIT будет преследовать инфиницию и зарегистрировать оптимизацию глубины вызова вниз до сих пор и полностью зависит от того, что еще происходит в то время. Если вы закончите компиляцию функции вверх по цепочке один раз из-за использования, а еще один дальше по цепочке - другой прогон, вы можете получить радикально разные результаты. Самое худшее для многих приложений, связанных либо с задержкой, либо с пользовательским интерфейсом, - это не-depterminism, а в более крупных приложениях это может быстро складываться.
-
! ((object) a == (объект) b) и (объект) a!= (объект) b не всегда скомпилированы до одного и того же кода, как это верно certianly for! (a == b) и a!= b, даже без каких-либо явных операторов или переопределений Equals. A '(объект) a!= (Объект) b' гораздо чаще запускает .Net имеет больше педантичного звонка в среду выполнения, что очень дорого.
-
Охрана рано и часто с помощью "(объекта)" или "RefEquals", если это очень полезно для JIT, даже если в настоящее время у вас нет переопределения оператора или Equals. (объект) еще лучше. Если вы думаете о том, что должен делать JIT, чтобы определить, может ли тип иметь переопределения, и иметь дело с правилами блаженства (sp) Equality и whatnot, его, как мини-ад, и anyting, которые могут быть опубликованы позже, и вы намерены ref равенство вы спасете от внезапного замедления или коварного кода позже. Если он уже является общедоступным и не запечатанным, JIT can not quarentee, что правила будут или будут иметь время преследовать их.
-
Защита более широко распространенных "нулевых" проверок является еще более важной, хотя и не является частью вопроса OP, поскольку в целом применяются те же правила и проблемы. '(object) a == null' и '! ((object) a == null)' являются эквивалентами педантичности.
Ответ 5
Ранее упомянутая статья об операторе == лучше отображала неполную информацию, по крайней мере, на .NET 4.0 (хорошо она была написана в версии 2.0 раза).
В нем указано, что ReferenceEquals не оптимизируется/встраивается, что верно, только если вы создаете проект с конфигурацией AnyCPU. Установка для "x86" или "x64" делает (object) obj == null и ReferenceEquals (object, null) в конечном итоге идентичной IL, где оба метода являются просто одной командой "ceq" IL.
Итак, ответ: IL, созданный обоими методами, идентичен в версиях Release/x86 или x64.
ReferenceEquals определенно более читабельна, по крайней мере, на мой вкус.
Ответ 6
public static bool ReferenceEquals (Object objA, Object objB) {
return objA == objB;
}
http://referencesource.microsoft.com/#mscorlib/system/object.cs,4d607d6d56a93c7e