Подтвердить что ты не робот

Объекты Javascript и производительность карт (Chrome, V8, Node JS)

Я пытаюсь понять, использовать ли JS Object или Map, если мне нужны случайные поиски с помощью строкового ключа в больших наборах данных ( > 1000 объектов).

Я написал простой тест http://jsperf.com/javascript-objects-vs-map-performance, и результаты показывают, что в Chrome (V8) объекты превосходят карты примерно в 2 раза. Тем не менее, я проверил другие браузеры, и результаты были противоположными. Почему они различаются в разных браузерах/двигателях?

Я также написал аналогичный тест в Node.JS, и я не вижу подобных результатов (тестовый пример 6 занял гораздо больше, чем тестовый пример 4):

Испытания

var now = require("performance-now");

var mapKeyValue = new Map();
var mapStringKeyValue = new Map();
var objectKeyValue = {};
var n = 10000;
var testSamples = 100;

var firstRow = 0;
var firstRowString = firstRow + "";

var middleRow = Math.floor(n / 2);
var middleRowString = middleRow + "";

var lastRow = n - 1;
var lastRowString = lastRow + "";

var nonExist = n * 2;
var nonExistString = nonExist + "";

function makeid() {
  var text = "";
  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

  for (var i = 0; i < 20; i++)
    text += possible.charAt(Math.floor(Math.random() * possible.length));

  return text;
}

for (var i = 0; i < n; i++) {
  var value = makeid();
  mapKeyValue.set(i, value);
  mapStringKeyValue.set(i + "", value);
  objectKeyValue[i + ""] = value;
}

var t0, t1;

var averages = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

for (var j = 0; j < testSamples; j++) {
  var k = 0;
  t0 = now();
  mapKeyValue.get(firstRow);
  t1 = now();
  averages[k++] += (t1 - t0);

  t0 = now();
  mapStringKeyValue.get(firstRowString);
  t1 = now();
  averages[k++] += (t1 - t0);

  t0 = now();
  objectKeyValue[firstRowString];
  t1 = now();
  averages[k++] += (t1 - t0);


  t0 = now();
  mapKeyValue.get(middleRow);
  t1 = now();
  averages[k++] += (t1 - t0);

  t0 = now();
  mapStringKeyValue.get(middleRowString);
  t1 = now();
  averages[k++] += (t1 - t0);

  t0 = now();
  objectKeyValue[middleRowString];
  t1 = now();
  averages[k++] += (t1 - t0);


  t0 = now();
  mapKeyValue.get(lastRow);
  t1 = now();
  averages[k++] += (t1 - t0);


  t0 = now();
  mapStringKeyValue.get(lastRowString);
  t1 = now();
  averages[k++] += (t1 - t0);

  t0 = now();
  objectKeyValue[lastRowString];
  t1 = now();
  averages[k++] += (t1 - t0);


  t0 = now();
  mapKeyValue.get(nonExist);
  t1 = now();
  averages[k++] += (t1 - t0);

  t0 = now();
  mapStringKeyValue.get(nonExistString);
  t1 = now();
  averages[k++] += (t1 - t0);

  t0 = now();
  objectKeyValue[nonExistString];
  t1 = now();
  averages[k++] += (t1 - t0);
}

console.log("Test samples number " + testSamples);

for (var i = 0; i < averages.length; i++) {
  averages[i] /= testSamples;
  console.log("Test case " + (i + 1) + " took in average " + (averages[i] * 1000000) + " ns");
}

Результаты

Test samples number 100
Test case 1 took in average 2050.269999999692 ns
Test case 2 took in average 751.2899999997202 ns
Test case 3 took in average 567.3000000004081 ns
Test case 4 took in average 727.2699999999688 ns
Test case 5 took in average 4760.029999999489 ns
Test case 6 took in average 1939.3400000004135 ns
Test case 7 took in average 673.549999999885 ns
Test case 8 took in average 689.3600000002564 ns
Test case 9 took in average 541.3700000001143 ns
Test case 10 took in average 1146.0599999999843 ns
Test case 11 took in average 3096.7699999998285 ns
Test case 12 took in average 644.7400000000058 ns

Сообщите мне, есть ли у вас какие-либо идеи относительно того, как улучшить бенчмарк и сделать его более точным. Спасибо.

4b9b3361

Ответ 1

Ваш самый длинный контрольный пример занял менее 0,005 миллисекунд. Компьютер ест его на завтрак. Вы находитесь на территории, где микроскопические колебания во время холодной загрузки резко изменят результат в относительном процентном соотношении. Это делает эти цифры менее значимыми.

Я бы предложил оптимизировать для удобства чтения и других проблем. Сделай это, сделай это правильно, затем сделай это быстро. Помните, что Карта новая и будет развиваться быстрее по мере развития двигателей.

Вот некоторые ресурсы, связанные с микро-эталонами, подводными камнями и путями их улучшения.

Ответ 2

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

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

Я написал новый контрольный пример, который гарантирует, что браузер не имеет возможности удалить код, а результаты показывают, что карты почти в два раза быстрее, чем связанные объекты: https://jsperf.com/map-vs-object- против заморозки

performance test results

Обратите внимание, что этот тест не включает стоимость для фактической инициализации объекта Map. В действительности, поэтому, скорее всего, быстрее использовать локальные объекты для небольших фрагментов кода, тогда как реальные Карты работают быстрее только в случае, когда вы храните большие объемы данных в глобальном контексте.

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