[Перевод] Либо быстро, либо неправильно

image

В 2018 году я упражнялся на Advent of Code (здесь вы можете посмотреть стримы моих решений). Каждый день в декабре они публикуют небольшую проблему, и вы должны написать программу, которая её решит. Обычно это занимает от пары минут до пары часов и это довольно весело, я рекомендую вам попробовать. Когда задача выполнена, она всегда доступна, не только в декабре.

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

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

С другой стороны, если решение медленное, вы можете использовать любую вычислительную мощность, и ее все равно будет недостаточно. Это могло бы сократить время работы с трех лет (на ноутбуке) до одного года (на самом мощном компьютере, который я только могу собрать). Какая разница? Все равно слишком долго.

Теперь перейдем к программному обеспечению. Легко назвать решения Advent Of Code ошибочными, когда они медленные, поскольку мы знаем, что быстрое решение должно существовать. С реальными проблемами никто этого не гарантирует.

За исключением некоторых случаев.

Собственно, довольно часто.

На самом деле, я бы сказал, почти всегда.
Давайте посмотрим. У меня есть библиотека под названием Datascript. Это устойчивая структура данных/база данных и так уж вышло, что она реализована для двух платформ: JS и JVM. Более того, она фактически написана на Clojure, и большая часть ее кодовой базы используется обеими платформами. Это означает, что мы знаем, что обе версии всегда совершают одни и те же действия. Есть небольшой слой, который покрывает специфичные для платформы детали, такие как типы данных и стандартная библиотека, но остальное является общим. Дело не в том, что одна версия является оригинальной, а другая — неэффективным портом. Они обе играют в одну игру.

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

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

Clojure 1.10 on JVM:

  • REPL boot time: 1.5 sec
  • Compile time: 6.5 sec
  • Tests time: 0.45 sec

ClojureScript 1.10.439 with advanced compilation:

  • Compile time: 78 sec
  • Tests time: 1 sec

ClojureScript 1.10.439 without Google Closure compilation:

  • Compile time: 24 sec
  • Tests time: 1.3 sec

Итак, о чем нам говорят эти числа? По сути, для обработки одного и того же кода вы можете потратить ~8 секунд, 24 секунды или 78 секунд. Выбор за вами. Кроме того, запустив ту же программу, вы можете получить результат за полсекунды, одну секунду или почти полторы секунды.

«Но подожди, Tonsky, их нельзя сравнивать! Это две большие разницы! Они созданы, чтобы делать совершенно разные вещи! Один из них работает в браузере!»

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

Что так долго делают компиляторы ClojureScript/Google Closure? Они зря тратят ваше время, вот что. Конечно, никто не виноват, но, в конце концов, все это решение просто неверно. Мы можем делать то же самое намного быстрее, у нас есть доказательства, у нас есть средства для этого, но так уж получилось, что это не так. Но мы могли бы. Если бы захотели. Эти огромные накладные расходы, которые вы платите, вы платите зря. Вы ничего не получаете от JS, кроме удвоения времени выполнения и астрономического времени сборки.

То же самое относится ко всем языкам с ужасно долгим временем сборки. Дело не в том, что они не могли бы работать быстрее. Они просто предпочитают не делать этого. Программа на C ++ или Rust слишком долго компилируется? Что ж, OCaml, вероятно, мог бы скомпилировать эквивалентную программу менее чем за секунду. И это по-прежнему будет быстро на уровне машины.

«Вау, вау, помедленнее! Это еще более несправедливо! Теперь это не просто две большие разницы, теперь это будто бы зубные щетки и космические корабли. Вы полностью игнорируете то, что дает каждый язык. Есть причина, по которой они так много времени тратят на компиляцию, понимаете?»

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

Представьте себе: из Москвы в Новосибирск, сердце Сибири, летит самолет, который преодолевает 2800 километров за 4 часа. Еще есть поезд, который преодолевает такое же расстояние за три дня. В поезде нет душа, плохая еда, кровати, на которых нельзя спать. А самолет — это комфортабельный современный самолет. Что бы вы выбрали? Цена такая же. Единственная разница — это ваш комфорт и ваше время.

image

Если мы примем это как метафору разработки программного обеспечения, вы удивитесь, что программисты с радостью выбирают поезд. Они даже до хрипоты спорят, что есть неопровержимые причины для выбора поезда. Нет, они не возражают, чтобы компилятор не торопился «поработать». Хотя существуют более быстрые способы добраться до того же пункта назначения. Легко запутаться, споря о деталях, но помните: мы все оказываемся в одном месте, независимо от того, на каком языке мы говорим.

Браузеры? Та же история. HTML — довольно неэффективный способ разместить пиксели на экране. Компьютер, который мог бы отображать миллионы полигонов в кадре, может с трудом прогружать веб-страницу. Как и в случае с решениями «Advent of Code», это не зависит от мощности вашего компьютера. И даже высокооптимизированный веб-код, основанный на Canvas и WebAssembly (Figma), заставляет вентиляторы моего Macbook крутиться при полной тишине при запуске собственного Sketch.

image

 — *похлопывает по крышке* Этот ПК способен запустить Crysis 3 в 4K на 144fps.
— Но может ли он запустить Atom?

Просто существуют пределы того, насколько далеко может зайти это неправильное решение. Текстовые редакторы на Electron не могут менять размер окна в реальном времени и проседают по кадрам, когда вы просто двигаете курсором. Slack на iMac Pro будет таким же медленным и требовательным к памяти, как и на 12-дюймовом Macbook.

Всё решение, «web stack», в целом, ошибочно. То же самое можно было бы сделать быстрее и эффективнее — ведь так много потраченного впустую потенциала. Пора признать это и начать все сначала. Существуют быстрые текстовые редакторы и программы для общения, которые вполне доступны даже для самых слабых нетбуков.

Я могу продолжать и продолжать. Следует помнить следующее: подумайте, что вы от этого получаете. Сопоставимы ли проблема и потраченные на нее ресурсы? Легко найти оправдания тому, почему дела обстоят так, как есть. Все они, вероятно, действительны, но это отговорки. Мы знаем, что возможны более быстрые программы, и это делает все остальное просто неправильным.

© Habrahabr.ru