Как стать автором
Обновить
30
0

Пользователь

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

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


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

Я предложил вам перепроверить ваши замеры, у вас же есть идеи по-поводу того, что concat быстрее push. Это ли не чайник Рассела?


Вы предлагаете мне потратить значительно большее количество времени чем то что вы потратили на написание этой статьи.

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


Извините, но даже не понятно что измерять. Вы сначала сравниваете с мастером, теперь с 17.5.0. Что за проект вы анализировали, не указано.

В самом начале статьи указано, что за проект :). master меняется постоянно, и это нормально, сейчас показатели скорости еще выше, поэтому можете и с master'ом сравнить, клонировать репозиторий и выполнить две команды — должно занять меньше времени, чем написать комментарий без ссылок, в котором вы не видите улучшений.

Да, вы правы, переформулировал. Спасибо большое!

Вам несколько человек, включая меня, написали что вы НЕ изменили сложность алгоритма. BigO у вас НЕ изменилось. Отсюда все вопросы к измерениям. Не понятно почему стало лучше. Оба варианта алгоритмически идентичны.

Ну как же не изменилось, если квадратическая сложность стала линейной.


Это не идентично. Ваш вариант вернёт массив массивов, а мой плоский массив.

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


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

В мастере многое уже поменялось, разработка не стоит на месте, правильнее наверно сравнивать версии 17.5.1 и 17.5.0.


Проблема в том, что не понятно из-за чего произошло изменение, это вызывает все вопросы.

Я писал выше, что V8 лучше оптимизирует маленькие функции, к примеру эти изминения тоже привели к увеличению быстродействия.


Совершенно не понятно при чём тут монорепозиторий.

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

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


В теории нет разницы между теорией и практикой. А на практике есть.

Речь идет о том, что количество входных данных не влияет на количество операций выполняемых функцией, поэтому да: не важно 10 элементов в массиве или миллиард.

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

Тут также каждый раннер вызывается один раз и потом для каждого файла (а их будет 50 штук) будет вызван processFile.

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


В двух словах идея такая:
имеет горячий код с квадратической сложностью -> делаем замеры -> оптимизируем код до линейной сложности -> делаем замеры -> код стал быстрее и проще в поддержке -> PROFIT


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

Как минимум код стал чище, а еще движку V8 проще оптимизировать маленькие функции, что хорошо отражается на производительности, по этому хуже он не стал точно :).


Это можно полечить например с помощью чего-то вроде [].concat(...runners.map(r => r()));, тогда хотя бы не будет добавления элементов по одному.

Буду благодарен за изминение кода + замеры, а еще мне не до конца понятно использование concat + spread при том, что map возвращает новый массив, и это равнозначно следующему коду:


runners.map((r) => r())

Но главный вопрос, почему же стало быстрее не давал мне покоя и кажется я нашёл ответ. Проводя контрольный замер автор сравнивает мастер+свой патч с некоторой версией v17.5.0. Рискну предположить что разница из-за какого-то другого изменения.

Ну это же легко проверить, вот ченчлог к версии v17.5.1, в этом и идея, что менялся только описанный код.


А тактов стало меньше потому что автор разделил функцию на три и они просто между ними размазались.

Не только тактов стало меньше, но и время выполнения сократилось, что намного важнее и гораздо заметнее для монорепозитория.

На всех проектах, которых я работал, ворнинги не использовались никогда :). И так получалось, что не имело значения: это фронт на angular/react, бэк на node.js, либо больше 200-та npm-пакетов. Правило либо исправляется, либо отключается до лучших времен — все просто. В статье об этом было сказано для того, что бы показать фундаментальные отличия в философии инструментов.


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


Однако, если разработчику удобнее с ворнингами и это делает код лучше, а работу продуктивнее — то, пожалуйста, это личный выбор каждого :).


С помощью плагина eslint-plugin-putout возможно использовать ESLint с привычными ворнингами совместо с Putout, который исправить может все, что найдет, при этом показывая только ошибки.

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


Еще есть возможность написать кодмод, который будет в автоматизированном режиме править deprecated методы, гораздо более простым способом, чем делая это с помощью ESLint. Такой подход, например, применяется для правил Putout, с помощью плагина @putout/plugin-putout.


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

А можно ли назвать такой подход автоматизацией, если в обязательном порядке нужно проверять код?

Да можно.


плагин @putout/plugin-react-hooks, представляет собой базовую реализацию, которую можно развивать для поддержки нужных условий.

Если вас заинтересовала тема автоматизированного рефакторинга, вы так же можете написать интересующие правила, пул реквесты приветствуются.

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

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

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


Вы забываете, что есть еще кастомные хуки, вроде описанного Деном Абрамовым useInterval, в них можно сложить многие описанные вами проблемы, такие как useEffect, useMemo и прочее, взгляните на примеры с usehooks. Опять же, статья не о том, зачем нужны хуки, а том как автоматизировать рефакторинг, на примере хуков.

Пример, описанный в статье — иллюстративный. Иногда выход новой мажорной версии библиотеки ломает обратную совместимость, и тогда варианта два:


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

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

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

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

PureComponent проигнорирует, а shouldComponentUpdate удалит. Используя приемы описанные в статье, поддержку и того и другого легко добавить.

Для этого стоит написать новые правила, плагин @putout/plugin-react-hooks, представляет собой базовую реализацию, которую можно развивать для поддержки нужных условий.

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

Для простоты изложения и наглядности, из всего что касается React Hook'ов использовалась лишь функция useState. useCallback можно будет добавить в будущем. Статья описывает способ, с помощью которого это можно реализовать, а архитектура плагина состоящая из достаточно простых правил к этому стимулирует.

Для babel 5-ой версии параллельный обход всеми плагинами был штатным способом.
И был отдельный ключ(не помню для CLI, или babelRc), который гонял плагины последовательно
И в сложных плагинах(когда замена производилась не для текущей ноды) паралельный обход создавал трудности

Это действительно создает трудности, в прочем, для putout в данный момент времени, мало смысла в паралельной обработке, поскольку, работает он достаточно быстро, и разрабатывается на компьютере, на котором прирост скорости от использования redrun имеет огромное значение, если учесть что для большинства пользователей ускорение оказалось не особо ощутимым, putout у них будет летать :). В прочем, в случае медленной работы у пользователей, можно будет подумать об ускорении. Что вы скажете о скорости? У вас быстро файлы трансформируются?


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

Как выглядел этот апи? Я не нашел об этом информации.

Eslint умеет исправлять ошибки, хотя его API и несколько ограничено/непривычно по сравнению с трансформациями babel

API Eslint значительно более ограничены и ориентированы в основном на форматирование, в то время как babel содержит, например такую удобную вещь как template, что значительно упрощает создание новых узлов.


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

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


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

Да, это действительно медленее, вы уверены, что babel и eslint объединяют параметры всех визиторов и обходят их за один проход? В этом может быть смысл. Идея каждому плагину использовать свой traverse возникла при разработке плагина remove-unused-variables, принцип работы сводится к тому, что бы найти все переменные, и определить использовались они или нет, после чего те которые использовались откидываются. Для того, что бы выполнить такую операцию необходимо обойти все AST-дерево сперва. После чего фильтровать. Если плагин будет предоставлять только опции обхода, он не сможет узнать, что обход дерева закончен.


  1. Трансформации делаются последовательно, и не могут конфликтовать с другими плагинами.
    Параллельные трансформации — не самая легкая в разработке и отладке вещь.

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


2.1. Но и взаимодействовать они также не могут, и их работа зависит от порядка. Например если сперва вызывается плагин удаляющий пустые блоки, а только потом удаление debugger/console.log, после чего могут остаться пустые блоки, которые уже не будут удалены

Есть идея во-время исправлений проходить по коду до тех пор, пока не будут исправлены все ошибки (либо до какого-то количества раз, например 10), как это реализовано в eslint. Сейчас же, если после одного прохода putout останется что исправить, его можно будет вызвать еще раз, и он улучшит ситуацию.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность