В javascript, доступ к «window.Math» медленнее или быстрее, чем доступ к объекту «Math» без «окна».? - PullRequest
5 голосов
/ 24 января 2010

Мне любопытно узнать, что лучше всего делать при обращении к «глобальному» пространству имен в javascript, которое является просто ярлыком для объекта window (или наоборот, в зависимости от того, как вы на него смотрите).

Я хочу знать, если:

var answer = Math.floor(value);

лучше или хуже, чем:

var answer = window.Math.floor(value);

Лучше или хуже, даже немного, для производительности, использования ресурсов или совместимости?

Есть ли у кого-то более низкая стоимость? (Что-то вроде дополнительного указателя или что-то)

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

Ответы [ 6 ]

14 голосов
/ 24 января 2010

Прежде всего, никогда не сравнивайте подобные вещи по соображениям производительности. Math.round явно проще для глаз, чем window.Math.round, и вы не заметите заметного увеличения производительности при использовании одного или другого. Так что не запутывайте свой код для очень небольшого увеличения производительности.

Однако, если вам просто любопытно, какой из них быстрее ... Я не уверен, как глобальная область видится "под капотом", но я думаю, что доступ к window точно такой же как доступ к Math (window и Math живут на одном уровне, о чем свидетельствует window.window.window.Math.round работает). Таким образом, доступ к window.Math будет медленнее.

Кроме того, при поиске переменных вы увидите увеличение производительности, выполнив var round = Math.round; и вызвав round(1.23), так как все имена сначала ищутся в текущей локальной области, а затем в области выше текущей, и так далее, вплоть до глобального масштаба. Каждый уровень объема добавляет очень небольшие накладные расходы.

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

Вот полное профилирование с использованием Firebug:

<!DOCTYPE html>
<html>
    <head>
        <title>Benchmark scope lookup</title>
    </head>
    <body>
        <script>
        function bench_window_Math_round() {
            for (var i = 0; i < 100000; i++) {
                window.Math.round(1.23);
            }
        }

        function bench_Math_round() {
            for (var i = 0; i < 100000; i++) {
                Math.round(1.23);
            }
        }

        function bench_round() {
            for (var i = 0, round = Math.round; i < 100000; i++) {
                round(1.23);
            }
        }

        console.log('Profiling will begin in 3 seconds...');
        setTimeout(function () {
            console.profile();
            for (var i = 0; i < 10; i++) {
                bench_window_Math_round();
                bench_Math_round();
                bench_round();
            }
            console.profileEnd();
        }, 3000);
        </script>
    </body>
</html>

Мои результаты:
Time показывает общее количество для 100 000 * 10 вызовов, Avg / Min / Max показывает время для 100 000 вызовов.

Calls  Percent  Own Time   Time       Avg        Min        Max
bench_window_Math_round
10     86.36%   1114.73ms  1114.73ms  111.473ms  110.827ms  114.018ms   
bench_Math_round
10      8.21%    106.04ms   106.04ms   10.604ms   10.252ms   13.446ms   
bench_round
10      5.43%     70.08ms    70.08ms    7.008ms    6.884ms    7.092ms

Как видите, window.Math действительно плохая идея. Я предполагаю, что доступ к глобальному объекту window добавляет дополнительные издержки. Однако разница между доступом к объекту Math из глобальной области и простым обращением к локальной переменной со ссылкой на функцию Math.round не очень велика ... Имейте в виду, что это 100 000 вызовов, и разница составляет всего 3,6 мс. Даже при миллионе вызовов вы увидите разницу только в 36 мс.

О чем следует подумать с помощью приведенного выше кода профилирования:

  • На самом деле функции ищутся из другой области видимости, что добавляет издержки (хотя это было едва заметно, я попытался импортировать функции в анонимную функцию).
  • Фактическая функция Math.round добавляет издержки (я предполагаю, что около 6 мс на 100 000 вызовов).
2 голосов
/ 24 января 2010

Это может быть интересный вопрос, если вы хотите узнать, как работает процесс Scope Chain и Identifier Resolution .

Цепочка контекста представляет собой список объектов, которые ищутся при оценке идентификатора, эти объекты не доступны для кода, доступны только его свойства (идентификаторы).

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

Последующие объекты в цепочке создаются, когда вы входите в контекст выполнения функции и оператором with и предложением catch, оба также вводят объекты в цепочку.

Например:

// global code
var var1 = 1, var2 = 2;
(function () { // one
  var var3 = 3;
  (function () { // two
    var var4 = 4;

    with ({var5: 5}) { // three
      alert(var1);
    }
  })();
})();

В приведенном выше коде цепочка областей действия будет содержать разные объекты на разных уровнях, например, на самом низком уровне в выражении with, если вы используете переменные var1 или var2, цепочка областей действия будет содержать 4 объекта, которые необходимо будет проверить для получения этого идентификатора: объект, введенный оператором with, две функции и, наконец, глобальный объект.

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

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

2 голосов
/ 24 января 2010

Производительность JS сильно отличается от браузера к браузеру.

Мой совет: сравните это. Просто поместите его в цикл for, дайте ему поработать несколько миллионов раз, и рассчитайте время ... посмотрите, что вы получите Обязательно поделитесь своими результатами!

1 голос
/ 24 января 2010

Насколько я понимаю логику JavaScript, все, что вы называете something, ищется в области видимости глобальной переменной. В реализациях браузера объект window является глобальным объектом. Следовательно, когда вы запрашиваете window.Math, вам на самом деле нужно отменить ссылку на то, что означает window, затем получить его свойства и найти там Math. Если вы просто спросите Math, первое место, где его ищут, это глобальный объект.

Так что да - вызов Math.something будет быстрее, чем window.Math.something.

D. Крокефорд рассказывает об этом в своей лекции http://video.yahoo.com/watch/111593/1710507,, насколько я помню, это в третьей части видео.

1 голос
/ 24 января 2010

(как вы сказали) Math.floor, вероятно, будет просто ярлыком для window.Math (поскольку window - глобальный объект Javascript) в большинстве реализаций Javascript, таких как V8.

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

Для удобства чтения я бы предпочел использовать Math.floor, разница в скорости будет настолько незначительной, что о ней не стоит беспокоиться никогда. Если вы делаете 100 000 этажей, возможно, пришло время отключить эту логику от клиента.

Возможно, вам захочется иметь нос вокруг источника v8. Там есть несколько интересных комментариев о том, как сбрасывать наносекунды с таких функций, как this int.Parse() one.

// Some people use parseInt instead of Math.floor.  This
// optimization makes parseInt on a Smi 12 times faster (60ns
// vs 800ns).  The following optimization makes parseInt on a
// non-Smi number 9 times faster (230ns vs 2070ns).  Together
// they make parseInt on a string 1.4% slower (274ns vs 270ns).
0 голосов
/ 24 января 2010

Если Math.round() вызывается в локальной области / области действия, интерпретатор должен сначала проверить локальную переменную, а затем в глобальном / оконном пространстве. Так что в локальном масштабе я думаю, что window.Math.round() будет немного быстрее. Это не ассемблер или C или C ++, поэтому я не буду беспокоиться о том, что быстрее по производительности по причинам, но если из любопытства, конечно, сравните его.

...