Javascript (V8) оптимизирует неиспользуемые возвращаемые значения - PullRequest
2 голосов
/ 26 мая 2020

Допустим, у нас есть такая функция:

function foo() {
  // do some work...
  return () => {}; // foo returns a function
}

Код клиента может использовать foo в двух сценариях ios:

  1. Использовать результат функция
const result = foo();
// some code that uses result...
Игнорировать результат функции
foo();

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

function foo(needTheResultValue = false) {
  // do some work...
  if (needTheResultValue) return () => {};
  // nothing is returned if the caller didn't ask for it
}

1 Ответ

2 голосов
/ 26 мая 2020

Разработчик V8 здесь. Короткий ответ: «это зависит от обстоятельств, не беспокойтесь об этом».

В общем, V8 (и другие движки, насколько мне известно) оптимизируются для каждой функции. Итак, в вашем примере, если и когда foo оптимизирован, он не знает, будет ли его возвращаемое значение использоваться или игнорироваться, поэтому он не может оптимизировать его.

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

function foo() {
  // Do some FOO work...
  return {};
}
function bar() {
  foo();
  // Do some BAR work...
}

Когда foo оптимизирован, он будет (продолжать) возвращать только что выделенный пустой объект, независимо от того, откуда он вызван. Когда bar оптимизирован, компилятор может решить встроить foo, и после этого шага он увидит (собственное внутреннее представление гипотетической функции, например):

function bar() {
  // Do some FOO work...
  {};
  // Do some BAR work...
}

И затем он может легко сбросить выделение неиспользуемых объектов там.

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

В частности, возврат некоторого значения, которое вы все равно вычислили, имеет ноль стоимость по сравнению с возвратом ничего, потому что каждая функция всегда что-то возвращает - если у нее нет оператора return, тогда двигатель незаметно вставит return undefined; за вас. Это означает, что function f1() {} и function f2() { return undefined; } будут компилироваться в один и тот же код. И если вы решили написать что-то вроде:

function overly_clever(need_result) {
  let result = do_some_work();
  if (result < 0) handle_error();

  if (need_result) {
    return result;
  } else {
    // Return nothing.
  }
}

, тогда эта функция будет немного (неизмеримо) медленнее , чем если бы вы заменили все после пустой строки простым return result, потому что «пустая» ветвь else (с автоматически вставляемой return undefined;) не быстрее, чем return result, поэтому оценка условия need_result - пустая трата времени.

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

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

...