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

Я строю систему времени выполнения, которая позволяет программисту указывать обратный вызов, который вызывается в определенных точках. Я использую Clang 7.0.1 / -std=c++17. Обратный вызов регистрируется во время выполнения, сохраняя лямбду как 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? Дайте мне знать, если вам нужна дополнительная информация.

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

1 Ответ

2 голосов
/ 14 мая 2019

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

...