Принудительное встраивание обратного вызова (лямбда) в C ++ 17 в библиотеке

Я строю систему времени выполнения, которая позволяет программисту указывать обратный вызов, который вызывается в определенных точках. Я использую -std=c++17 7.0.1 / -std=c++17 . Обратный вызов регистрируется во время выполнения, сохраняя лямбду как std::function . Когда среда выполнения позже вызывает std::function обратного вызова std::function , она передает 6 аргументов (необходимость, учитывая общность среды выполнения). Обратите внимание, что std::function создается в приложении, но используется статически связанной библиотекой, которая компилируется отдельно. Тем не менее, я использую LTO (через -flto и LLD 7.0.1), поэтому я надеялся, что он все еще сможет выполнить эту оптимизацию. Я новичок в некоторых из этих вещей, так что, надеюсь, это возможно.

Когда я компилирую с -O3 и указываю __attribute__((flatten)) в объявлении вызывающей функции, лямбда не является встроенной. Когда я запускаю свою систему, используя события perf, я вижу, что функция не встроена:

return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
 mov    -0x90(%rbp),%rdi  
 lea    -0x48(%rbp),%rsi  
 mov    %rbx,%rdx         
 mov    %r15,%rbx         
 callq  *0x180(%r15)      
...

Этот вызов занимает нетривиальное количество времени, и кажется, что он должен быть встроенным; в общей сложности есть только несколько call-сайтов. Я определенно видел лямбды, встроенные ранее, но я не уверен, что мой подход использования функтора (через std::function ) как-то дисквалифицирует встраивание.

Возможно ли форсирование inline? Дайте мне знать, если здесь нужна дополнительная информация.

РЕДАКТИРОВАТЬ: Спасибо за всю очень полезную информацию. Теперь я понимаю, что способ, которым я настроил свою среду выполнения, не дает компилятору возможности встроить обратный вызов. Комментарии ясно дают понять, почему это так. Были некоторые намеки на альтернативные подходы, которые могут быть неприемлемыми. Учитывая, что 1) я контролирую как приложение, так и источник времени выполнения (и модели программирования / API); 2) Я компилирую и библиотеку, и приложение одновременно (и могу даже сделать их унифицированным процессом сборки), есть ли альтернативные подходы, которые я мог бы здесь использовать, которые потенциально могли бы позволить встроить? Может быть шаблоны и лямбды (не std::functions )? Я новичок в этой области, и у меня есть все уши, если у кого-то есть идеи о том, как эффективно дать компилятору то, что ему нужно встроить. В худшем случае, я могу даже создать собственную версию библиотеки (в качестве доказательства концепции) для каждого приложения, если это открывает какие-либо возможности ...

Всего 1 ответ


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


Есть идеи?

10000