Ваш вопрос неясен. Поэтому все, что я могу предложить, - это расплывчатые решения.
Первый способ, который стоит рассмотреть, - это руководство vtable. Это устраняет косвенную память по сравнению с автоматическим vtable. В основном вы храните указатель на правильную реализацию метода, и вы непосредственно следуете этому указателю функции.
Второй подход заключается в подъеме ветви из петли. Ваш код должен иметь много времени. Если решение о том, какая ветка должна быть сделана до повторения, вы можете переместить ветку там.
Представьте, что вы были
void foo() {
bool which_branch = decide();
VectorWrapper wrap(decide);
wrap.populate();
for (auto x : some_loop_domain) {
x.set(wrap.get());
}
}
уведомления о том, что решение принято вне цикла, но цена может быть оплачена в течение цикла.
Если мы сможем переместить ветку за пределы цикла, стоимость будет выплачена один раз, а не один раз за элемент в some_loop_domain
.
Один из способов сделать эту работу - предоставить компилятору более легкую оптимизацию константы. Поэтому мы избегаем сохранения состояния, для которого вектор используется в оболочке, и вместо этого передайте его. Значение, которое мы передаем, упорядочивается как «явно постоянное» в контексте, где оно используется, или даже постоянной времени компиляции.
Чтобы использовать постоянную времени компиляции, код, использующий его, должен быть храповым. Тем не менее, эта храповая обработка должна быть только в точке «выше петли», так как ветка обычно достаточно дешевая, чтобы не заботиться.
Чтобы полагаться на разумный оптимизатор, вы также заполняете там очевидную константу и передаете ее как значение. Это может быть менее надежным в моем опыте.
void foo() {
bool which_branch = decide();
auto body = [&](auto which_branch) {
VectorWrapper wrap(which_branch);
wrap.populate();
for (auto x : some_loop_domain) {
x.set(wrap.get());
}
};
if (which_branch) {
body(std::true_type{});
} else {
body(std::false_type{});
}
}
теперь в пределах body
, which_branch
является константой времени компиляции. Все само по себе это может привести к ветви будучи поднятым, но это может потребовать:
auto body = [&](auto which_branch) {
static_assert(decltype(which_branch){} || !decltype(which_branch){});
VectorWrapper<decltype(which_branch)> wrap;
wrap.populate();
for (auto x : some_loop_domain) {
x.set(wrap.get());
}
};
где мы делаем VectorWrapper
template
на классе, когда экземпляр и читать в bool
контексте возвращает true
или false
как constexpr
.
Код в пределах VectorWrapper
остается аналогичным, за исключением того, что он считывает константу времени компиляции вместо метода, чтобы решить, какую ветвь взять.
Этот метод может быть очень навязчивым или вообще не работать. Он может принимать, казалось бы, заполненный веткой код и поднимать ветви.
Вы можете сделать декартовое произведение многих ветвей низкого уровня аналогичным образом, где вы можете создавать 2^10 различных низкоуровневых процедур, выбирать между ними перед входом в цикл, а затем запускать одну из 1024 циклов.
Это звучит как преждевременная оптимизация. Вы измерили? –
Да, конечно. Эта оптимизация связана с тем, что производительность этой функции обнаруживается на критическом пути, где каждый скачок, который мы делаем, учитывается, следовательно, код, который может быть встроен, повышает производительность. Мы видели фактические пробелы в производительности при попытке прототипа, который использует, например, только Vector1, а не вариант, который использует полиморфную версию Vector1, Vector2 –
Ну, тогда вам нужно передать вектор в код, специализированный для этого типа, т. Е. Код шаблона. –