Если ты видишь статью, что язык Х быстрее, чем язык Y – можешь закрывать статью



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

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

    Естественно, все это чушь, но не мне вам об этом говорить. Поэтому к нам в подкаст пришел Андрей Акиньшин — разработчик и математик, кандидат физико-математических наук, мейнтейнер BenchmarkDotNet и perfolizer, автор книги Pro .NET Benchmarking и просто очень, очень крутой инженер.


    Ниже — избранные цитаты.

    Предусмотреть в бенчмарках все невозможно


    У моего коллеги недавно случилось следующее. Он программировал с утра, у него всё было хорошо, всё работало быстро. В какой-то момент всё начало втыкать — Rider работает медленно, IDEA, браузер – всё работает медленно. Он никак не мог понять, в чём же дело? А потом понял. Он работал на ноутбуке чёрного цвета, который стоял у окна. С утра было довольно прохладно, а днём поднялось солнышко, ноутбук очень сильно нагрелся и ушёл в термальный троттлинг.

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

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

    Я приведу аналогию с языками. Когда ты учишь свой первый функциональный ЯП, тебе нужно немного модифицировать своё отношение к миру — понять принципы функционального программирования, то, как вообще нужно мыслить. Потом ты берёшь следующий функциональный язык Х, и у тебя в голове эти принципы уже есть. Ты смотришь пару hello world и тоже начинаешь писать.

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

    Я считаю, что нужно в каждой области доходить до похожего уровня, а дальше идти вширь.

    В какой-то момент в бенчмаркинге я сосредоточился именно на точности измерения, на особенности определенных рантаймов, железяк, ещё чего-то. Потом я перестал открывать для себя Америку каждый раз, и все перфоманс-проблемы начали попадать в уже известные мне классы. И я пошёл вширь – в сторону перфоманс-анализа: что делать с цифрами, которые мы намерили. А это та область, где я ещё не достиг граней познания. Мне уже стали понятны некторые вещи, но впереди еще большой кусок работы — понять, как же всё это применять на практике, какие формулы использовать, какие не использовать, какие подходы хорошие, какие нет.

    Бенчмаркинг ради бенчмаркинга – не самое лучшее, чем можно заниматься


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

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

    Грубо говоря, если у меня в коллекции есть максимум 10 элементов, и есть два варианта — написать простой алгоритм за куб или очень сложный за n*log n — я напишу простой за куб, который будет понятен всем, который будет легко поддерживать и модифицировать. Потому что я понимаю — мои перфомансные ограничения он никогда в жизни не пробьёт.

    Если ты написал медленное решение для маленького набора данных, а потом его использовали для большого, и у этого не было сверхплохих последствий (обычно их нету) – ну, пошли и починили. Зато в голове будет модель того, как в будущем этих ошибок избежать.

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

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

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

    У тебя есть всегда трейдоф между перфомансом и красотой


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

    Я считаю, нужно ориентироваться на текущие бизнес-требования, и в рамках них писать максимально чистый, понятный, красивый, поддерживаемый код. А в тот момент, когда оно начинает жать, (или есть ощущение, что скоро начнёт), то тогда уже что-то менять.

    И даже если ты будешь всегда концентрироваться исключительно на перфомансе — нет такой штуки, как идеально оптимизированный, максимально производительный код. Это значит всё — забыли про C#, забыли про все красивые языки. А лучше вообще писать в машинных кодах, потому что Ассемблер – он тоже по синтаксису ограничен. А если сразу по байтикам писать, то ты получишь перфоманс-boost.

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

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

    У языка нет такого свойства как перфоманс


    Если ты видишь статью, что язык Х быстрее, чем язык Y – можешь закрывать статью. Язык – это математическая абстракция. Это набор правил, согласно которому составляется программа. У него нет производительности, у него нет перфоманса, это нечто, что существует в твоей голове и находит воплощение в текстовом редакторе.

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

    Например, в условном языке на ранних стадиях развития реализовали метод для сортировки с помощью пузырька. Ну, не знаю — ребята хотели побыстрее выкатить релиз, написали простейшую сортировку, которую могли сделать. Ты взял, заиспользовал этот метод, и он оказался медленнее на больших данных, чем в другом языке, где сделан quicksort. Значит ли это, что ты можешь говорить о перфомансе каких-то языков? Нет. Ты можешь говорить, что вот эта конкретно апишка из этого языка на этой операционной системе, вот на этом железе, вот в этих окружениях работает медленнее, чем другая апишка из другого языка в другом environment. Так можно сказать. Но получится очень длинный абзац текста, чтобы сформулировать правильно.

    Условно можно сказать, что C++ в большинстве случаев работает быстрее, чем JavaScript. Но более правильно будет сказать, что С++ программисты с хорошим опытом С++, которые пишут на С++, напишут программу, которая скорее всего будет быстрее, чем джаваскриптер на JavaScript напишет что-то, что будет работать в браузере.

    Но и здесь есть очень много оговорок. А что если чувак, который писал на JavaScript скажет, что это не так, и пойдёт там на какой-нибудь там WebAssembly или еще чего-нибудь переделывать. Или найдет на GitHub суперинтерпретатор-компилятор JavaScript, который работает с очень усечённым подмножеством JS на три с половиной синтаксические конструкции, но зато выдаёт сверхбыстрый нативный код.

    И вот там при желании можно написать такой код, который обгонит С++. Более того, можно написать свой компилятор JavaScript, который будет задизайнен, чтобы компилировать одну-единственную программу и обогнать по скорости «плюсы». И Это, в принципе — валидный вариант.

    Социальное давление популярного опенсорсного проекта


    С ростом и известностью проектов приходит некий уровень ответственности. Но на самом деле у тебя не появляются обязательства. Этот факт не всегда просто понять, особенно когда приходят всякие люди на GitHub и говорят: «У меня вот здесь не работает! Почините срочно! Мне очень надо, чтоб вот это заработало. Идите и чините!» Или чувак заводит ишью, а я в отпуске. Проходит три-четыре дня, я даже не видел того, что он что-то там завёл. Я где-то отдыхаю, и чувак начинает — «Какого хрена вы мне не отвечаете? Что вообще у этого проекта за комьюнити?! Вы вообще все отвратительные люди, с вами надо делать плохие вещи! Я потратил своё время, написал вам, что вы неправы, а вы с этим вообще ничего не делаете, уже четыре дня меня игнорируете! Как так можно?!»

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

    И вот, когда появляется иммунитет против людей, которые от тебя что-то хотят, то жить становится намного проще. Сейчас я добираюсь до BenchmarkDorNet, когда у меня есть время и настроение покодить. Я знаю, что там много багов. Они в основном некритичные, и касаются каких-то маргинальных случаев — в таком-то окружении с последней превьюхой пятого DotNet что-то где-то не работает. Ну и ладно, пущай не работает. Когда у меня будет настроение, я пойду и пофикшу.

    Если другим людям нужно, они могут пофиксить сами и прислать пулреквест — сделаю ревью, когда у меня будет время и настроение.



    Смотрите весь подкаст здесь.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 44

    • UFO just landed and posted this here
        +6

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

        • UFO just landed and posted this here
            +1
            Есть доля истины в ваших словах.
            Однако, не все же трудозатраты направлены на мгновенную прибыль (open-source тому подтверждение). Часто тот или иной маркетинговый ход, который не генерирует прибыль, в купе с прочими инструментами приносит определённые дивиденды.

            Возможно, аудио/видео-формат направлен на торговлю хлебалом создание узнаваемого медийного образа.
            • UFO just landed and posted this here
              0
              Мне не понятно зачем это надо авторам.

              Потому что есть отдельные «потребители контента», которые читать не хотят, а хотят видео или хотя бы аудио. Следовательно либо авторы делают подкаст/видео, либо теряют часть аудитории.
              • UFO just landed and posted this here
                  0
                  Ну так тут ещё проще. У таких авторов основная аудитория сидит на ютюбе и они для них в первую очередь и работают. Или например денег они на ютюбе больше получают. Или ещё какой-то профит там у них выше.

                  А тексты писать для таких авторов это дополнительная работа и они этим заниматься не хотят.
                +1

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

              0
              можно играть в танки и слушать подкаст
              конечно, на быстрых танках это не так удобно, но вот на маусе, е100 — мммм, выехал, встал под углом и расслабился, спокойно слушаешь подкаст, пока пушка не перезарядится
                0
                Лучшая игра для прослушивания подкастов — симулятор дальнобойщика, EuroTrack Simulator, например. Думать в игре вообще не надо, а так можешь и время с пользой проводить.
                0
                Многие за рулем слушают.
                –11
                Перформанс — это из мира моды чото про выход моделек? Трейдоф — это про торговлю?
                  +4
                  специалист в области перфоманса

                  Это артисты, устраивают перфомансы, представления. Цирковые бродяги, например. Вообще с этим словом статья играет новыми, неожиданными красками!


                  Ну не писать же "производительность" или, не дай бог, "скорость".

                  +1
                  Или найдет на GitHub суперинтерпретатор-компилятор JavaScript, который работает с очень усечённым подмножеством JS на три с половиной синтаксические конструкции, но зато выдаёт сверхбыстрый нативный код.

                  1. При условии что такой интерпретатор существует
                  2. Если интерпретатор поддерживает JS не полностью, не надо называть его интерпретатором JS.

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


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

                  Перфоманс есть у конкретных рантаймов, окружений, у конкретных программ, у конкретных апишек.

                  Да, но. Особенности языка накладывают ограничения на то каким может быть его рантайм.

                    +3

                    Для TS, как и для JS возможен компилятор просто исходя из тьюринг полноты доказывать тут ничего не нужно.

                      +1
                      Интересно посмотреть во что будет скомпилирована функция eval().
                        +2

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

                          +1
                          на той же iOs нельзя генерировать код «на лету» и его выполнять…
                          придётся таки тащить с собой интерпретатор…
                            +2
                            на той же iOs нельзя генерировать код «на лету» и его выполнять…

                            Почему нельзя? Ставим виртуальную машину Java и что-то вроде этого. Но проще делать приложение с бекэндом и отправлять запрос на бек, где все можно сгенерировать и возвращать ответ.
                              0

                              Так нельзя виртуальные машины :)

                                0
                                Но это уже проблема iOs
                        0

                        «Чистый» компилятор невозможен, вернее возможен не на всех платформах, так как есть функция eval(), которая, по сути, является механизмом макроподстановок.

                          0

                          Раскрываем eval на этапе препроцессинга. Макросы ведь не один день в компилируемых языках присутствуют.

                            0
                            И как вы будете раскрывать строку на этапе препроцессинга, если эту строку программа читает из файла/сети/из пользовательского ввода в процессе выполнения?
                              +1
                              Ну, например, в JVM (и Java тоже) есть множество способов сгененерить классы из текста во время выполнения. Компилируемый язык может компилировать код прямо во время работы приложения. Будет ли это быстрее работы интерпретатора — будет зависит от оптимизаций компилятора/интерпретатора.

                              Если код чаще всего одинаковый и отличается лишь аргументами с производительностью тоже скорее все будет нормально в обоих случаях, если кешировать результаты.
                              0
                              а если в eval передаётся, например, код, который пришёл из БД (из REST API) или введён пользователем?
                          0

                          Такое существовало еще лет 15 назад и писалось элеметарно используя SpiderMonkey/V8. Вызывался нативный код, синтаксис ограничивался довольно сильно, все дела.

                            0
                            Если вы ограничили синтаксис, это уже нельзя называть JS.
                              0

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

                                +1
                                Я с вами категорически несогласен. Есть стандарт, описывающий что такое C++. Или в ваш компилятор можно засунуть всё многообразие программ, соответствующих стандарту, и тогда он является компилятором C++ или нельзя и тогда он не является компилятором C++. Он является компилятором чего-то другого.

                                Если следовать вашей логике, то «компилятор», который всегда даёт на выходе программу, печатающую «Hello world», является «компилятором C++, хоть и с ограничениями».
                                  0

                                  Т.е. Вы утверждаете, что при выпуске новой версии стандарта с++ в мире пропадают все компиляторы с++? Ведь существующие компиляторы еще не поддерживают новую версию стандарта с++.

                                    +1
                                    Ну давайте рассуждать в терминах «компилятор C++14», «компилятор C++20», тогда проблемы с пропаданием не возникает.
                                    • UFO just landed and posted this here
                          +7

                          Я не согласен. В статье слишком идеализированное представление. Языки компилируются реальными компиляторами под реальное железо, возможности и ограничения языка просто обязаны влиять на перформанс.
                          Например, C++ позволяет управлять размещением объектов в памяти, а языки со сборкой мусора — нет, и вдобавок в них объекты перемещают. Это значит, что в целом классе задач, завясящих от локальности расположения объектов памяти, языки со сборкой мусора будут принципиально медленнее.
                          Или, например, языки со статической типизацией при прочих равных не будут медленнее языков с динамической — в статическом языке вся информация о типах уже есть, в динамическом её ещё предстоит выяснить во время исполнения.
                          Или, например, GIL в Python — снижает производительность до уровня однопоточной. Сам дизайн языка плохо совместим с многопоточностью и скорее всего проще придумать новый язык, чем писать многопоточный интерпретатор для старого.
                          Ещё пример — constexpr в плюсах позволяет быть уверенным, что вычисления будут на этапе компиляции.
                          В каком-то другом языке можно только надеяться, что неидеальный программист напишет код, который реально можно вычислить во время компиляции, и что неидеальный компилятор догадается это сделать.

                            –2

                            Конечно, если сравнивать скажем скорость вызова метода в сишечке и в питоне, то первая выиграет по скорости. Тем не менее быстродействие прграмм зависит от очень многих факторов, а языки отличаются не только по скороти выполнения программ, но и по скорости их написания.
                            Тем не менее заголовок не врет: "Если ты видишь статью, что язык Х быстрее, чем язык Y – можешь закрывать статью", так как ее автор скорее всего специалист в области перфоманса, нежели программист.

                              0
                              Ну constexpr не совсем позволяет быть уверенным, что вычисления будут на этапе компиляции. Он говорит что хорошо бы чтобы они были на этапе компиляции(и скорее всего они будут), но это не всегда так
                                0

                                Это неверно. Это constexpr функция не обязана вычисляться в compile time.А constexpr переменная обязана иметь compile-time значение и их можно использовать в качестве аргументов шаблонов

                              0
                              Меня все больше смущает куда двигается индустрия создания софта и как это повлияет на наше будущее.
                              Парадигма «сделай красиво, а оптимальность это на сладкое» зародилась во времена когда делать производительный код было дороже чем подождать годик и гении из Интел подгонят новое железо на котором код будет работать в 2 раза быстрее.
                              Эти времена прошли. Может еще вернутся, но пока мы живем в реальности другой.
                              Целые новые поколения инженеров-программистов оперируют парадигмой которая была создана в ныне не существующей реальности. Похмелье после этого праздника будет горьким.
                              Я согласен с автором в утверждении, что не нужно ставить производительность во главу угла, но оперировать красотой как чем то универсальным — это путь тупиковый.
                              Мы должны думать о производительности, это наша работа, чтоб помимо того чтоб у пользователя работал наш софт, он должен работать так чтоб пользователь не чувствовал «у меня украли время, почему он работает так медленно?!»
                              А с современным валом статей и размышлений «лишь бы красиво» мы уже находимся в ситуации когда банальный софт сжирает память и процессор без сожалений, просто потому что текущие инструменты и языки кажутся инженерам удобными и они приносят в жертву интересы пользователей.
                                +2

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


                                Если же серьёзно — от языка может зависеть использование heap'а (например, java без heap'а существовать не может) и доступные конструкции (например, если язык не допускает использование int'ов, а даёт только float'ы, то производительность с целочисленными операциями у него будет соответствующая).

                                  0
                                  я сравнивал производительность bwbasic и Rust

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

                                    0
                                    «то производительность с целочисленными операциями у него будет соответствующая» — это бабушка еще на двое сказала, есть IR, если орпеделенный статический анализ показывает, что float тут не требуется и достаточно использовать int, то производительность может быть равноценной с языком в котором есть int. Компилятор может произвести так же различные преобразования когда уверен, что точность вычислений будут сохранена относительно исходного кода, тоесть в конечном итоге зная примерно как работает компилятор или интерпритатор или байткод машина вообшем случае возможно написать достаточно производительный код или эквивалетный код, была целая статья на эту тему «кротовые норы в Js». Касательно Java и heap, честно стандарт не читал, но почти уверен, что определение heap в нем не дается, если я правильно понимаю это абстракция исполнительной среды, не вполне уверен но думаю вполне можно придумать среду в которой не будет heap в привычном нам понимании
                                    +5
                                    Как же похорошел dotnet при Акиньшине
                                      –2
                                      Если другим людям нужно, они могут пофиксить сами и прислать пулреквест — сделаю ревью, когда у меня будет время и настроение.

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

                                      Only users with full accounts can post comments. Log in, please.