кэширование (например, целевое кэширование ветвления ), параллельные единицы нагрузки (часть конвейерной обработки, но также такие вещи, как "попадание под промах", которые не останавливают конвейер) и вневыполнение порядка может помочь преобразовать load
- load
- branch
в нечто, что ближе к фиксированному branch
.Складывание / исключение инструкций (каков правильный термин для этого?) На этапе предсказания декодирования или ветвления конвейера также может внести свой вклад.
Однако все это зависит от множества разных вещей: сколько разных ветвейесть целевые показатели (например, сколько различных виртуальных перегрузок вы, вероятно, сработаете), сколько вещей вы зацикливаете (кэш цели филиала «теплый»? как насчет icache / dcache?), как виртуальные таблицы или таблицы косвенностиразмещены в памяти (они удобны для кеша, или каждая новая загрузка vtable, возможно, высвобождает старую vtable?), многократно кешируется ли кэш из-за многоядерного пинг-понга и т. д.Я определенно не являюсь экспертом в этом вопросе, и многие мои знания основаны на изучении встроенных процессоров по порядку, поэтому некоторые из них - экстраполяция. Если у вас есть исправления, не стесняйтесь комментировать!)
Theправильный способ определить, будет ли это проблемой для конкретной программы, - это, конечно, профиль.Если вы можете, сделайте это с помощью аппаратных счетчиков - они могут многое рассказать вам о том, что происходит на различных этапах конвейера.
Редактировать:
КакХанс Пассант отмечает в приведенном выше комментарии Современная оптимизация косвенного взаимодействия внутреннего цикла , ключом к тому, чтобы эти две вещи занимали одинаковое количество времени, является способность эффективно «отбрасывать» более одной инструкции за цикл.Устранение инструкций может помочь в этом, но суперскалярный дизайн , вероятно, более важен (попадание при промахе - очень маленький и конкретный пример, лучше использовать полностью избыточные единицы нагрузки).
ДавайтеВозьмем идеальную ситуацию и предположим, что прямая ветвь - это всего лишь одна инструкция:
branch dest
... и косвенная ветвь - три (возможно, вы можете получить ее двумя, но больше, чем одна):
load vtable from this
load dest from vtable
branch dest
Давайте предположим, что ситуация абсолютно идеальна: * этот и весь виртуальный таблицы находятся в кеше L1, кэш L1 достаточно быстр, чтобы поддерживать амортизированный один цикл на стоимость инструкции для двух нагрузок.(Вы можете даже предположить, что процессор переупорядочил нагрузки и смешал их с более ранними инструкциями, чтобы дать им время завершиться до перехода; это не имеет значения для этого примера.) Также предположим, что целевой кэш ветви горячий, и конвейер отсутствуетсбрасывает стоимость ветвления, и инструкция ветвления сводится к одному циклу (амортизируется).
Теоретический минимум время для первого примера, следовательно, составляет 1 цикл (амортизируется).
Теоретический минимум для второго примера, исключение отсутствующих команд или избыточные функциональные блоки или что-то, что позволит удалить более одной инструкции за цикл, составляет 3 цикла (есть 3 инструкции)!
Косвенныйзагрузка всегда будет медленнее, потому что есть больше инструкций, пока вы не достигнете чего-то вроде суперскалярного дизайна, который позволяет удалять более одной инструкции за цикл.
Как только вы это сделаете, минимум для обоих примеров станет чем-то между 0и 1циклы, опять же, при условии, что все остальное идеально.Возможно, вам нужно иметь более идеальные условия для второго примера, чтобы фактически достичь этого теоретического минимума, чем для первого примера, но теперь это возможно.
В некоторых случаях, которые вас волнуют, вы, вероятно,не будет достигать этого минимума ни для одного примера.Либо целевой кэш ветви будет холодным, либо vtable не будет в кеше данных, либо машина не сможет переупорядочить инструкции, чтобы в полной мере использовать избыточные функциональные блоки.
... здесь начинается профилирование, что, в общем, хорошая идея.
Вы можете просто поддержать небольшую паранойю о виртуалах. См. статью Ноэля Ллописа о ориентированном на данные дизайне , превосходные Слайды объектно-ориентированного программирования и сварливые, но обучающие презентации Майка Актона . Теперь вы внезапно перешли к шаблонам, которыми процессор, вероятно, будет доволен, если вы обрабатываете много данных.
Функции языка высокого уровня, такие как виртуальные, обычно являются компромиссом между выразительностью и контролем. Я, честно говоря, думаю, что, просто повышая свою осведомленность о том, что на самом деле делает виртуальная (не бойтесь время от времени читать представление о разборке и обязательно заглядывайте в руководства по архитектуре вашего ЦП), вы будете стремиться использовать его когда это имеет смысл, а не когда это не так, и профилировщик может покрыть все остальное, если это необходимо.
Однозначные заявления о том, что «не используйте виртуальное» или «виртуальное использование вряд ли измеримо», вызывают у меня ворчливость. Реальность, как правило, более сложная, и вы либо окажетесь в ситуации, когда вам достаточно внимания, чтобы составить профиль или избежать ее, либо вы окажетесь в тех 95%, которые, возможно, не стоят того, чтобы о них заботиться, за исключением возможного образовательного контента.