Comments 14
if else быстрее всех в обоих рантаймах, я правильно понял?
У вас не совсем одинаковые методы. В случае с ветвлением у вас попугай — это по сути дефолтная ветка (вы не делаете проверку на попугая), а вот в switch
блоке делается дополнительная проверка и есть дополнительный дефолтный кейс с исключением. Ожидаемо switch
медленнее.
Еще было бы интересно померить switch
-expression. Если сделать попугая дефолтной веткой везде, switch
генерируют похожий код (хотя expression имеет дополнительную локальную переменную), см. SharpLab.
UPD: В примере я явно указал базовый тип перечисления как byte
, что привело к возникновению разных неоптимальных asm-инструкций (что странно). Оставив тип перечисления по умолчанию (int32), asm-код для обоих switch
оказался идентичным (как и ожидалось). Ссылка заменена.
Могу сразу сказать, что если у вас где-то встретилось
public static Animal CreateAnimalByTernaryOperator(bool isCat)
{
return isCat ? (Animal)new Cat() : new Dog();
}
То где-то вы свернули не туда. В очень редких случаях такое бывает нужно, но тогда нужно заводить нормальный энум enum AnimalType { Cat, Dog }
с самого начала, а не ждать пока появятся попугаи.
То, что для тернарного оператора и иф-элса дефолтным бранчом выбираются разные это забавный факт, который в реальности вряд ли можно как-то использовать. Впрочем, я этого не знал.
Какая-то уж совсем преждевременная оптимизация получается.
На фоне выделений памяти и прочего.
Во-первых, есть бранч предиктор, который снизит стоимость джампа почти до нуля (если мы предполагаем. что true/false приходит достаточно стабильно, если нет, то какая ветка дефолтная уже не важно).
Во-вторых если мне будет жалко одного такта на джамп то я скорее поменяю ветки if/else (есть автоматический рефакторинг в студии и райдере), чем буду тернарник делать. Тем более, что он не умеет во что-то чуть более сложное чем 2 экспрешна.
Ну а в-третьих в языке с ГЦ и миллионом ненужных аллокаций это всё курам на смех. LINQ в 10 раз медленнее циклов, но все пишут на них (кроме избранных, которые и верстку рендерят на стрингбилдерах).
Есть некоторая грань, до которой перфоманс можно получать бесплатно, просто грамотно продумывая архитектуру, а за ней начинается безумие, когда лучше взять какой нибудь раст и иметь 1500% буст перфоманса без пляски над тем что джит выплюнул. Тем более, что это нигде не документировано, так что вчера там был brtrue, а завтра — brfalse. Да еще и tiered JIT есть, который может увидеть паттерн и спокойно перекомпилировать, поменяв ветки местами.
.NetCore 3.0, выходит, на ~25% медленней?
Так как IL-код для .NetCore и .NET Framework одинаковый, надо посмотреть, какие команды процессору получаются в результате JIT-компиляции. Но для этого нужно читать исходники JIT'а, что, скорее всего, не самое простое занятие.
Сравниваем c# операторы ?: vs if-else vs switch