Бонусное видео курса Performance-Aware Programming разбирает, во что на практике обходятся правила «чистого кода». В качестве примера взята классическая иерархия классов из литературы по clean code: shape_base с наследниками square, rectangle, triangle и circle и виртуальной функцией Area. Сумма площадей массива фигур через указатели и вызовы vtable занимает около 35 циклов на фигуру.
Если нарушить первое правило — заменить полиморфизм на enum shape_type, struct shape_union и switch в GetAreaSwitch — фигуры можно хранить плоским массивом без указателей. Компилятор видит всю логику, индирекция исчезает. Скорость вырастает до 24 циклов на фигуру, то есть в 1,5 раза. В терминах железа это откат с iPhone 14 Pro Max на iPhone 11 Pro Max — три-четыре года прогресса стёрты.
Дальше нарушают правило «не знать внутренностей». В switch-версии видно: все формулы площади похожи — widthheight с каким-то коэффициентом. Если квадрату и кругу продублировать width в height, а коэффициенты (1, 1, 0.5, Pi32) вынести в таблицу, вся операция сводится к CTable[Shape.Type]Shape.Width*Shape.Height. Та же задача выполняется за 3–3.5 цикла на фигуру — уже 10x быстрее clean code. По single-thread производительности это прыжок с современных процессоров на средний CPU 2010 года: правила «чистого кода» стирают 12 лет развития железа.
Затем задачу усложняют: появляется виртуальная функция CornerCount, а считать нужно сумму площадей с весом 1/(1+количество углов). В class hierarchy и switch-версии добавляются новые ветвления и вызовы. В табличной версии достаточно поменять значения в CTable — код остаётся прежним. Разрыв с clean code вырастает почти до 15x. Вывод: чем сложнее задача, тем агрессивнее clean code вредит производительности. А если прогнать те же вычисления через слегка оптимизированную AVX-версию, отрыв от «чистой» версии достигает 20–25x. Причём никакой серьёзной оптимизации не делалось — только убрали loop-carried dependency для честных замеров.
Из пяти правил, реально влияющих на структуру кода — полиморфизм вместо switch, запрет на знание внутренностей, маленькие функции, одна функция = одно дело, DRY — четыре оказываются вредными. Единственное разумное — DRY: не дублировать один и тот же код. Для скорости это не мешает. Остальные правила прячут логику от компилятора за виртуальными вызовами и разбивают код на изолированные куски, которые сложно оптимизировать и самому программисту. В итоге «чистый код» стоит десятилетий прогресса hardware, и если его преподавать — нужна звёздочка: ваш код станет в 15 раз медленнее или больше.