diff --git a/packages/readabilityjs/test/test-pages/habr.com/expected-metadata.json b/packages/readabilityjs/test/test-pages/habr.com/expected-metadata.json new file mode 100644 index 000000000..b3d693b9c --- /dev/null +++ b/packages/readabilityjs/test/test-pages/habr.com/expected-metadata.json @@ -0,0 +1,12 @@ +{ + "title": "Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень", + "byline": "programmerguru", + "dir": null, + "excerpt": "Картинка, конечно, стронгли анрилейтед Разные трюки я тестировал на Google Chrome 107.0.5304.107 и Mozilla Firefox 107.0 на Windows 10. Чтобы результаты всегда...", + "siteName": "Habr", + "siteIcon": "https://assets.habr.com/habr-web/img/favicons/favicon-16.png", + "previewImage": "https://habr.com/share/publication/712386/ff74768c013a8fb6236b5cbace64588a/", + "publishedDate": "2023-01-24T09:00:03.000Z", + "language": "Russian", + "readerable": true +} diff --git a/packages/readabilityjs/test/test-pages/habr.com/expected.html b/packages/readabilityjs/test/test-pages/habr.com/expected.html new file mode 100644 index 000000000..d65667971 --- /dev/null +++ b/packages/readabilityjs/test/test-pages/habr.com/expected.html @@ -0,0 +1,277 @@ +
+
+ +
+ +

+

+
+

Картинка, конечно, стронгли анрилейтед

+

Разные трюки я тестировал на Google Chrome 107.0.5304.107 и Mozilla Firefox 107.0 на Windows 10.

+

Чтобы результаты всегда были железно воспроизводимыми, я отключил все С-State’ы, ядра зафиксировал на 5 ГГц.

+

У меня 9900К, это Coffee Lake c AVX256, какие оптимизации применит Jit для вашего процессора — я не знаю, результат на вашем компьютере может отличаться от моего, в т.ч. из-за микроархитектуры процессора.

+

Скорость парсинга кода тоже входит в бенчмарк, поэтому браузер с быстрым парсером будет впереди.
+ +

+

+ Есть ли у переменной оверхед? +

+

Есть ли смысл использовать только dot notation? Какова цена выноса лишней переменной?

+
var array = new Array(65535).fill()
+
+// 3
+var a = array.map((x) => x)
+var b = array.map((x) => x)
+var c = array.map((x) => x)
+
+// 2
+var a = array.map((x) => x)
+var b = array.map((x) => x).map((x) => x)
+
+// 1
+var a = array.map((x) => x).map((x) => x).map((x) => x)
+

Чтобы узнать, гоняет ли браузер память туда-сюда, делаем мы .map на массив длиной 65535 с нулями внутри. Линк.

+

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

+

Хром не заметил разницы, а вот лиса заметила. Применительно к лисе, у лишней переменной есть измеряемый оверхед.

+

+ Есть ли разница между var, let, const или их отсутствием? +


+

Проверим. Используя разные биндинги — создадим POJO с переменной е. Потом добавим ему функцию о и запустим эту функцию. Бенчмарк простой, но движущихся частей много. Линк.

+
var g = { e: [] }
+g.o = function(x) { g.e.push(...[1,2,3]) }
+g.o()
+

Код выглядит так, отличаются только биндинги.

+

Результат неожиданный, но железно воспроизводимый. var, быстрее.

+

+ Bounce pattern, Switch case, длинная тернарка +

+

Если обе конструкции логически одинаковые, они должны строить одно и то же синтаксическое дерево, верно? Давайте проверим.

+
// switch case
+function thing(e) {
+    switch (e) {
+      case 0:
+        return "0";
+
+      case 1:
+        return "1";
+
+      case 2:
+        return "2";
+
+      case 3:
+        return "3";
+        
+      default:
+        return "";
+    }
+}
+
+// bounce pattern
+function bounce(x)
+{
+   if (x === 0) return "0";
+   if (x === 1) return "1";
+   if (x === 2) return "2";
+   if (x === 3) return "3";
+   
+   return ""
+}
+
+// ternary
+function bounce(x) {
+  return 0 === x ? "0" : 1 === x ? "1" : 2 === x ? "2" : 3 === x ? "3" : "";
+}
+

Вот так выглядит код. Для всех вариантов он одинаков, отличаются только вызовы.

+

+ ▍ 1. Вызов в цикле +


+
for (let t = 0; 1e5 > t; t++) bounce(0), bounce(2), bounce(6);
+

Вызов выглядит так. Линк.


+

+ ▍ 2. В цикле с другим типом +


+
for (let t = 0; 1e5 > t; t++) bounce("0"), bounce("2"), bounce("");
+

Тут мы покидываем строку вместо числа. В свитче и if блоках используется строгое равенство, поэтому свитч выходит только через default, а if’ы выходят только последний return. Линк.


+

+ ▍ 3. Без цикла +


+
bounce(0), bounce(2), bounce(6)
+

Просто три вызова подряд, никаких циклов. Линк.

+

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

+

Также лиса, похоже, не строит одно и то же AST, как это делает хром. Рекомендую заменить ваши длинные if’ы и bounce паттерны на свитчи, чтобы избежать лисиных тормозов.

+

+ Инициализация массива +

+

Для примера возьму из паттернов функционального программирования, когда ты инициализируешь массив, прокидывая лямбду в инициализатор. Просто ради примера, в качестве этой лямбды будет fizzbuzz.

+
var times = 65535;
+
+function initializer(val, z) {
+    const i = z % 5 | 0;
+    return 0 == (z % 3 | 0) ? 0 === i ? "fizzbuzz" : "fizz" : 0 === i ? "buzz" : z;
+}
+
+// for i
+var b = new Array(times);
+for (var i = 0; i < times; i++) {
+    b[i] = initializer(b[i], i)
+}
+b
+
+// for push
+var c = [];
+for (var i = 0; i < times; i++) {
+    c.push(initializer(c[i], i))
+}
+c
+
+// Fill Map
+new Array(times).fill().map(initializer)
+
+

Это не самый красивый fizzbuzz, но это мой fizzbuzz. Линк на бенч.

+

Вариант с fill map создаёт два массива, сначала при вызове конструктора, потом при вызове map. Но такой вариант безальтернативно быстрее на хроме.

+

+ Конкатенация массивов +


+
+
// reduce
+arr.reduce((acc, val) => acc.concat(val), [])
+
+// flatMap
+arr.flatMap(x => x)
+
+// flat
+arr.flat()
+
+// reduce push
+arr.reduce((acc, val) => {
+    if (val) val.forEach(a => acc.push(a));
+    return acc;
+}, [])
+
+// forEach push
+let acc = [];
+
+arr.forEach(val => {
+    val && val.forEach(v => acc.push(v));
+}), acc;
+
+//concat spread
+[].concat(...arr)
+
+

Конкатенация массивов на 1 уровень, поведение идентичное flat(1). Линк.

+

Иногда я не понимаю, почему разработчики движков оставили такой потенциал для оптимизации.

+

+ Уничтожение хрома +

+

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

+

+ ▍ Итерация по массиву +

+

Сравнивать будем Array.prototype.forEach vs for...of vs for. На код смотрите по линку.

+

Ради производительности, циклы for, лучше переделать в forEach, чтобы хром не отставал.

+

+ ▍ Содержит ли строка значение +


+
// text.includes()
+url.includes('matchthis')
+
+// text.test()
+/matchthis/.test(url)
+
+// text.match()
+url.match(/matchthis/).length >= 0
+
+// text.indexOf()
+url.indexOf('matchthis') >= 0
+
+// text.search()
+url.search('matchthis') >= 0
+
+

Трюк с IndexOf быстрее и на лисе, и на хроме. Используйте трюк с IndexOf. Линк на бенчмарк.

+

+ Преобразование строки в число +

+

Тестируем неявное преобразование, парсинг и вызов конструктора.

+
// implicit
+var imp = + strNum
+
+// parseFloat
+var toStr = parseFloat(strNum)
+
+//Number
+var num = Number(strNum)
+

+

+ ▍ Int +

+

Линк на бенч.

+

+ ▍ Float +

+

Я перепроверял, это не ошибка. Неявный каст стринги в инт практически бесплатный у лисы. Линк на бенч.

+

+ Выводы +


+
    +
  1. Лисичка похорошела.
  2. +
  3. JS сделан за неделю на коленке.
  4. +
  5. Я не пишу на JS.
  6. +
  7. Вы тоже прекращайте.
  8. +

+
+ Играй в нашу новую игру прямо в Telegram! +
+

+

+
+ +
+
+

Теги:

+ +
+
+

Хабы:

+ +
+
+
+
\ No newline at end of file diff --git a/packages/readabilityjs/test/test-pages/habr.com/source.html b/packages/readabilityjs/test/test-pages/habr.com/source.html new file mode 100644 index 000000000..689fbeb90 --- /dev/null +++ b/packages/readabilityjs/test/test-pages/habr.com/source.html @@ -0,0 +1,1776 @@ + + + + + + + + Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень / Хабр + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+ + + Обновить + + +
+
+
+
+
+
+ +
+ +
+
+
+
+ 2231.74 +
+
+
+ Рейтинг +
+
+
+
+ RUVDS.com +
+ VDS/VPS-хостинг. Скидка 10% по коду HABR10 +
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+

+ Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень +

+
+ +
+ + + Время прочтения + + 4 мин +
+ + Просмотры + + 22K +
+
+
+ Блог компании RUVDS.com Высокая производительность *JavaScript *Клиентская оптимизация *Браузеры +
+
+
+
+
+
+
+
+
+ +
+ +
Картинка, конечно, стронгли анрилейтед
+
+ Разные трюки я тестировал на Google Chrome 107.0.5304.107 и Mozilla Firefox 107.0 на Windows 10.
+
+ Чтобы результаты всегда были железно воспроизводимыми, я отключил все С-State’ы, ядра зафиксировал на 5 ГГц.
+
+ У меня 9900К, это Coffee Lake c AVX256, какие оптимизации применит Jit для вашего процессора — я не знаю, результат на вашем компьютере может отличаться от моего, в т.ч. из-за микроархитектуры процессора.
+
+ Скорость парсинга кода тоже входит в бенчмарк, поэтому браузер с быстрым парсером будет впереди.
+
+

+ Есть ли у переменной оверхед? +


+ Есть ли смысл использовать только dot notation? Какова цена выноса лишней переменной?
+
+
var array = new Array(65535).fill()
+
+// 3
+var a = array.map((x) => x)
+var b = array.map((x) => x)
+var c = array.map((x) => x)
+
+// 2
+var a = array.map((x) => x)
+var b = array.map((x) => x).map((x) => x)
+
+// 1
+var a = array.map((x) => x).map((x) => x).map((x) => x)

+ Чтобы узнать, гоняет ли браузер память туда-сюда, делаем мы .map на массив длиной 65535 с нулями внутри. Линк.
+
+
Здесь и далее в качестве единицы измерения на графиках будет указано кол-во выполнений кода в секунду, включая парсинг и компиляцию. Больше — лучше
+
+ Хром не заметил разницы, а вот лиса заметила. Применительно к лисе, у лишней переменной есть измеряемый оверхед.
+
+

+ Есть ли разница между var, let, const или их отсутствием? +


+

+ Проверим. Используя разные биндинги — создадим POJO с переменной е. Потом добавим ему функцию о и запустим эту функцию. Бенчмарк простой, но движущихся частей много. Линк.
+
+
var g = { e: [] }
+g.o = function(x) { g.e.push(...[1,2,3]) }
+g.o()

+ Код выглядит так, отличаются только биндинги.
+
+

+ Результат неожиданный, но железно воспроизводимый. var, быстрее.
+
+

+ Bounce pattern, Switch case, длинная тернарка +


+ Если обе конструкции логически одинаковые, они должны строить одно и то же синтаксическое дерево, верно? Давайте проверим.
+
+
// switch case
+function thing(e) {
+    switch (e) {
+      case 0:
+        return "0";
+
+      case 1:
+        return "1";
+
+      case 2:
+        return "2";
+
+      case 3:
+        return "3";
+        
+      default:
+        return "";
+    }
+}
+
+// bounce pattern
+function bounce(x)
+{
+   if (x === 0) return "0";
+   if (x === 1) return "1";
+   if (x === 2) return "2";
+   if (x === 3) return "3";
+   
+   return ""
+}
+
+// ternary
+function bounce(x) {
+  return 0 === x ? "0" : 1 === x ? "1" : 2 === x ? "2" : 3 === x ? "3" : "";
+}

+ Вот так выглядит код. Для всех вариантов он одинаков, отличаются только вызовы.
+
+

+ ▍ 1. Вызов в цикле +


+
for (let t = 0; 1e5 > t; t++) bounce(0), bounce(2), bounce(6);

+ Вызов выглядит так. Линк.
+
+

+

+ ▍ 2. В цикле с другим типом +


+
for (let t = 0; 1e5 > t; t++) bounce("0"), bounce("2"), bounce("");

+ Тут мы покидываем строку вместо числа. В свитче и if блоках используется строгое равенство, поэтому свитч выходит только через default, а if’ы выходят только последний return. Линк.
+
+

+

+ ▍ 3. Без цикла +


+
bounce(0), bounce(2), bounce(6)

+ Просто три вызова подряд, никаких циклов. Линк.
+
+

+ Похоже, что после первоначальной компиляции лиса не пытается дальше оптимизировать цикл, как это делает хром.
+
+ Также лиса, похоже, не строит одно и то же AST, как это делает хром. Рекомендую заменить ваши длинные if’ы и bounce паттерны на свитчи, чтобы избежать лисиных тормозов.
+
+

+ Инициализация массива +


+ Для примера возьму из паттернов функционального программирования, когда ты инициализируешь массив, прокидывая лямбду в инициализатор. Просто ради примера, в качестве этой лямбды будет fizzbuzz.
+
+
var times = 65535;
+
+function initializer(val, z) {
+    const i = z % 5 | 0;
+    return 0 == (z % 3 | 0) ? 0 === i ? "fizzbuzz" : "fizz" : 0 === i ? "buzz" : z;
+}
+
+// for i
+var b = new Array(times);
+for (var i = 0; i < times; i++) {
+    b[i] = initializer(b[i], i)
+}
+b
+
+// for push
+var c = [];
+for (var i = 0; i < times; i++) {
+    c.push(initializer(c[i], i))
+}
+c
+
+// Fill Map
+new Array(times).fill().map(initializer)
+

+ Это не самый красивый fizzbuzz, но это мой fizzbuzz. Линк на бенч.
+
+

+ Вариант с fill map создаёт два массива, сначала при вызове конструктора, потом при вызове map. Но такой вариант безальтернативно быстрее на хроме.
+
+

+ Конкатенация массивов +


+

+
// reduce
+arr.reduce((acc, val) => acc.concat(val), [])
+
+// flatMap
+arr.flatMap(x => x)
+
+// flat
+arr.flat()
+
+// reduce push
+arr.reduce((acc, val) => {
+    if (val) val.forEach(a => acc.push(a));
+    return acc;
+}, [])
+
+// forEach push
+let acc = [];
+
+arr.forEach(val => {
+    val && val.forEach(v => acc.push(v));
+}), acc;
+
+//concat spread
+[].concat(...arr)
+

+ Конкатенация массивов на 1 уровень, поведение идентичное flat(1). Линк.
+
+

+ Иногда я не понимаю, почему разработчики движков оставили такой потенциал для оптимизации.
+
+

+ Уничтожение хрома +


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

+ ▍ Итерация по массиву +


+ Сравнивать будем Array.prototype.forEach vs for...of vs for. На код смотрите по линку.
+
+

+ Ради производительности, циклы for, лучше переделать в forEach, чтобы хром не отставал.
+
+

+ ▍ Содержит ли строка значение +


+
// text.includes()
+url.includes('matchthis')
+
+// text.test()
+/matchthis/.test(url)
+
+// text.match()
+url.match(/matchthis/).length >= 0
+
+// text.indexOf()
+url.indexOf('matchthis') >= 0
+
+// text.search()
+url.search('matchthis') >= 0
+

+
+

+ Трюк с IndexOf быстрее и на лисе, и на хроме. Используйте трюк с IndexOf. Линк на бенчмарк.
+
+

+ Преобразование строки в число +


+ Тестируем неявное преобразование, парсинг и вызов конструктора.
+
+
// implicit
+var imp = + strNum
+
+// parseFloat
+var toStr = parseFloat(strNum)
+
+//Number
+var num = Number(strNum)
+

+

+ ▍ Int +


+
+

+ Линк на бенч.
+
+

+ ▍ Float +


+
+

+ Я перепроверял, это не ошибка. Неявный каст стринги в инт практически бесплатный у лисы. Линк на бенч.
+
+

+ Выводы +


+
    +
  1. Лисичка похорошела. +
  2. +
  3. JS сделан за неделю на коленке. +
  4. +
  5. Я не пишу на JS. +
  6. +
  7. Вы тоже прекращайте. +
  8. +

+
+ Играй в нашу новую игру прямо в Telegram! +
+
+
+
+ + +
+ +
+
+
+ Теги: + +
+
+ Хабы: + +
+
+
+
+
+
+
+
+
+ + + Всего голосов 132: ↑116 и ↓16 + + +100 +
+ +
+
+ + + Комментарии + + 77 +77 +
+ + +
+
+
+ +
+
+
+ + + Закрыть + + +

+ Редакторский дайджест +

+

+ Присылаем лучшие статьи раз в месяц +

+
+
+ +
+ +
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
+

+ Комментарии 77 +

+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+

+ Публикации +

+
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+

+ Информация +

+
+
+
+
+
+
+ Сайт +
+
+ ruvds.com +
+
+
+
+ Дата регистрации +
+
+ +
+
+
+
+ Дата основания +
+
+ +
+
+
+
+ Численность +
+
+ 11–30 человек +
+
+
+
+ Местоположение +
+
+ Россия +
+
+
+
+ Представитель +
+
+ ruvds +
+
+
+
+
+
+
+
+
+

+ Ссылки +

+
+
+ +
+
+
+
+

+ Приложения +

+
+
+
+
    +
  • +
    + +
    +

    + RUVDS Client +

    +

    + Приложение для мониторинга и управления виртуальными серверами RUVDS с мобильных устройств. +

    + +
    +
    +
  • +
+
+
+
+
+
+

+ Виджет +

+
+
+
+
+ +
+
+
+
+
+
+

+ Виджет +

+
+
+
+
+ +
+
+
+
+
+
+
+

+ Блог на Хабре +

+
+
+ +
+
+
+
+
+
+
+
+
+ + +
+
+ +
+
+ + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/packages/readabilityjs/test/test-pages/habr.com/url.txt b/packages/readabilityjs/test/test-pages/habr.com/url.txt new file mode 100644 index 000000000..082f687a1 --- /dev/null +++ b/packages/readabilityjs/test/test-pages/habr.com/url.txt @@ -0,0 +1 @@ +https://habr.com/ru/company/ruvds/blog/712386/ \ No newline at end of file