Насколько быстры лямбда-функции в GCC - PullRequest
27 голосов
/ 15 июня 2011

Немного поигравшись с C ++ 0x лямбда-выражением в G ++, мне стало интересно, насколько хорошо будет производительность в общих / специфических ситуациях по сравнению с альтернативными способами без использования лямбда-функций.

Кто-нибудь знает более или менее всестороннее обсуждение производительности лямбда-выражений или ситуаций, в которых следует избегать их, несмотря на больший комфорт при разработке?

Ответы [ 5 ]

28 голосов
/ 15 июня 2011

Если вы рассматриваете старый способ определения структуры с помощью operator () как альтернативный способ, тогда не будет никакой разницы, потому что лямбды в значительной степени эквивалентны этому. Просто синтаксически удобнее. Давайте дождемся более полного ответа ...

16 голосов
/ 16 июня 2011

При первом знакомстве с лямбда-выражением у многих возникает смутное впечатление, что для создания этих функций происходит какое-то волшебство во время выполнения. В частности, если у вас есть функция, которая возвращает вновь созданную функцию в качестве результата, может показаться, что возвращаемая функция «создается» каждый раз, когда вызывается окружающая функция. Но это неправильно - лямбда-выражение (и это верно для любого языка) содержит некоторый код, который можно скомпилировать так же, как и любой другой код, и все это происходит статически, без каких-либо затрат , которые должны быть осталось на время выполнения.

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

Опять же, это все, что есть в любом языке с замыканиями, и у кода C ++ нет причин страдать от некоторых проблем с производительностью, в отличие от других языков. Одна странность в лямбда-выражениях C ++ заключается в необходимости указывать, какие переменные вы закрываете, тогда как в большинстве других языков вы просто закрываете все по умолчанию. Казалось бы, это дает C ++-коду некоторое преимущество в том, что он имеет больший контроль над тем, сколько вещей должно быть в пакетах с замыканием - но это то, что компилятору очень легко сделать автоматически, без явных аннотаций. Это приводит к одной из самых распространенных вещей, которые делают компиляторы функциональных языков - «лямбда-лифтинг» - когда функция эффективно поднимается до верхнего уровня, избегая необходимости создавать замыкания во время выполнения, если они не нужны. Например, если вы пишете (используя некоторый псевдокод, подобный JS):

function foo(x) {
  return function(y) { return y+3; }
}

тогда легко (как для компилятора, так и для человека) увидеть, что возвращаемая функция не зависит от x, и теперь компилятор может ее поднять, как если бы вы написали:

function generated_name1234(y) { return y+3; }
function foo(x) {
  return generated_name1234;
}

Подобные методы используются, когда закрываются только некоторые значения.

Но это расходится. Суть в том, что лямбда-выражения не являются чем-то, что влечет за собой снижение производительности - закрытые переменные или нет.

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

5 голосов
/ 15 июня 2011

я не вижу никакой причины, по которой замыкания должны быть менее эффективными, чем эквивалентные функции с тем же числом и размером передаваемых параметров

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

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

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

, если конкретный компилятор (например, g ++ или clang) ведет себя в конфликте с вышеприведенным, это предупреждающий знак плохогореализация.Clang имеет возможность легко расширять оптимизацию, передаваемую дизайном, поэтому любые такие недостатки должны быть легче устранены в долгосрочной перспективе, по сравнению, скажем, с g ++

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

2 голосов
/ 16 июня 2011

Точно так же, как упоминалось другими, нет никаких причин, по которым сгенерированные компилятором замыкания, являющиеся результатом лямбд, должны отличаться по производительности от написанных от руки.И чтобы проверить это, я просто изменил лямбда-выражения, используемые в парсере, написанными от руки классами.Все они содержат только несколько строк жесткого кода и выполняются миллионы раз, поэтому каждое изменение в производительности будет сразу заметно.Результат - точно такое же время выполнения.Поэтому нет никакой разницы в производительности.

1 голос
/ 16 декабря 2014

Мы начинаем избегать использования лямбда-выражений в некоторых случаях (игровая среда), потому что созданное замыкание (если оно имеет захваченные значения) имеет связанный новый / delete для хранения любых захваченных значений.Хотя во многих средах это не проблема, мы стремимся экономить микросекунды за раз, чтобы добиться максимальной производительности.Лямбды (те, что с вложенными переменными) занимали важное место в нашем профилировании и были в числе первых.

...