Оптимизация — Ограничения для встроенных функций-оболочек в Stack Overflow

Мой вопрос касается применения встроенных оптимизаций на оболочках функций в C ++, рассмотрим следующий код, объект WorkerStructure инициализируется оболочкой функций, которая инкапсулирует некоторую часть функциональности. Обертка функции затем используется при вызове метода WorkerStructure :: doSomeWork.

Будут ли функциональные возможности, инкапсулированные объектом workerFunction, встроены при применении к методу WorkerStructure :: doSomeWork? Очевидно, что если функциональность определена в каком-то другом модуле перевода, объект workerFunction только инкапсулирует указатель функции, Существуют ли другие обстоятельства, когда встраивание невозможно?

Когда лямбда-функция, определенная в другом модуле перевода, передается через оболочку функции, эффективно ли она эквивалентна передаче указателя на функцию?

struct WorkerStructure
{
WorkerStructure(std::function <bool(float)> &f):workerFunction(f) {}

void doSomeWork(float inputValue)
{
if(workerFunction(inputValue))
{
//do some conditional operation
}
}
std::function <bool(float)> workerFunction ;
};

1

Решение

Полиморфная природа std::function по своей сути делает это очень-очень трудно на самом деле встроить вызов. Так как std::function может рассказать любую вызываемую сущность; как бы вы написали встроенный код?

Это похоже на встраивание виртуальных функций, которые вызываются через базовый указатель без какой-либо другой доступной информации (иначе говоря, нет присвоения от производного до базового указателя до вызова, который компилятор может использовать для включения встраивания).

Большую часть времени, std::function реализуется с void* указатель и указатель функции на специализацию шаблонной функции, которая выполняет фактический вызов, приведение и прочее. Конечно, есть варианты, в которых для этого используются виртуальные функции, и с ними становится понятнее, почему это удивительно сложно. Даже оптимизация во время соединения не сможет ничего сделать, поскольку это не имеет значения, у вас уже есть вся информация, которую вы можете получить на сайте вызовов (что не так уж много).

Вот очень сырая версия std::function используя указатель на версию функции шаблона, имея дело только с аспектом сохранения и вызова (исключая управление памятью, копирование, перемещение, сброс, оптимизацию пространства и т. д.):

template<class Sig>
class function;

template<class R, class... Args>
class function<R(Args...)>{
typedef R (*call_type)(void*, Args...);
void* _obj;
call_type _caller;

public:
template<class F>
function(F f)
: _obj(new F(f))
, _caller([](void* p, Args... args){ return (*static_cast<F*>(p))(args...); })
{}

R operator()(Args... args) const{
return _caller(_obj, args...);
}
};

Живой пример. Я думаю, что было бы очень трудно проверить, что на самом деле внутри _obj а также _caller и точка, где function вызывается.

Просто для справки, вот версия с виртуальными функциями.

5

Другие решения

Забавно, я спросил о встраивании виртуальных функций в Clang / LLVM на список рассылки только сегодня. Динамическая природа std::function делает это по сути виртуальный вызов, потому что virtual вызовы не намного больше, чем указатель на функции.

Используя LLVM в качестве примера, давайте поиграем со следующей программой:

#include <cstdio>

typedef void (*Function)();

void donothing() {}
void print() { printf("Hello World!"); }

Function get(int i) {
if (i % 2 == 0) { return donothing; }
return print;
}

int main() {
Function f = get(0);
f();
}

Основная функция выделяется:

define i32 @main() uwtable readnone {
ret i32 0
}

Следовательно, компилятор имеет возможность понять, какая функция выбрана (с комбинацией встраивания и постоянного распространения), и действительно встроил вызов.

К сожалению, я продемонстрировал в своем электронном письме, что передача через виртуальные таблицы не работает (оптимизатор каким-то образом потерял информацию и не смог встроить вызов). Так что, хотя вполне возможно, что встраивание работает через std::functionвполне может зависеть не только от компилятора, но и от конкретной реализации std::function что вы случайно используете. Я боюсь, что вам нужно будет поэкспериментировать с вашим приложением.

1

По вопросам рекламы [email protected]