Разница в производительности ((объект)obj1 == (объект)obj2) и object.ReferenceEquals( obj1, obj2)
Есть ли дополнительные затраты при использовании object.ReferenceEquals
метод стихи с использованием ((object)obj1 == (object)obj2)
?
В первом случае будет задействован статический вызов метода, и в обоих случаях будет задействована некоторая форма приведения к объекту.
Даже если компилятор уравновешивает эти методы, как насчет неравенства?
(object)obj != null
по сравнению с...
!object.ReferenceEquals(obj,null)
Я предполагаю, что в какой-то момент произойдет логическое отрицание либо внутри оператора!=, Либо применительно к результату метода ReferenceEquals. Как вы думаете?
Существует также проблема читабельности. ReferenceEquals кажется более понятным при проверке равенства, но из-за неравенства можно пропустить !
предшествующий object.ReferenceEquals
тогда как !=
в первом варианте трудно не заметить.
6 ответов
Есть ли дополнительные затраты при использовании метода object.ReferenceEquals
Нет. Метод напрямую содержит минимальное описание IL для проверки равенства ссылок (для записи: он эквивалентен VB Is
оператор) и часто будет вставляться JIT (особенно при нацеливании на x64), поэтому нет накладных расходов.
По поводу читабельности: лично я так думаю object.ReferenceEquals
потенциально более читабелен (даже в отрицательной форме), потому что он явно выражает свою семантику. Актерский состав object
может сбивать с толку некоторых программистов.
Я только что нашел статью, обсуждающую это. Предпочитает (object)x == y
потому что след IL меньше. Утверждается, что это может облегчить использование метода X
используя это сравнение. Однако (без каких-либо подробных знаний о JIT, но логически и интуитивно) я считаю, что это неправильно: если JIT ведет себя как оптимизирующий компилятор C++, он рассмотрит метод после включения вызова ReferenceEquals
так (ради метода встраивания X
) объем памяти будет одинаковым в любом случае.
То есть: выбор одного пути по сравнению с другим никак не повлияет на JIT и, следовательно, на производительность.
Вопреки ответам здесь я нашел (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
конфигурация тоже...
Как указывалось, акцент должен делаться на удобочитаемость / значимость / раскрытие намерения.
Добавив два моих цента после многих поздних часов в критически важном коде, на очень большие кодовые базы с иногда безумной глубиной вызовов.
Помимо "микро-эталона" в реальном мире, JIT имеет гораздо больше проблем и проблем, и ни у кого нет роскоши времени компиляции C++ WPO, ни у простоты компиляторов C# более прямые переводы, и все же все проблемы не обязательно иметь весь контекст после завершения компилятора C#.
"Педантичные" формы:
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 будет гоняться за встраиванием и регистрировать оптимизацию глубины вызова только пока, и полностью зависит от того, что еще происходит в то время. Если вы в конечном итоге скомпилируете функцию в цепочке один раз из-за использования, а еще дальше по цепочке - другой прогон, вы можете получить радикально отличающиеся результаты. Худшее для многих приложений, связанных либо задержкой, либо управляемым пользовательским интерфейсом, это недетерминированность, а в более крупных приложениях это может быстро сложиться.
! ((объект)a == (объект)b) и (объект)a!= (объект) b не всегда компилируются в один и тот же код, как это верно для!(a == b) и a! = б, даже без каких-либо явных операторов или переопределений Equals. A '(объект)a!= (Объект)b' с гораздо большей вероятностью вызовет более педантичный вызов.Net в среду выполнения, что очень дорого.
Заблаговременно и часто используйте '(object)' или 'RefEquals', если это очень полезно для JIT, даже если в настоящее время у вас нет переопределений оператора или Equals. (объект) еще лучше. Если вы думаете о том, что JIT должен сделать, чтобы определить, может ли тип иметь переопределения, и иметь дело с правилами bizantine (sp) Equality и тому подобным, это как мини-ад, и что угодно, что может быть обнародовано позже, и вы намереваетесь ref Равенство вы спасете себя от внезапного замедления или шаткого кода позже. Если оно уже является публичным и не запечатано, JIT не может гарантировать, что правила будут или будут иметь время преследовать их.
Защита обычно более распространенных "нулевых" проверок, вероятно, еще более важна, хотя и не является частью вопроса ОП, поскольку в целом применяются те же правила и проблемы. '(object)a == null' и '!((object)a == null)' являются "педантичными" эквивалентами.
Издержки Object.ReferenceEquals связаны только с загрузкой аргументов, которые в большинстве сценариев JIT будут удалены. После этого и Object.ReferenceEquals, и оператор == сводятся к одному оператору IL ceq. В худшем случае разница будет незначительной.
Что еще более важно, Object.ReferenceEquals гораздо более показателен, чем (объект)o1 == (объект)o2. Это четко указано в коде "Я проверяю на равенство / идентичность ссылок", а не скрываю намерение под кучей забросов.
Ранее упомянутая статья о том, что оператор == лучше, предоставляет неполную информацию, по крайней мере, в.NET 4.0 (ну, это было переписано в 2,0 раза).
В нем говорится, что ReferenceEquals не оптимизируется / встроен, что верно только в том случае, если вы строите свой проект с конфигурацией AnyCPU. При установке значения 'x86' или 'x64' (object)obj == null и ReferenceEquals(object, null) в конечном итоге становится идентичным IL, где оба метода являются просто одной инструкцией 'ceq' IL.
Таким образом, ответ таков: IL, созданный обоими методами, идентичен в сборках Release / x86 или x64.
ReferenceEquals определенно более читабелен, по крайней мере, на мой вкус.
public static bool ReferenceEquals (Object objA, Object objB) {
return objA == objB;
}