#c #optimization #lambda #c 17 #currying
#c #оптимизация #лямбда #c 17 #каррирование
Вопрос:
В качестве упрощенного примера предположим, что я начинаю с чего-то вроде следующего кода:
void function1 (Object amp;myObject) {
for (int b = 0; b < 10000; b ) {
// ... a bunch of code that uses the below myFunction(int, int) in different ways
auto x = myObject.myFunction(12345, b);
// do something with x and etc
}
}
и что я хочу сделать, это взять то, что находится внутри цикла for, поместить его в отдельную встроенную функцию, которая вызывается вместо этого, и каким-то образом предоставить этой функции доступ к каррированной версии myObject.myFunction(a, b)
in, которая b
уже установлена.
Один из способов сделать это так:
__attribute__((always_inline))
inline void function2 (Object amp;myObject, int amp;b) {
#define curried(a) myObject.myFunction(a, b)
// ... a bunch of code that uses the below function in different ways
auto x = curried(12345);
// do something with x and etc
}
void function1 (Object amp;myObject) {
for (int b = 0; b < 10000; b ) {
function2(myObject, b);
}
}
Который, если вы можете заставить компилятор успешно function2
встроиться, выдает тот же ассемблерный код без накладных расходов. (Для чего может потребоваться как минимум -O1 или -O2, по крайней мере, с clang , даже если __attribute__((always_inline))
используется.)
В любом случае, использовать макросы в этой ситуации несколько неудобно, и приходится передавать значение b
from function1
to function2
. Было бы намного лучше сделать что-то вроде передачи лямбда-функции в качестве параметра function2
, но тогда я столкнулся с проблемами с захватами (вы можете передать только не захваченную лямбду, но нам нужно будет захватить MyObject), а также с некоторыми дополнительными накладными расходами. В качестве примера я хотел бы сделать это:
__attribute__((always_inline))
inline void function2 (some_type curried) {
// ... a bunch of code that uses the below function in different ways
auto x = curried(12345);
// do something with x and etc
}
void function1 (Object amp;myObject) {
for (int b = 0; b < 10000; b ) {
auto lambda = [amp;](int a) {return myObject.myFunction(a, n);};
function2(lambda);
}
}
Но это не удается, если у лямбда-выражения нет захватов, поскольку нет типа, который вписывается в «some_type».
Итак, мой вопрос:
Есть ли какой-нибудь правильный способ сделать это с нулевыми накладными расходами, по крайней мере, с некоторой минимальной оптимизацией?
Комментарии:
1. Почему бы не использовать lambda:
auto curried = [amp;](auto a) { return myObject.myFunction(a, b);}
?2. Вы компилируете с включенной оптимизацией?
3.
inline
это просто предложение . Компилятор должен решить, должен ли он на самом деле встроить вызов. ‘4. Ваша вставка относится к
function2
, это не влияет на вызовmyObject.myFunction(12345, b)
. На самом деле вы только что добавили еще один уровень, который компилятор, возможно, не сможет полностью оптимизировать — вместо того, чтобы просто напрямую вызывать его — и это, вероятно, причина дополнительных накладных расходов
Ответ №1:
Лямбда-выражение должно выполнять эту работу
auto curried = [amp;](autoamp; a) { return myObject.myFunction(a, b);}
или
auto curried = [amp;](const autoamp; a) { return myObject.myFunction(a, b);}
Используете ли вы один или другой, будет зависеть от того, должна ли ваша функция изменяться a
в качестве параметра ввода-вывода. Вы должны быть осторожны, если myObject
or b
выходит из области видимости раньше curried
, так как лямбда-выражение будет содержать висячую ссылку.
Также обратите внимание, что лямбды доступны только с C 11
Комментарии:
1. Я передаю лямбда-
function2
выражение или все еще передаюb
и просто заменяю #define ?2. @MikeBattaglia Вы вызываете лямбда-выражение, как если бы вы делали это для любой функции
auto x = curried(1234);
3. Я имею в виду, вы просто заменяете
#define
infunction2
на lambda или меняете подписьfunction2
, чтобы использовать lambda в качестве параметра?4. Я думаю, что моя проблема заключалась в том, что для выполнения того, что я хотел, вам нужно передать лямбда-выражение в качестве параметра функции в function2. Но вы не можете этого сделать, если лямбда-выражение имеет какие-либо захваты, потому что оно не преобразуется в указатель на функцию, потому что оно имеет странный временный тип данных. Но вы можете сделать это, если функция, которую вы передаете, объявлена как шаблон, так что это сделало это за меня.