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

е каркасы журналирования (например, log4j) позволяют передавать лямбда-выражения вместоStringс API регистрации. Аргумент заключается в том, что если строка особенно выразительна для конструирования, конструкцию строки можно лениво выполнить с помощью лямбда-выражения. Таким образом, строка создается, только если уровень журнала системы совпадает с уровнем журнала.

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

Предположим, что наш традиционный метод регистрации выглядит так:

void log(int level, String message) {
    if (level >= System.logLevel)
        System.out.println(message);
}
// ....
System.logLevel = Level.CRITICAL;
log(Level.FINE, "Very expensive string to construct ..." + etc);

Давайте предположим, чтоFINE меньше чемCRITICALТаким образом, хотя создается дорогая строка, это не так, поскольку сообщение не выводится.

API лямбда-логинга помогают в этой ситуации, так что строка оценивается (создается) только при необходимости:

void log(int level, Supplier<String> message) {
    if (level >= System.logLevel)
        System.out.println(message.apply());
}
// ....
System.logLevel = Level.CRITICAL;
log(Level.FINE, () -> "Very expensive string to construct ..." + etc);

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

System.logLevel = Level.CRITICAL;
if (Level.FINE >= System.logLevel)
    System.out.println("Very expensive string to construct..." + etc);

В этом случае нам не нужно оценивать строку до вызова API журналирования (потому что ее нет), и, по-видимому, мы получим производительность только за счет вставки.

Таким образом, мой вопрос в том, как лямбда-выражения помогают нам в этой ситуации, учитывая, что компилятор может, возможно, встроить вызовы API журналирования? Единственное, о чем я могу думать, это то, что в случае лямбды строка не оценивается, если уровень ведения журнала не совпадает.

 tsolakp06 дек. 2017 г., 21:40
Как мог компилятор знать, чтоlog(Level.FINE, () -> "Very expensive string to construct ..." + etc); и `if (Level.FINE> = System.logLevel) System.out.println (" Очень дорогая строка для создания ... "+ и т. д.); ' такие же? Для него первый - просто вызов метода, а второй - другой вызов метода, основанный на некотором условии.
 Raffi Khatchadourian07 дек. 2017 г., 18:42
@tsolakp Вопрос касается встраивания метода в не лямбда-примере.

Ответы на вопрос(3)

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

   public String process(){
        //do some important bussiness logic
        return "Done processing";
    }

1) Без встраиванияprocess() будет вызываться независимо от уровня ведения журнала:

log( Level.FINE, "Very expensive string to construct ..." + process() );

2) с наклономprocess() будет вызываться только при определенном уровне ведения журнала, и наша важная бизнес-логика не сможет работать:

if (Level.FINE >= System.logLevel)
    System.out.println("Very expensive string to construct..." +  process() );

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

 tsolakp07 дек. 2017 г., 23:51
@RaffiKhatchadourian. Если бы мы могли как-то гарантировать, что создание сообщения не будет иметь побочных эффектов, то мы, вероятно, могли бы рассмотреть тонкую настройку компилятора для обработки этого особого случая. Но даже после всего этого должна быть очень хорошо рекламируемая функция, чтобы разработчики больше не занимались ручной оптимизацией.
 Raffi Khatchadourian07 дек. 2017 г., 23:15
Вы бы ответили так же, если бы аргумент не имел побочных эффектов?

ростых примеров, которые вы предоставили (когда это простоString конкатенация).

Фактически, этот API может использоваться более изощренным способом:

 public void log(Level level, Supplier<String> msgSupplier) 

Допустим, у меня есть специальный поставщик, который выполняет довольно дорогое создание журнала сообщений:

    Supplier<String> supplier = () -> {
        // really complex stuff
    };

и тогда я использую его в нескольких местах:

LOGGER.log(Level.SEVERE, supplier);
...
LOGGER.log(Level.SEVERE, supplier);

Тогда что бы вы включили? Развернуть-вставить его в

System.logLevel = Level.CRITICAL;
if (Level.FINE >= System.logLevel)
    System.out.println(supplier.get());

не имеет никакого смысла.

Как сказано вjava.util.logging.Logger классJavaDoc:

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

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

 Raffi Khatchadourian06 дек. 2017 г., 21:59
Оптимизация не будет происходить с лямбда-примером. Вместо этого это будет с оригинальным примером. Вопрос связан с тем, нужны ли лямбды, как правило, с помощью метода встраивания.
Решение Вопроса

ок. Это вообще не верно.

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

Так что пока этовозможно для действительно умного JIT-компилятора я бы очень удивился, увидевфактически сделал это. Если вы обнаружите, что работаете с ним, и напишите тестыдоказывать что этот подход не дороже, чем использование лямбда-выражений, и продолжайте доказывать, что со временем это здорово - но, похоже, вы заинтересованы в том, чтобы предположить, что это так, чего я определенно не стал бы.

 Raffi Khatchadourian07 дек. 2017 г., 19:12
@JonSkeet Я заменил параметр аргументом, а вызов метода - телом метода. Вы смотрите на пример лямбда-выражения? Подстановка применяется к примеру без лямбда-выражения.
 Raffi Khatchadourian06 дек. 2017 г., 21:25
Извините, я не совсем уловил изменение заказа. Есть ли другой способ, которым я мог бы написать оптимизацию, которую можно было бы включить?
 Jon Skeet06 дек. 2017 г., 22:39
@RaffiKhatchadourian: Inlining не меняет, выполняются ли вещи, как они выполняются. То, что вы пытаетесь сделать, это удалить выполнение «построения большой строки» - это не то, что встроенный для вас подойдет.
 Andremoniy06 дек. 2017 г., 21:39
Идея не в оптимизации JIT, а в том, чтобы избежать вызова сложного или дорогостоящего сокращения сообщения. Встраивание не имеет никакого смысла в этом случае
 Jon Skeet07 дек. 2017 г., 19:15
@RaffiKhatchadourian: вы заменили параметрв точке его использования, Это не когда аргумент оценивается, хотя. Встраивание не может этого сделать, потому что оно влияет на то, выполняются ли операции. Это должна быть оптимизация, а не то, что может вызвать изменение поведения. Предположим, что оценка аргумента вызвала исключение или вызвала создание файла - вы не можетепо выбору сделай это.

Ваш ответ на вопрос