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

image

Для сравнения производительности используется геометрическое среднее времени выполнения 10 различных программ. Согласно последним результатам Rust превзошел С++ в 6 задачах из 10.

Под катом некоторое детали и мнение о дальнейших перспективах.

Как правильно интерпретировать результаты?


Benchmarks Game — популярный проект, в котором языки программирования сравниваются на основании измерений производительности написанных сообществом программ, решающих поставленные задачи. Как правило, для каждой задачи существует как минимум несколько хорошо оптимизированных реализаций на одном и том же языке. Для подведения общих итогов выбирается только одна реализация для каждого языка, показавшая наилучший результат.

Следует ожидать, что для популярных языков программирования наилучшие предложенные решения близки к пределу производительности самих языков. Последние месяцы языки C, C++ и Rust показывают очень близкие результаты. Таким образом, можно сделать вывод, что хорошо оптимизированная программа на Rust в среднем не должна уступать по производительности хорошо оптимизированным программам на C или C++.

Но ведь бенчмарки ничего не значат!


И да, и нет.

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

К примеру, Rust показывает худшие сравнительные результаты на тех задачах, решения которых требуют использования регулярных выражений. Из этих результатов можно сделать вывод, что реализация регулярных выражений на Rust (крейт regex) работает медленнее чем PCRE2, при решении поставленных задач. С другой стороны, Rust заметно превосходит C++ для задач, требующих агрессивного применения SIMD операций.

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

Может ли текущая ситуация измениться?


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

Стоит ли ожидать дальнейшего улучшения производительности Rust?


Да. Компилятор Rust — относительно молодой проект, и значительное количество возможных оптимизаций еще просто не реализовано полноценным образом. Основной преградой является LLVM, который формально хоть и является универсальным транслятором, на практике хорошо оптимизирован для C/C++, и не очень хорошо справляется с байт-кодом (IR), полученным из других языков программирования.

Rust — более строгий язык программирования чем C и С++. Это означает, что для типичной программы Rust может дать гораздо больше гарантий транслятору (LLVM), который в свою очередь может их использовать для выполнения более агрессивных оптимизаций.

Таким образом, следует ожидать, что в ближайшие годы Rust сможет заметно улучшить свою производительность. В то же время, компиляторы C/C++ развиваются уже несколько десятилетий, и за это время достигли предела своих возможностей. Их дальнейшие оптимизации ограничены в основном особенностями языка, а не реализациями компиляторов.

Еще несколько лет назад мало кто мог представить, что возможно с высокой эффективностью писать код, превосходящий по производительности реализации на C/C++, используя при этом новый язык программирования и даже (прости господи) функциональный подход. Я думаю, что в ближайшее годы Rust докажет обратное своим примером, начав занимать уверенные первые места в различных тестах производительности. А вы?

Комментарии (277)


  1. youROCK
    17.12.2019 20:13
    +7

    Я склонен верить, что в большом количестве сценариев у Rust есть преимущество в плане возможности проведения различных оптимизаций, потому что это memory-safe язык. Насколько я понимаю, оптимизаторы Си сильно ограничены из-за того, что в языке есть арифметика указателей и не всегда можно однозначно сказать, используется ли какой-то определенный фрагмент памяти или нет (strict aliasing?).


    1. technic93
      18.12.2019 01:00
      +4

      Да но в расте это не включено везде, потому что llvm ломается


      1. mayorovp
        18.12.2019 10:27

        А что ещё осталось включить?


      1. PsyHaSTe
        18.12.2019 11:43

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


        Можно и щас включить через флаг, но на свой страх и риск.


    1. Antervis
      18.12.2019 12:15
      -5

      Я склонен верить, что в большом количестве сценариев у Rust есть преимущество в плане возможности проведения различных оптимизаций, потому что это memory-safe язык

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


      1. khim
        18.12.2019 12:52
        +5

        Наоборот: безопасное подмножество rust как раз позволит компилятору делать некоторые оптимизации.

        Простейший пример:

          foo(int& x, int& y, int[] z) {
            ...
            x = y;
            ...
          }
        


        Компилятор C++ обязан в этом месте читать из памяти и писать в память — потому что он не знает объекты x и y — это один объект или это разные объекты… а может один из них — это ещё и элемент массива?

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


        1. Antervis
          18.12.2019 13:22
          -2

          "некоторые оптимизации", про которые написал я, это не те "некоторые оптимизации", про которые написали вы. Потому и "некоторые"


        1. mk2
          18.12.2019 17:54
          -2

          Используйте в С/С++ restrict и будет вам счастье.


  1. Siemargl
    17.12.2019 21:11
    +2

    Язык с ручным управлением памятью быстрее, чем язык с автоматическим при прочих равных. Ваш К.О.

    Хотя немножечко неточно — тесты не при прочих равных, т.к g++ != LLVM, надо было сравнивать одинаковый тулчейн.


    1. interprise
      17.12.2019 21:14
      +2

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


      1. Siemargl
        17.12.2019 21:32
        -1

        в расте — ручное, в с++ arc


      1. PsyHaSTe
        17.12.2019 23:26
        +1

        Позиция одного из ключевых разработчиков раста (и автора The Rust Programming Language) — в расте статический GC.


        1. khim
          18.12.2019 00:57
          +3

          По поводу терминологии можно спорить до посинения, но практически граница проходит между языками, умеющими автоматически «разрывать циклы» и языками, которые этого не делают. А считать ли Rust (и современный C++, кстати) «языком с GC» или «языком без GC» — это неважно.

          Потому что языки, «разрывающие циклы» — платят за это высокую цену. Как мне кажется… слишком высокую цену.

          Потому что в таких языках вы не знаете когда и как вы будете «платить» за выделение и освобождение памяти, можете только догадываться — опираясь на конкретную реализацию GC конкретно вот в конкретной версии вашего runtime. А завтра — всё может быть совсем по-другому.

          О том что это важно можно заметить по количеству статей, посвящённой этому вопросу на Хабре (да и на любом сайте где обсуждается C#, Java, JavaScript и тому подобные языки). Рано или поздно об этом приходится задумываться всем, кто на этих языках работает.

          А вот при обсуждении C++ тонкости общения с jemalloc'ом или tcmalloc'ом итересуют очень малый процент разработчиков… потому что если вы не пытаетесь «разрывать циклы», то вы всё ещё не можете сказать точно какую цену вы заплатите за выделение и освобождение памяти… зато можете сказать точно — когда эта цена будет нулевой… и оказывается что для 99.99% случаев на практике — этого достаточно.


          1. denisshabr
            18.12.2019 15:31

            Что значит разрывать циклы? break и continue?


            1. PsyHaSTe
              18.12.2019 15:45
              +4

              Речь про циклические зависимости. Например:


              void Foo() 
              {
                var a1 = new A();
                var a2 = new A();
                a1.Next = a2;
                a2.Next = a1;
              }

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


              1. Siemargl
                18.12.2019 19:09

                Поправка «если неправильно использовать рефкаунтинг» — но да, все возможно, компилятор не спасет. Верно так.


                1. PsyHaSTe
                  18.12.2019 19:22

                  То есть это правильное использование рефкаунтинга? Почему тогда вот этот код падает? https://repl.it/@Pzixel/AcceptableInsidiousUsernames


                  1. KanuTaH
                    18.12.2019 19:30
                    +1

                    Как вы увидели, что код по вашей ссылке падает, если он даже не собирается.


                    1. Siemargl
                      18.12.2019 19:34

                      Потому что не Раст. ЛОЛ

                      Я проверил верно исправленный — не падает.

                      Посмеялся, глядя на процесс правок. Нет чтобы учебники читать, всё критикуют…

                      cout << (x->Next).lock()->Value;
                      так нельзя, lock() может вернуть nullptr


                    1. PsyHaSTe
                      18.12.2019 19:41

                      Спасибо, поправил.




                      Основная идея тут не в том, что я не умею проверять что weak_ptr.lock() вернул нулл, а в том, что структура данных именно что должна владеть данными, а не просто ссылаться. В некоторых случаях типа деревьев weak_ptr может помочь, но и в расте точно так же получится Weak использовать.


                      Речь про полноценный ГЦ была именно в том что разработчику там не надо забивать себе голову — он всегда делает сильную ссылку, а с циклом разберется рантайм. И это действительно помогает. А еще это помогает делать структуры вроде "массив и ссылка на первый элемент". Self-referential structures огромная морока в расте, для которой целый Pin изобрели, в плюсах я не слышал ни про какое известное решение, а в языках с ГЦ это вообще не проблема.




                      так нельзя, lock() может вернуть nullptr

                      Ну вот, так и знал что мне про это напишут...


                      1. KanuTaH
                        18.12.2019 19:50

                        :) Ну да, поскольку тут владение a2 по сути отсутствует (weak_ptr не дает владения), то в момент завершения Foo() для него вызовется деструктор, expired() для соответствующего weak_ptr вернет true, а lock() вернет nullptr.

                        P.S. Честно говоря, рефкаунтинг сам по себе и не способен бороться с циклическими зависимостями. Поэтому в питончике вдобавок к рефкаунтингу добавили (отключаемый) gc.


                        1. khim
                          18.12.2019 20:29
                          +1

                          Поэтому в питончике вдобавок к рефкаунтингу добавили (отключаемый) gc.
                          В третьей версии вроде как уже неотключаемый.

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

                          С учётом того, что одна из самых популярных в мире платформ (iOS) обходится без «полноценного GC»… похоже что Rust идёт в верном направлении…


        1. Stormwalker
          18.12.2019 14:50
          +1

          Стив Клабник не совсем ключевой разработчик — он главный в команде документации. Хорошо знает, как устроен GC, Patric Walton, но он пишет в основном в твиттере ссылка


    1. struvv
      17.12.2019 21:32

      что подразумевается под «автоматическим управлением памятью»?


    1. khim
      18.12.2019 00:40
      +8

      Язык с ручным управлением памятью быстрее, чем язык с автоматическим при прочих равных. Ваш К.О.
      Вот только прочие — неравные. Ваш К.О.

      Собственно многие программы до сих пор написаны на Fortran, потому что аналогичные же программы на C++ — медленнее. А медленнее они как раз потому что «Язык с ручным управлением памятью» — не всегда быстрее.

      Разработчикам компиляторов C++ проблемы алиасинга реально часто мешают порождать оптимальный код. И у Rust'а тут вполне себе есть преимущество.

      P.S. И да: тут как раз вопрос лежит в практической плоскости. Теоретически любую программу на Fortran вдумчивой работой с типами и restrict можно довести до более быстрой программы на C++. А вот практически — у реально существующих разработчиков — это не выходит… Такие дела.


      1. Siemargl
        18.12.2019 08:48
        -4

        В С++ применяется ARC — еще раз, это Automatic Reference Counting. Я не понимаю, почему в противопоставлении вы пишете, что С++ — с ручным управлением %-)

        И кстати, я немного отстал от жизни и не помню наличия работы с хипом в Фортране.


        1. Siemargl
          18.12.2019 12:35

          UPD. В Фортране95 есть ручное управление памятью операторами ALLOCATE, DEALLOCATE.
          Собственно тезис строго наоборот.


        1. Bas1l
          18.12.2019 15:10
          +1

          unique_ptr — вовсе не подсчет ссылок. shared_ptr — это подсчет ссылок. Но в хорошей кодовой базе его почти не должно быть. Он в идеале нужен только для shared ownership (внезапно), а оно нужно очень редко.


        1. PsyHaSTe
          18.12.2019 15:46

          К слову, обычно буква a в arc означает atomic


          1. Siemargl
            18.12.2019 19:12
            -2

            К слову, иногда лучше жевать.
            shared_ptr не атомарный счетчик ссылок


            1. PsyHaSTe
              18.12.2019 19:19

              В блоке управления он вполне себе атомарный


              Note that the control block of a shared_ptr is thread-safe: different std::shared_ptr objects can be accessed using mutable operations, such as operator= or reset, simultaneously by multiple threads, even when these instances are copies, and share the same control block internally.


              1. Siemargl
                18.12.2019 19:25
                -1

                Маловато будет.
                Советую читать Мейерса


            1. Antervis
              18.12.2019 19:34
              +1

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

              *За исключением юзкейса, когда два потока пытается без синхронизации менять один и тот же инстанс shared_ptr. Тогда реальное число инстансов может рассинхронизироваться со значениями счетчиков. Для этого существует набор атомарных операций над shared_ptr, а также в стандарте будет std::atomic_shared_ptr.

              Советую читать Мейерса
              Хороший совет — он в книжке как раз обо всём этом рассказывал.


        1. khim
          18.12.2019 17:49
          +1

          В C++ применяется автоматический подсчёт ссылок и много чего ещё — но это всё конструкции библиотеки (стандартной или нестандартной), а не языка.

          Компилятору очень мало чего о семантике всего этого известно.

          В Rust же все ограничения заложены в язык, попадают в IR и далее в оптимизатор… где они, в настоящее время, особо не используются, так как оптимизатор заточен, в первую очередь, под C/C++.


          1. Siemargl
            18.12.2019 19:17

            С++ прекрасно проверяется линтерами, которые знают про стандартную библиотеку (она же часть Стандарта), и например простой пример — gcc проверяет строку формата printf, хотя теоретически тоже не должен ничего знать.

            Так что насчет неверного утверждения про Фортран? Пока что оно подтверждает моё


            1. khim
              18.12.2019 20:37

              Пока что оно подтверждает моё
              Не очень понимаю о чём вы там говорите.

              Так что насчет неверного утверждения про Фортран?
              А что там с фортраном не так? SciPy уже без него собиратеся? Нет? Ну значит пока — реально существующий код написанный на Fortran всё ещё быстрее реально существующего кода, написаного на C++.

              То что в Fortran «сливает» в Benchmak Game — это нормально: это достаточно ограниченный язык, некоторые алгоритмы на него ложатся плохо. Однако в своей области компетенции — он быстрейший… о чём, собственно, и речь.

              Rust претендует на то, что будет быстрее C/C++, но при этом ещё и всегда, а не только при работе с матрицами, как Fortran… поживём, увидим.


              1. Siemargl
                18.12.2019 21:33

                Язык с ручным управлением памятью быстрее
                Фортран с ручным, C++ с автоматическим. Мое утверждение тут не противоречит вашему.


                1. PsyHaSTe
                  18.12.2019 22:08

                  С++ язык с автоматическим управлением? Простите, но — нет.


                1. khim
                  18.12.2019 22:42

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


            1. 0xd34df00d
              18.12.2019 22:17

              С++ прекрасно проверяется линтерами, которые знают про стандартную библиотеку

              Может, вы знаете про какие-то секретные линтеры, но в моём опыте ни coverity, ни clang'овский анализатор, ни даже PVS (хотя он хуже всех себя проявил) не способны найти всего того, что может найти адекватная система типов.


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


              1. Siemargl
                18.12.2019 22:22

                Конечно так. Поздновато от С++ требовать идеала =)

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


                1. khim
                  18.12.2019 22:47

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

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

                  С точки зрения компилятора C++ — это язык с ручным управлением памяти. А Fortran, опять-таки, с точки зрения компилятора — язык с ручным управлением памяти (то, что вы назвали «отсутствие работы с хипом», как раз).


                  1. Siemargl
                    18.12.2019 23:04

                    Кажется я начинаю понимать… Но тут не раст и не ява, которые имеют исключительно свою точку зрения.

                    Я предпочитаю иметь свою т.зр. в своих программах:
                    1. Использую new/delete — ручное управление
                    2. Использую unique_ptr — RAII
                    3. Выбираю shared/weak_ptr — ARC
                    4. Подключаю boehm — оппа — программа с GC
                    5. Беру в расход только автоматическую и статическую память — вообще красота — никаких нежданок, никакого управления…

                    Ну да, конечно — это мое ручное управление всей подсистемой памяти, компилятор то не знает =)

                    Но в современном C++11 и новее рекомендуется таки пп2,3 — которые относятся к автоматическим


                    1. khim
                      17.12.2019 23:38

                      Ну да, конечно — это мое ручное управление всей подсистемой памяти, компилятор то не знает =)
                      Именно так. Это не смешно, а довольно грустно. Например потому, что любая манипуляция с память через указатель на char может, потенциально, изменить значение любой переменной, адрес которой куда-то, когда-то, хоть раз передавался.

                      При этом «стрелять» это может самым неожиданным способом. Сравните, например, foo и bar — и подумайте почему случае с bar компилятор смог показать всю свою изворотливость (и развёрнутый цикл и векторизация и вообще всё круто), а вот foo — осталась, фактически, неоптимизированной.

                      Но в современном C++11 и новее рекомендуется таки пп2,3 — которые относятся к автоматическим
                      Да — это облегчает работу программисту, но… увы и ах — не компилятору. Более того — эти все чудесные обёртки в системной библиотке зачастую усложняют работу компилятору… ну вот простейший (хотя и довольно-таки патологический) пример… внушает, да?

                      Именно за счёт этого языки с автоматическим управлением памяти (не путать с трассирующим GC) могут иногда выигрывать у C/C++… а иногда — и довольно-таки заметно выигрывать…


                      1. KanuTaH
                        18.12.2019 00:03

                        При этом «стрелять» это может самым неожиданным способом. Сравните, например, foo и bar — и подумайте почему случае с bar компилятор смог показать всю свою изворотливость (и развёрнутый цикл и векторизация и вообще всё круто), а вот foo — осталась, фактически, неоптимизированной.

                        Компилятору можно и подсказать. Ну да, char* в C/C++ — это «особенный» тип, ничего не попишешь. Но средствА есть.


                        1. khim
                          18.12.2019 00:23

                          СредствА-то есть, но вот только эти многоуровневые системы костылей — честно говоря начинают надоедать.

                          Так что если Rust будет показывать сравнимую производительность на идеоматичном коде без специальных забот обо всех этих тонкостях (типа «разворачивания» std::unique_ptr для возврата значения из функции и заворачивания его обратно в std::unique_ptr в месте получения) — то это будет разумный довод в пользу перехода.

                          Потому что сейчас ситация такая: на C++ можно сделать так, чтобы твоя программа была максимально быстрой… но умеют делать это один человек из ста — и то требуется много возни и чуть ли не изучение каждой функции в дизассемблере, чтобы не перепутать куда какие костылики и как вбивать… неудобно.


                          1. KanuTaH
                            18.12.2019 00:33

                            Такие «тонкости», как с unique_ptr могут и починить в будущих версиях компилятора. Меня в расте печалит главным образом его упертость в плане «компилятор лучше знает». Да, мне уже на это отвечали, дескать, «чистый код не самоцель», но… тогда получается, что шило меняется на мыло. К тому же мне надо решать задачи, а не писать обертки, и тем более не тешить свой NIH-синдром, занимаясь RIIR, а если что-то где-то надо оптимизировать в узком месте, то средствА всегда найдутся. В общем, я пока не готов.


                            1. 0xd34df00d
                              18.12.2019 00:38

                              Такие «тонкости», как с unique_ptr могут и починить в будущих версиях компилятора.

                              Я думал, что шутки про достаточно умный компилятор — прерогатива ФП-тусовки.


                              1. KanuTaH
                                18.12.2019 00:40

                                Ну а чо мы, лысые, что ли. Те же RVO и copy elision же запилили.


                                1. khim
                                  18.12.2019 00:51

                                  RVO и copy elision не зашиты в стандартах. unique_ptr, увы, зашит.

                                  А поскольку это затрагивает «священную корову» — обратную совместимость — то шансов на исправление, мягко говоря, немного.


                                  1. 0xd34df00d
                                    18.12.2019 02:48

                                    RVO уже зашито.


                                    1. Antervis
                                      18.12.2019 03:02

                                      RVO уже зашито.
                                      только потому, что эта оптимизация может влиять на наблюдаемое поведение.


                                      1. 0xd34df00d
                                        18.12.2019 03:03

                                        Возможность RVO была зашита всегда, я же говорю о том, что теперь-то оно mandatory.


                            1. khim
                              18.12.2019 00:50

                              Такие «тонкости», как с unique_ptr могут и починить в будущих версиях компилятора.
                              Нет, не могут. Это ABI.

                              Теоретически можно выпустить новую версию ABI — но шансов на это примерно нуль.

                              А в Rust — это не зафиксировано пока в ABI никак и, когда зафиксируют, могут сделать лучше.


                              1. KanuTaH
                                18.12.2019 01:06

                                Ну да, пожалуй, не сломав обратной совместимости, этого не починить.


                          1. Antervis
                            18.12.2019 02:57

                            типа «разворачивания» std::unique_ptr для возврата значения из функции и заворачивания его обратно в std::unique_ptr в месте получения
                            ABI в обе стороны «играет» — сегодня на коне раст, а завтра плюсы. Хотя казалось бы… А еще ABI не имеет значения при инлайнинге функций
                            Потому что сейчас ситация такая: на C++ можно сделать так, чтобы твоя программа была максимально быстрой… но умеют делать это один человек из ста
                            собственно точно так же и в расте и примерно любом другом ЯП.


                            1. khim
                              18.12.2019 03:41
                              +1

                              ABI в обе стороны «играет» — сегодня на коне раст, а завтра плюсы. Хотя казалось бы…
                              А что именно «казалось бы»?

                              Rust имеено потому и не имеет аналога Itanium C++ ABI, что не хочет, чтобы «замороженный» раз и навсегда ABI приводил к тому, что подобные вещи нельзя было бы исправить.

                              А вот у C++ — приоритеты другие.

                              Потому «на дальней дистанции» Rust должен бы иметь преимущество.

                              собственно точно так же и в расте и примерно любом другом ЯП.
                              Ну вот эти «игры» и помогают понять насколько нужно отказываться от «типичных идиом» ради скорости.

                              Core Guidelines прямо говорит: используйте unique_ptr<T> и создавайте эти переменные с помощью make_unique… но на самом-то деле нужно использовать new и возвращать gsl::owner, если мы хотим «эффективности как в C»!

                              Вот и интересно понять — насколько реально быстрый код на Rust отличается от «идеоматичного», «рекомендуемого»…


                              1. PsyHaSTe
                                18.12.2019 11:32

                                Ну на нашей практике максимально тупой идиоматичный код выдал практически максимальный перфоманс, судя по профайлеру и загрузке процессора. Еще в 2 раза выжали, пройдясь vtune'ом и расставив стратегические префетчи где надо, убрав работу с памятью.


                                И судя по всему, это не у нас одних.


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


                              1. Antervis
                                18.12.2019 11:50

                                А что именно «казалось бы»?
                                а вы глянули в мои примеры? Изменение кажется незначительным, но переворачивает всё с ног на голову.
                                Rust имеено потому и не имеет аналога Itanium C++ ABI, что не хочет, чтобы «замороженный» раз и навсегда ABI приводил к тому, что подобные вещи нельзя было бы исправить.

                                Потому «на дальней дистанции» Rust должен бы иметь преимущество.

                                пока rust не стабилизирует ABI, ни о какой «дистанции» речи и быть не может. А дальше будет иметь значение компромиссы какого из ABI лучше применимо к конкретным приложениям.


                                1. khim
                                  18.12.2019 15:46
                                  +4

                                  пока rust не стабилизирует ABI
                                  А почему вы считаете, что он это, вообще, должен делать?

                                  Движение вообще в другую сторону идёт: clang умеет отказываться от ABI для static функций, к пример, чтобы быстрее было.


                                  1. lieff
                                    18.12.2019 16:09

                                    Это и gcc/msvc уже очень давно делает, и не только для статик, но и для всех при lto/pgo. ABI он наружу, чтобы статические либы и dll\so не протухали каждые несколько месяцев.


                                    1. khim
                                      18.12.2019 16:18
                                      +2

                                      ABI он наружу, чтобы статические либы и dll\so не протухали каждые несколько месяцев.
                                      Тем не менее C++14 ABI и C++17 ABI несовместим, так что с некоторой частотой это всё равно происходит.

                                      Я уже не говорю про разные компиляторы.

                                      В общем в отношении Rust'а моё отношение всё ещё немного скептическое… но уже совсем немного…


                                      1. lieff
                                        18.12.2019 17:05

                                        Ну это да. И это порождает определенные проблемы, из-за этого даже вводят всякие _GLIBCXX_USE_CXX11_ABI, и оно даже реально пригождается в реальной жизни. И вот представим что на это забьют и это все станет регулярным. Раз в 3 месяца пересобираем весь мир, к примеру (а проблем с пересборкой конечно же ни у кого не возникнет). А проприетарщина вообще, кому она нужна? Давайте исходники, либы не принимаем.


                                  1. Antervis
                                    18.12.2019 17:44
                                    -1

                                    А почему вы считаете, что он это, вообще, должен делать?
                                    представьте, что вы хотите поставлять вашу библиотеку без исходников — совершенно адекватный коммерческий юзкейс. И вам наверно захочется чтобы эту библиотеку можно было использовать не только из-под ubuntu 18.1.2 и rustc 1.39.16 на компьютерах с intel i7 8-ого поколения, которым это всё барахло компилируется у вас. Точнее, компилировалось у вас вчера. А сегодня вы обновили компилятор до rustc 1.40.1 (nightly) чтобы посмотреть очередную классную фичу, а еще обновили librustrt.so, и отныне всем вашим клиентам нужно будет воспроизвести эти шаги чтобы работать с вашими новыми библиотеками.


                                    1. khim
                                      18.12.2019 19:01
                                      +2

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

                                      Попытки «заморозить» C++ ABI показали, что пользоваться этим всё равно умеют единицы, а проблемы это вызывает у всех.


                                      1. lieff
                                        18.12.2019 19:44
                                        -1

                                        То-есть пользоваться растом исключительно через другой язык? Где таки постарались и нарушения ABI редки?


                                        И чем умеют пользоваться, и какие проблемы у всех возникают? C++ ABI умеют пользоваться единицы и у всех с ним проблемы возникают?
                                        Ну так а вы предлагаете еще чаще эти проблемы сделать, а пользоваться умеют все — скомпилил и какой-то ABI получил. И его желательно не замечать, только чинить если у кого-то где-то сломается, а не ломать все время. Вот чинить да, может и единицы умеют. Посмотрите как это в firefox решается для stdc++, и это при редких изменениях, а при нестабильном — будет вообще кошмар.


                                        Ломать его, конечно, приемлимо в некоторых случаях, например если ни либы ни dll\so не нужны. Но в остальном — чем реже тем лучше.


                                        1. khim
                                          18.12.2019 22:14
                                          +2

                                          Где таки постарались и нарушения ABI редки?
                                          Проблема не в том, что «там постарались и нарушения ABI редки», а в том, что если вы делаете «широкий», «быстрый» интерфейс, то сделать его эффективно — очень тяжело. А если у вас там сплошные «хенлы» и внутренняя реализация скрыта — то ничего, кроме того, что предоставляет C — не нужно.

                                          Посмотрите как это в firefox решается для stdc++, и это при редких изменениях, а при нестабильном — будет вообще кошмар
                                          Три или четыре раза её меняли полностью несовместимым образом. Несколько раз обнаруживали несовместимость и чнили. Заметьте: libstd++ — это библиотека, разработчики которой прилагают титанические усилия для поддержания совместимости — и всё равно, как вы говорите: это проблема.

                                          Но в остальном — чем реже тем лучше.
                                          Это теория. А на практике — libicu имеет 60 несовместымых версий. Boost — сравнимое число. И это ведь — «золотой стандарт», библиотеки, которыми пользуются огромное количество народу! А в более мелких народ вообще не задумывается о том, чтобы была хоть какая-то совместимость.

                                          Если требуется совместимость — то C++ интерфейс всё равно заворачивается в C интерфейс (как в libandroidicu) и потом «с другой стороны» на него снова вешается C++ обёртка.

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


                                          1. lieff
                                            18.12.2019 22:34

                                            Все так: glibc — мало проблем, libstd++ — больше, Boost — еще больше; libicu — не сталкивался. Но это не значит что им не пользуются.
                                            У steam runtime, например, достаточно стабильное API/ABI, в пределах дистра благодаря этому куча кода шарится, даже у flatpak/snap идут ссылки на свои core для этих целей. Есть куча более стабильных библиотек что таких проблем не вызывают. И это будет еще только больше проблем если весь этот мир пересобирать раз в несколько месяцев.


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


                                            К слову про производительность. Все последние изменения в stdc++ abi они из-за фич языка, не из-за производительности. Производительность да, это хорошо, и над этим думать надо. Но это редко надо, бесконечность итераций тут не надо для одной архитектуры для стабилизации.


                                            1. khim
                                              18.12.2019 22:41
                                              +1

                                              Все последние изменения в stdc++ abi они из-за фич языка, не из-за производительности.
                                              Потому что изменения для увеличения производительности потребуют изменить ABI — а он священен.

                                              Но это редко надо
                                              Там где это не нужно — есть Java, Python… и C интерфейс.

                                              У steam runtime, например, достаточно стабильное API/ABI, в пределах дистра благодаря этому куча кода шарится, даже у flatpak/snap идут ссылки на свои core для этих целей.
                                              Если вы всё собираете одним компилятором из неизменных исходников — то не вижу проблем.


                                              1. lieff
                                                18.12.2019 22:49

                                                Проблема тут следующая: Я не собираю игру тем же компилером что steam runtime. И когда пакую ее во flatpak, я не собираю core, но ссылку на него указываю. Понятия не имею, что там за компилер использовался, но игра работает.


                                                Ну то-есть ABI нужен везде, где надо что-то с чем-то совместить, компоненты, где твое может быть только часть. Поставить third party, собрать что-то один раз и использовать либу, сделать dll\so и экономить юзеру память, плагины к какому-то софту итд. Когда все это не надо — ну да, тут эффективнее lto и статическая линковка, ктож спорит.


                                          1. Antervis
                                            19.12.2019 00:10

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


                                            1. khim
                                              19.12.2019 00:26
                                              +1

                                              Это то, что всё равно по итогу делается. Я не знаю ни одной «живой» операционки, предоставляющей из коробки C++ интерфейс хоть к чему-нибудь.

                                              Даже какой-нибудь MFC — он вместе с приложением идёт и вам нужна ровна та версия, которая будет совместима ровно с тем компилятором, которым вы это приложение собираете…


                                              1. lieff
                                                19.12.2019 01:03

                                                Как это не видели? В винде msvcp*, это хоть и redist и его обычно с приложением поставляют (хотя от старых студий уже вроде в комплекте, от 6й точно), но оно будет шариться между всеми использующими эту стабильную версию приложениями, и ему нужен ABI. Так же directx redist включают, COM интерфейс это тоже не C.


                                                Макось? Полно не сишных интерфейсов, и что печально — сишные opengl и openal депрекейтят.


                                                На лине тоже firefox использует stdc++ из системы, причем там при сборке конфигурится, какую минимально поддерживать. Есть куча других либ с не сишными интерфейсами.


                                                Но вы абсолютно правы, что си интерфейс — наиболее ABI стабильный. А с++ и другие ломаются куда чаще. Именно потому его все любят, когда нужна совместимость. Именно потому — часто ломать ABI плохо, неважно stdc++, rust или go. Это вызывает проблемы в определенных случаях и их придется решать одним из способов.


                                                1. khim
                                                  19.12.2019 01:14

                                                  Именно потому — часто ломать ABI плохо, неважно stdc++, rust или go.
                                                  И именно поэтому в ядре Linux специально ломают ABI, ага.

                                                  Понимаете — мантра «часто ломать плохо» работает, когда альтернатива не слишком сложна. Если же ABI всё равно регулярно ломается — то проще не делать вид, что его можно как-то «застабилизировать», а признать, что его нельзя использовать, если вы два компонента независимо собирать хотите.

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


                                                  1. lieff
                                                    19.12.2019 01:25

                                                    Вы опять про все пересобрать. Вы понимаете, что если вы можете все пересобрать, — оно вам не надо. А если Far собран С++, а плагин на дельфи, и его создатель не имеет контроля над сборкой фара, и всех его версий у всех, — то ABI нужно. Да, оно кода-нибудь сломается, но ABI тут всё равно нужен, так что обратная мантра тоже не работает. И чем дольше оно не ломается — тем лучше. В вашем варианте мы просто не делаем плагины фару, всегда пересобираем из какой-то репы те что у вас есть.


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


                                                    1. khim
                                                      19.12.2019 04:34

                                                      А если Far собран С++, а плагин на дельфи
                                                      … то использовать как C++-строки, так и Delphi-строки в ABI вы не можете. И вам придётся, скорее всего, использовать C-style ABI.

                                                      И чем дольше оно не ломается — тем лучше.
                                                      Конечно. POSIX-api скоро полвека стукнет — а оно всё ещё «не ломается»

                                                      Кейсов где ABI нужен полно.
                                                      А где я с этим спорил? Продуманный ABI может жить десятилетиями… А вот ABI, который получился у вас, когда вы завели три класса и десять функций, а потом всё это проэкспортировали — нет.

                                                      А если вы уже разрабатываете ABI — то его можно и в C-функциях и в GBus, какой-нибудь, превратить… а если о нём не думать — то ничего хорошего не выйдет.


                                                      1. lieff
                                                        19.12.2019 11:25

                                                        Нет, на винде ABI у дельфи для dll`ок вполне совместим. Ну то есть вы согласны что ABI иногда нужен, и что когда он долго не ломается — это хорошо?
                                                        Вы просто к тому что у других языков это не получится и надо всегда делать обертки на си? Ну хорошо, это же не противоречит тому, что ломать ABI плохо. Просто язык значит никогда не будет самостоятельным и требовать си для кейсов когда нужен ABI, именно по этой причине. И чем чаще происходит поломка ABI — тем более это верно.


                                                        Вот только не все спешат бежать делать си обертки для своих библиотек. А как миниум С++ и ObjC/Swift в реальной жизни вполне используют. Под виндой какой-то из стабильных msvcp, под линем stdc++ от какой-то версии и получают шаринг между приложениями и приемлимое время жизни приложения.


                                                        Отказ от поддержки ABI по сути будет перекладывание работы с разработчика языка на разработчиков библиотек\приложений для кейсов когда он нужен и он никогда не сможет стать именно заменой C/C++.


                                                        1. khim
                                                          19.12.2019 14:13
                                                          +1

                                                          Ну то есть вы согласны что ABI иногда нужен, и что когда он долго не ломается — это хорошо?
                                                          Конечно. Но такой у Rust уже есть.

                                                          Вы просто к тому что у других языков это не получится и надо всегда делать обертки на си?
                                                          Зачем вам «обёртки на си». В C++ есть extern "C", в Rust тоже extern есть.

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


                                                          1. lieff
                                                            19.12.2019 14:49

                                                            Ну тут я согласен. Можно тогда язык использовть через си ABI, раз своего нет. Но конкретно у раста это потребует всякие #[repr(C)] и libc::*, а не все это делают. Язык получается не самостоятельный и это все равно минус. Свой стабильный ABI — это хорошо, или сделать чтобы все нужные конструкции типа #[repr(C)] были автоматом при экспорте в либы\dll\so. Но постойте-ка, это и получился бы совой стабильный ABI.


                                                            А иначе — все таки перекладывание этой проблемы с языка на разработчика. Не рассчитана либа на си — придется решать. А если все либы рассчитаны на си (как вы предлагаетте, всем так делать) — ну так вот он, свой стабильный ABI, стандартизируем как его свой и проблема решена. А пока этого нет — будут делать и так и так и проблема останется.


                                                            1. khim
                                                              19.12.2019 15:33

                                                              А иначе — все таки перекладывание этой проблемы с языка на разработчика.
                                                              А стабильный API без разработчика никак не сделать.

                                                              Простейший пример прям из жизни: работаете вы с Xml и назвали вы детей childs (не native speaker, бывает). Потом, в следующей версии, пришёл грамматей и поправил. Стало вместо childs, как и положено childred.

                                                              И? Как вы эту проблему на уровне языка решать собрались?

                                                              А если все либы рассчитаны на си (как вы предлагаетте, всем так делать)
                                                              Нет. Большинство библиотек — вообще не требуют стабильного API. Они поставляются в исходниках и собираются так, как нужно.

                                                              А вот «большие» компоненты, поставляемые отдельно — да, здесь C ABI достаточно.

                                                              P.S. На самом деле самый разумный и, главное, практически отлично работающий подход к стабильному ABI — у разработчиков ядра Linux. Все знают, что внутри ядра — нет никакого стабильного ABI. Но не все осознают насколько, при этом, стабильно ABI на уровне системных вызовов. Windows отдыхает. Но да — это даром не даётся. Это нужно думать, планировать, моделировать, описывать. Делать это для каждой мелкой библиотеки — глупо и ненужно. Но если вы хотите поставлять библиотеку без исходников… то у вас нет выбора.


                                                              1. lieff
                                                                19.12.2019 16:02

                                                                Не путайте проблемы API и ABI, я говорил только про ABI. API — да, проблема на стороне разработчика библиотеки.


                                                            1. mayorovp
                                                              19.12.2019 16:13

                                                              #[repr(C)] — это не костыль, а механизм, позволяющий отделить ABI от деталей реализации


                                                              1. lieff
                                                                19.12.2019 16:17

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


                                                                1. mayorovp
                                                                  19.12.2019 16:26

                                                                  Подавляющее большинство либ на Rust (а на текущий момент — так вообще все) линкуются статически, на кой им стабильный ABI?


                                                                  1. lieff
                                                                    19.12.2019 16:35

                                                                    Так я про это и говорю: если не нужен ABI, не надо модулей, плагинов, шаринга кода/rdata между независимыми приложениями, удобного интерфейса к JIT и прочего — лучше линковать lto, это эффективнее и сделает каждой функции совой оптимальный ABI.


                                                                    Речь же про кейсы когда он нужен, а для системного языка — это маст хев. Для потенциального заменителя C/C++ — тоже.


                                                                    1. mayorovp
                                                                      19.12.2019 16:37

                                                                      Ну так в этих редких кейсах и надо использовать #[repr(C)].


                                                                      1. lieff
                                                                        19.12.2019 17:00

                                                                        Это не редкие кейсы. Механизм совмещения — фича очень серьезная и используется повсемесно. Пирчем разные модули разрабытывают разные люди/команды/кампании. Связать все это очень важно. Например либу делает одини люди, а используют — другие, вы гарантируете что все либы это сделают, как предлагает khim? Получается я беру либу — проблема, еще беру — еще проблема. И что мне делать? А если все реально начнут делать #[repr(C)] и прочее необходимое, и других вариантов не будет — так проблемы тогда нет, тогда это и будет стабильный ABI для раста.


                                                                        Вот вам еще пример: вот скомпилил я Qt под asan/tasn. Вот они у меня лежат и я их иногда использую. Мне вот очень не понравиться если все минорные обновления компилера начнут его ломать. Или если я не смогу слинковаться с либой из дистриба немного другим компилером, у мнея их вообще 2, clang и gcc, а в репе собиралось одним (причем точно не ими, потмоу что вот они обновились, а все остальное — нет). Понимаете масштаб проблемы?


                                                                        1. mayorovp
                                                                          19.12.2019 17:03

                                                                          Как это всё относится к Rust? В Rust вы не будете компилить какой-нибудь qt-rs отдельно от своей программы, а значит и ломаться тут нечему.


                                                                          1. lieff
                                                                            19.12.2019 17:13
                                                                            +1

                                                                            Я вам пытаюсь показать кейс, когда ABI нужен, вы пытаетесь показать кейс где не нужен. Как это к rust относится? Я же конкретно про него пишу — не все либы используют и будут использовать совместимость с си, потому проблема ABI останется. Надо или на си ABI перейти для любой внешней комоновки, или свой стабильный сделать, и это реально очень надо.
                                                                            Просто возьмите кейс где он нужен, а не статическую линковку qt-rs только к совему приложению. Например задейтесь целью зашарить его загрузку между несколькоми независимыми приложениями, контроля над которыми вы не имеете.


                                                                            1. mayorovp
                                                                              19.12.2019 18:11

                                                                              То есть у вас есть три сторонних программы, которые статически слинкованы с одной и той же библиотекой, а вы пытаетесь её оттуда выдрать и сделать динамической?


                                                                              Я правильно понимаю ваш кейс?


                                                                              1. lieff
                                                                                19.12.2019 18:45

                                                                                Не вы, у разработчиков приложений появится такая возможновть, если либа поддержит динамическую линковку. А если динамическия линковка — почти всегда нужен ABI. Вот представьте: обновился в репе компилер — и хоба, прилетает вместе все 40гиг установки, потому что все надо собирать именно с этой версией. Ставите компилер из тестинг репы потому что вам надо? Ваши проблемы. А так, ментейнер возьмет либу, возьмет приложения, скомпилит в пакадж, проверит что все работает. И весь мир в апдейтах прилетать не будет. Вы можете эти приложения локально собрать, подцепив либу из репы, компилер будет другим наверняка, но работать будет и память будет шариться. Это все благодаря ABI и возможности совмещать модули. Все это разношерстная кампания, разные люди, у них разные системы, разные версии и им вот просто очень сильно надо уметь совмещаться внешне без пересборки мира.


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


                                                                                1. khim
                                                                                  19.12.2019 19:06
                                                                                  +1

                                                                                  А так, ментейнер возьмет либу, возьмет приложения, скомпилит в пакадж, проверит что все работает.
                                                                                  Не возьмёт и не проверит. Все попытки продвижения GNU/Linux на десктоп, так или иначе, в это упираются.

                                                                                  Да, есть небольшое количество библиотек, которые «следят за гигиеной» и выпускают совместимые версии: libstdc++, Qt, в KDE кой-чего, может ещё пяток можно вспомнить… остальные же на это всё равно забивают.

                                                                                  Отсюда — все эти WinSxS, докеры и прочее. Люди всё равно испльзуют программы с «проверенными» библиотеками.

                                                                                  Все это разношерстная кампания, разные люди, у них разные системы, разные версии и им вот просто очень сильно надо уметь совмещаться внешне.
                                                                                  Не нужно. Разработчики дистрибутивов уже лет двадцать пытаются продвинуть эту «нирванну»… а воз и ныне там: разработчики всё равно пакуют библиотеки с приложением. Посмотрите на какой-нибудь Acrobat Reader: он всё равно тащит с собой ICU и OpenSSL, libORBit и libbonobo, libcurl и libJP2K… потому что это — самый просто способ уменьшить количество головной боли у техподдержки.

                                                                                  Да, можно говорить, что это плохо, неправильно и так далее… а толку?


                                                                                  1. lieff
                                                                                    19.12.2019 19:40

                                                                                    Все верно, я и сам пакую и тащу все статически, для универсальной сборки, которую можно скачать, и запускать неизвестно где. Для винды тоже так делаю, даже с redist не люблю связываться. Вот для flatpak/snap можно указать core вполне, с проблемами пока не сталкивался. А вот в репе — можно и собрать, тянув все динамически. Если говорить именно про ABI — оно просто не соберется иначе, проблему сразу будет видно. И в репах таких приложений тоже хватает.


                                                                                    1. khim
                                                                                      19.12.2019 23:01

                                                                                      Если говорить именно про ABI — оно просто не соберется иначе, проблему сразу будет видно.
                                                                                      Если мы уже начинаем говорить про «оно просто не соберется» — значит речь уже не про бинарники, значит и стабильный ABI особо уже не нужен…


                                                                                      1. lieff
                                                                                        19.12.2019 23:50

                                                                                        Про бинарник, который попадет в репу и будет тащить все динамически из нее, в репах таких хватает. Просто вы в основном API проблемы описываете, а ABI проблема тут крайне редко возникает (хотя я сталкивался), обычно это бывает если в git выкладывают предкомпиленые либы, вручную собранные.


                                                                      1. Antervis
                                                                        19.12.2019 17:39

                                                                        Ну так в этих редких кейсах и надо использовать #[repr©].
                                                                        вы сами то пробовали так делать? Просто на дистанции в пару десятков лет намного проще победить пару изменений ABI, нежели тянуть сишный интерфейс.


                                                                        1. mayorovp
                                                                          19.12.2019 18:16

                                                                          #[repr(C)] — это не обязательно сишный интерфейс, это просто указание не переставлять поля в структуре местами.


                                                                        1. khim
                                                                          19.12.2019 18:27

                                                                          Неубедительно. В самом популярной на сегодня OS (а это, как бы, не Windows, а Android) все интерфейсы C-шные. Ни C++, ни COM там нету… и ничего — никто не умер.


                                                1. mayorovp
                                                  19.12.2019 08:38

                                                  Как это не видели? В винде msvcp*

                                                  msvcp — это не интерфейс ОС, а рантайм С++.


                                                  COM интерфейс это тоже не C

                                                  Но это и не С++. Кстати, кто-нибудь знает, он там патентами какими-то закрыт или как? Удобная же вещь, а за пределами продуктов Microsoft почти никем не поддерживается...


                                                  1. lieff
                                                    19.12.2019 11:30

                                                    Да я вкурсе что такое msvcp. Речь про то что он ставится в систему и будет шариться между всеми приложениями, как stdc++ под линем. Чем не интерфейс не на си? Вот WINAPI — да, там везде си, в .net — уже нет, на маке тоже в основном нет. Но это все не к тому, что си интерфейс нажеднее все равно никто не спорит. Это как раз наглядно показывает что это нужная фича, и сишные интерфейсы часто из-за этого выбирают.


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


                                                    1. mayorovp
                                                      19.12.2019 12:33

                                                      Речь про то что он ставится в систему и будет шариться между всеми приложениями, как stdc++ под линем.

                                                      В данном случае, это не достоинство msvcp, а скорее недостаток линя. Или недостаток винды, зависит от точки зрения.


                                                      Так-то и stdc++ можно в теории в нескольких копиях в систему поставить...


                                                      1. lieff
                                                        19.12.2019 13:05

                                                        Все так. На винде решают проблему, поставляя redist c приложением и держа несколько копий мажорных версий в системе. На лине — поддерживая какую-то минимальную версию stdc++.
                                                        Так же на лине теперь доступен вариант и как в винде — указывая разные версии core в flatpak/snap, будем попадать на разные версии stdc++. В системе их тоже может стать несколько, и все еще будем их шарить между приложениями.


                                                        1. mayorovp
                                                          19.12.2019 13:10

                                                          Но это обход проблемы стабильного ABI, а не её решение.


                                                          1. lieff
                                                            19.12.2019 13:29

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


                                                            1. khim
                                                              19.12.2019 14:08

                                                              Если он совсем часто ломается — это так-себе ABI, поддержка усложнится и пользваться станут реже, или станут от чего-то отказываться, это однозначная проблема для кейсов где он нужен.
                                                              В Python ABI подерживается, грубо говоря год (в каждой минорной версии он свой). Много народу уже отказались?


                                                              1. lieff
                                                                19.12.2019 14:51

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


                                                                1. khim
                                                                  19.12.2019 15:34

                                                                  Кэш он, может, и перекомпилит — но вот расширения на C — нет. А их, в общем, немало…


                                                                  1. lieff
                                                                    19.12.2019 16:05

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


                                                      1. khim
                                                        19.12.2019 14:06

                                                        Так-то и stdc++ можно в теории в нескольких копиях в систему поставить...
                                                        Более того — нужно. Если вы старые приложения хотите использовать. А если вы используете библиотеки, собранные gcc 4.x и gcc 5+… получите «массу удовольствия» даже несмотря на одну версию libstdc++…


                                              1. Antervis
                                                19.12.2019 01:04
                                                -1

                                                Это то, что всё равно по итогу делается. Я не знаю ни одной «живой» операционки, предоставляющей из коробки C++ интерфейс хоть к чему-нибудь.
                                                а как же винда, знатная часть интерфейсов которой торчит наружу в виде COM-объектов, имеющих сишный и плюсовый интерфейсы?


                                                1. khim
                                                  19.12.2019 01:12
                                                  +1

                                                  COM-объекты прекрасно обрабатываются на C, а вот как раз чтобы из C++ с ними можно было работать — там особое расширение, а не стандартный C++


                                      1. Antervis
                                        18.12.2019 21:51
                                        -1

                                        Прикрутите к ней API на C — и поставляйте.
                                        для такой цели c++ подходит лучше раста
                                        Попытки «заморозить» C++ ABI показали, что пользоваться этим всё равно умеют единицы, а проблемы это вызывает у всех.
                                        что вы понимаете под «пользоваться ABI»? И какие «проблемы» у всех это вызывает? Что вы вообще понимаете под «ABI»? Вот например вот здесь:
                                        Тем не менее C++14 ABI и C++17 ABI несовместим, так что с некоторой частотой это всё равно происходит.
                                        вам известно о каких-то breaking changes о которых не известно никому другому?


                                        1. khim
                                          18.12.2019 22:38
                                          +1

                                          вам известно о каких-то breaking changes о которых не известно никому другому?
                                          Почему «неизвестно никому другому»? Известно. Ну вот вам простейший пример:
                                          test.h:
                                          struct Foo {
                                            int value;
                                          };
                                          
                                          struct Bar {
                                            static constexpr Foo foo{42};
                                          };
                                          
                                          test1.cc:
                                          #include "test.h"
                                          
                                          constexpr Foo Bar::foo;
                                          
                                          const Foo* baz() {
                                            return &Bar::foo;
                                          }
                                          
                                          test2.cc:
                                          #include "test.h"
                                          
                                          const Foo* qux() {
                                            return &Bar::foo;
                                          }
                                          
                                          main.cc:
                                          int main() {
                                          }
                                          


                                          В C++14 всё Ok:
                                          $ g++ -c -std=c++14 test1.cc -o test1.o
                                          $ g++ -c -std=c++14 test2.cc -o test2.o
                                          $ g++ main.cc test1.o test2.o
                                          

                                          В C++17 всё Ok:
                                          $ g++ -c -std=c++17 test1.cc -o test1.o
                                          $ g++ -c -std=c++17 test2.cc -o test2.o
                                          $ g++ main.cc test1.o test2.o
                                          

                                          А смешивать — нельзя:
                                          him@khim1:/tmp/1$ g++ -c -std=c++14 test1.cc -o test1.o
                                          khim@khim1:/tmp/1$ g++ -c -std=c++17 test2.cc -o test2.o
                                          khim@khim1:/tmp/1$ g++ main.cc test1.o test2.o
                                          /usr/bin/ld: test2.o:(.rodata._ZN3Bar3fooE[_ZN3Bar3fooE]+0x0): multiple definition of `Bar::foo'; test1.o:(.rodata+0x0): first defined here
                                          collect2: error: ld returned 1 exit status
                                          


                                          Получите, распишитесь.

                                          Поэтому, в частности, у нас переход на C++17 происходил через flag day.

                                          Эта несовместимость — известна, собственно никто её и не скрывает.

                                          что вы понимаете под «пользоваться ABI»?
                                          Ну вот то, про что вы пишите: выпустить либу — а потом новую версию без пересборки бинарника, который этой либой пользуется. Хорошо работает в случае C интерфейса (как зачастую и делают, даже если «внутри» там сплошной C++), отвратительно — в случае с C++.

                                          Да, это возможно… но надёжнее — интерфейс на C.


                                          1. Antervis
                                            19.12.2019 00:39
                                            -1

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


                                            1. khim
                                              19.12.2019 01:07

                                              А для этих целей есть разнообразные fabi-version (обратите внимание на 12ю и 13е версии). Ну или всякие чудеса типа вот такого.

                                              Увы, единственный способ не поиметь проблем — это использовать C и «узкие» интерфейсы. Всё остальное — от лукавого.


                                              1. Antervis
                                                19.12.2019 01:47

                                                А для этих целей есть разнообразные fabi-version (обратите внимание на 12ю и 13е версии)
                                                «corrects», «corrects» и еще раз «corrects»…
                                                По сути, единственная причина почему сишный ABI не меняется в том, что сам язык практически не развивается. А вот в плюсах иногда ABI приходится расширять по мере изменения языка.
                                                Ну или всякие чудеса типа вот такого.
                                                с каких пор __int128 является стандартным плюсовым типом, обязанным иметь одинаковую реализацию в разных компиляторах?


                                                1. khim
                                                  19.12.2019 04:40

                                                  с каких пор __int128 является стандартным плюсовым типом, обязанным иметь одинаковую реализацию в разных компиляторах?
                                                  С тех пор, как он был добавлен в psABI?

                                                  А вот в плюсах иногда ABI приходится расширять по мере изменения языка.
                                                  А зачем? Мои можно сразу выделить «переносимое подмножество» — а остальное объявить внутренним делом реализации языка.

                                                  Вот вы тут COM вспоминали… Много там появилось за последние 10 лет? А C++ таки прилично поменялся…


                                                  1. Antervis
                                                    19.12.2019 13:08

                                                    С тех пор, как он был добавлен в psABI?
                                                    1. psABI != стандарт языка с++
                                                    2. __int128 не является обязательным к реализации согласно psABI
                                                    3. psABI почти никак не описывает эту реализацию
                                                    А зачем? Мои можно сразу выделить «переносимое подмножество» — а остальное объявить внутренним делом реализации языка.
                                                    оно и так выделено. extern «C» называется. Но еще раз: решать проблемы бинарной совместимости через си — самый болезненный из возможных вариантов.
                                                    Вот вы тут COM вспоминали… Много там появилось за последние 10 лет? А C++ таки прилично поменялся…
                                                    Ну так благодаря бинарной совместимости COM и не меняется


                                          1. 0xd34df00d
                                            19.12.2019 19:16
                                            +1

                                            Зачем такие ужасы выдумывать? Достаточно ведь вспомнить, что в C++17 noexcept — часть типа функции (и участвует в мэнглинге), а в C++14 — нет.


                                            1. khim
                                              19.12.2019 23:04

                                              Спасибо за напоминание.

                                              Ситуация с noexcept в чём-то лучше: можно создать библиотеку, которая работает и с C++14 клиентами и с C++17.

                                              А вот с этими переменными — засада полная: сделать так, чтобы модули C++14 и C++17 не конфликтовали нельзя вообще. Никак. Во-всяком случае мне решения найти не удалось…


                                            1. Antervis
                                              20.12.2019 10:58

                                              Достаточно ведь вспомнить, что в C++17 noexcept — часть типа функции (и участвует в мэнглинге), а в C++14 — нет.
                                              Я тоже так думал, но простейшая проверка опровергает это утверждение.


                        1. PsyHaSTe
                          18.12.2019 11:27

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


                          Удобно ведь? Чем раст и занимается, по сути.


                          1. KanuTaH
                            18.12.2019 11:36

                            Ну вот смысл в том, чтобы без этой подсказки код не компилировался

                            Нет уж, спасибо :) Может быть, мне действительно нужно получить алиасинг в этом месте для того, чтобы получить byte representation некоего объекта. Не надо за меня думать.


                            1. PsyHaSTe
                              18.12.2019 11:39

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


                            1. mayorovp
                              18.12.2019 11:45
                              +1

                              При обычном программировании ситуация "массив байт — это реально массив байт" встречается куда чаще, чем ситуация "массив байт — это byte representation некоего объекта".


                              1. KanuTaH
                                18.12.2019 11:50

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


                                1. mayorovp
                                  18.12.2019 12:55

                                  Любая строка (std::string) — это скрытый массив байт. Который заведомо не является byte representation некоего объекта, просто потому что строка владеет этим массивом.


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


                                  1. KanuTaH
                                    18.12.2019 13:15

                                    Я думаю, немного:

                                    godbolt.org/z/XiA6YZ

                                    :) Приватные поля классов, пусть даже и char* — это вам не аргумент функции, который может придти ХЗ откуда, в том числе из другого модуля.


                                    1. mayorovp
                                      18.12.2019 13:22

                                      У вас UB в коде: указатель никогда не инициализируется, но при этом используется. И к тому же в примере со строкой используются полностью локальные переменные.


                                      Если это исправить, то поведение string::fill окажется таким же, как и int foo(int& i, char *p).


                                      1. KanuTaH
                                        18.12.2019 13:31

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


                                        1. mayorovp
                                          18.12.2019 13:44
                                          +1

                                          А что насчёт вот такого примера? Он что, тоже искусственный? Вроде бы совершенно стандартная ситуация, когда надо посимвольно обойти переданную по ссылке строку...


                                          Обратите внимание, что компилятор даже сам указатель "c" вынужден считывать на каждом шаге цикла, потому что не уверен что тот не алиасится с владеющей им же структурой!


                                          UPD Нашёл пост на Хабре, откуда я узнал об этом эффекте: https://habr.com/ru/company/pvs-studio/blog/475636/


                                      1. KanuTaH
                                        18.12.2019 13:37

                                        Вот, кстати, пример с аргументом-ссылкой в string::fill():

                                        godbolt.org/z/7oPNmw

                                        Тоже вполне прилично разворачивается. Но да, тут &i тоже не может придти хз откуда. Но такой сценарий все-таки куда более частый, чем ваш.


                                        1. mayorovp
                                          18.12.2019 13:45
                                          +1

                                          Но такой сценарий все-таки куда более частый, чем ваш.

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


                                          1. KanuTaH
                                            18.12.2019 13:51

                                            Там смысл был в том, что оба аргумента могут придти ХЗ откуда.

                                            Карма не позволяет писать ответы слишком часто, поэтому отвечу здесь же и на второй ваш комментарий:

                                            Да, но если эту функцию РЕАЛЬНО ВЫЗВАТЬ, скажем, в main():

                                            godbolt.org/z/BtFJDr

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


                                            1. mayorovp
                                              18.12.2019 13:56

                                              Всю программу, к сожалению, в main не заинлайнить; всё равно где-то компилятору придётся проводить границы между функциями...


                                            1. PsyHaSTe
                                              18.12.2019 13:57

                                              Там смысл был в том, что оба аргумента могут придти ХЗ откуда.

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


                                              Карма не позволяет писать ответы слишком часто

                                              Подправил этот момент.


                                              1. KanuTaH
                                                18.12.2019 14:01
                                                +1

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

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

                                                Подправил этот момент.

                                                Благодарю! :)


                          1. Antervis
                            18.12.2019 11:58
                            -1

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


                            1. PsyHaSTe
                              18.12.2019 12:20

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


                              1. Antervis
                                18.12.2019 12:55
                                -2

                                да, код пет проджекта на 100 строчек кажется очень красивым. Не потому, что его не писали «умные программисты», а потому, что его не писал никто кроме вас.

                                А вот когда вы лет через 10 заглянете в rust проект, переживший пару сотен «умных программистов», десятки тысяч «срочная фича, дедлайн вчера» и ни одного рефакторинга («всё ж работает!»), но с кучей unsafe костылей (которые неизбежно будут появляться везде, где борроучеккер будет препятствовать изменениям кода), вот тогда вы будете не одну ночь дебажиться, подавляя желание переписать всё с нуля (ибо времени не выделено), и напишете в рабочий чатик «смотрите какую хрень я нашел в нашем проекте». И вам там ответят «да, это надо переписать, там где-то был тикет на это, лет 5 назад, но Вася Пупкин уволился и делать его некому».


                                1. PsyHaSTe
                                  18.12.2019 13:12

                                  Покажите проект с кучей unsafe-костылей. Или вы скажете что они все маленькие и плохие? Даже в rustc ансейфа очень мало, назвать его маленьким язык не повернется. При том что это блин компилятор, с кучей низкоуровневой магии.


                                  Если вернуться чуть ближе к миру смертных и посмотреть на типичную библиотеку вроде actix то там 54 ансейфов на 54750 строк кода. Учитывая что почти все ансейфы — однострочные, получаем сколько? 0.1% ансейф кода? Ну да, это явно можно назвать кучей ансейф костылей.


                                  Возьмем serde_json, 15к строк кода, пять ансейфов
                                  Возьмем parity-ethereum, 200к строк кода, 68 ансейфов.


                                  Продолжать?


                                  1. Antervis
                                    18.12.2019 13:20

                                    а что, популярные открытые плюсовые библиотеки плохо написаны? Типа буста? Или компиляторы?

                                    Я говорю про типичный прод типичного бизнеса, который практически не заинтересован в качестве кода. Тот самый плюсовый код, который вы критикуете, обитает преимущественно в них. И таких проектов на расте сейчас раз-два и обчелся, он попросту слишком молод.

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


                                    1. PsyHaSTe
                                      18.12.2019 13:51

                                      а что, популярные открытые плюсовые библиотеки плохо написаны? Типа буста? Или компиляторы?
                                      молод.

                                      Парити — это буст или компилятор?


                                      куча низкоуровневой магии там в миддленде и бекенде

                                      Простите, а MIRI тогда это что?
                                      Задача раста — компилировать в LLVM. С чем он и справляется, будучи написан полностью на расте.


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

                                      На них хватает проектов, просто вы о них возможно не слышали.


                                      Решил наш собственный микросервис растовый проверить, 10к строк, два ансейфа.


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


                                      1. Antervis
                                        18.12.2019 13:57

                                        Взял вот наш микросервис растовый, 10к строк, два ансейфа.

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


                                        1. PsyHaSTe
                                          18.12.2019 14:13

                                          Что-то мне этот разговор напоминает


                                          image


                                          Ну и даже если — какая разница? 99.99% людей никогда не пишут проекты на миллионы строк кода. Даже если там что-то вдруг пойдет не так (почему? Какой механизм за этим стоит), это затронет жалкую горстку людей. Остальные могут пользоваться бенефитами, которые гарантированно есть на проектах до миллион строк.


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


                                          1. KanuTaH
                                            18.12.2019 14:16

                                            Не, ну честно, одно дело — разработка кучкой энтузиастов, которые болеют за идею, другое — людьми из бодишопов, которые фигак-фигак и в продакшн. Думаете, там кто-то будет стесняться влепить десяток unsafe, если «сроки горят»?


                                            1. PsyHaSTe
                                              18.12.2019 14:28

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


                                              А во-вторых написать сейф вариант часто короче и быстрее написать чем ансейф, посмотрите тот же пример выше с get_unchecked против [], поэтому ленивый разработчик скорее словит панику чем уб.


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


                                          1. Antervis
                                            18.12.2019 16:00
                                            -1

                                            Что-то мне этот разговор напоминает
                                            одумайтесь. Вы сравниваете заведомо плохой код на плюсах с заведомо хорошим кодом на расте и приводите это в аргумент того, какие плюсы плохие. До этого вы сравнивали раст с плюсами приводя в пример вообще сишный код.
                                            Остальные могут пользоваться бенефитами, которые гарантированно есть на проектах до миллион строк.
                                            да вы бы хоть такой пример привели. Просто любой большой проект, который не энтузиасты в свободное время делают, и не разработчики языка, а обычные программисты из тех самых 99.9%, которые перекладывают json'ы, закрывают тикеты и у которых нет времени рефакторить код на каждую хотелку.


                                  1. Siemargl
                                    18.12.2019 14:36

                                    Зачем далеко ходить? Можно взять сабж Бенчмаркинг гейма и посчитать % ансейфов.

                                    Хотя впрочем я не считаю, что без ансейфа было бы существенно медленнее. Разве что громкого заголовка могло не получиться )

                                    Да и вообще, Расту надо доказывать не производительность кода (тут сомнений мало), а производительность написания.


                                    1. PsyHaSTe
                                      18.12.2019 14:41

                                      Зависит от целей. Написать быстрый код на расте проще, чем на жс или на java. То есть если буквально есть цель написать сервис, который держит миллион рпц и не выжирает 300% памяти сервера, то на расте получится написать проще и быстрее.


                                      А если вам не нужен быстрый код то и раст не нужен.


                                1. mayorovp
                                  18.12.2019 13:28
                                  +1

                                  Для того, чтобы подобного ужаса не случилось, в Rust достаточно одного правила: в основном проекте не должно быть никакого unsafe! Если требуется что-то хитрое — пиши абстрактную библиотеку.


                                  В С++ аналогичный набор правил намного сложнее.


                      1. Siemargl
                        18.12.2019 08:33

                        ну вот простейший (хотя и довольно-таки патологический) пример… внушает, да
                        нет. в случае с make_unique дополнительно выполняется явная инициализация (0 для инт, вызвался бы конструктор для пользовательского типа)


                    1. Antervis
                      17.12.2019 23:59

                      Я предпочитаю иметь свою т.зр. в своих программах:
                      1. Использую new/delete — ручное управление
                      2. Использую unique_ptr — RAII
                      3. Выбираю shared/weak_ptr — ARC
                      то, что язык поддерживает RAII а библиотеки с его помощью автоматизируют ручное освобождение памяти (и иных ресурсов, btw), никак не отменяет того факта, что управление памятью в с++ ручное.
                      4. Подключаю boehm — оппа — программа с GC
                      зачем?


                      1. Siemargl
                        18.12.2019 12:00
                        -1

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

                        зачем?
                        пример просто для полноты картины


                      1. potan
                        18.12.2019 18:53

                        RAII поддерживается и в Rust (при этом более гибко, чем в C++, позволяя, если необходимо, освободить ресурсы досрочно). То есть в нем ручное управление памятью?


                        1. Siemargl
                          18.12.2019 20:36
                          -1

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


                          1. potan
                            19.12.2019 00:48

                            Заимствование и владение в Rust большенство проблем решает.


                            1. Siemargl
                              19.12.2019 01:07
                              -1

                              Ахах, так это и есть эта проблема… Носиться = заимствовать [под роспись и по очереди]


                              1. mayorovp
                                19.12.2019 08:45

                                Кроме непонятных эмоциональных аналогий какие-то аргументы будут?


                                1. Siemargl
                                  19.12.2019 09:03
                                  -1

                                  Никаких эмоций — ОВ это ручное управление циклом жизни переменной под дулом компилятора. Весьма неудобно


                                  1. mayorovp
                                    19.12.2019 09:18

                                    под дулом компилятора

                                    Снова аргументация от эмоций.


                                  1. potan
                                    19.12.2019 14:15
                                    +1

                                    GC удобнее, но не везде применим.
                                    Под дулом компилятора такими вещами заниматься лучще, чем без него. Вооюще пора понять, что в современных развитых языках компилятор друг, а не враг программиста.


                              1. potan
                                19.12.2019 14:12

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


    1. potan
      18.12.2019 18:40
      -1

      Язык с ручным распределением регистров быстрее, чем язык с автоматическим при прочих равных. Ваш К.О.


  1. lieff
    17.12.2019 21:47
    +4

    Посмотрел первый тест reverse-complement. Прямо с самого начала идут различия.
    В Расте:


    stdin.lock().read_to_end(&mut data).unwrap();

    В С++:


    // read line-by-line, according to game rule. Should be replaced by fread()
    while (fgets_unlocked(cur_pos, remainbytes, stdin) != 0)

    Разве это можно напрямую сравнивать на большом количестве строк? Дальше там еще больше различий идет. Если omp c thread::spawn еще можно сравнить, то в треде там совсем по разному обработка идет.


    1. KanuTaH
      17.12.2019 21:55
      +2

      Растик по ходу сжульничал. В описании задачи особо упомянуто, что «Each program should: read line-by-line a redirected FASTA format file from stdin (grow the data, buffered-read by buffered-read; don't get the size and make a single allocation.) [...]». Интересно, много ли там такого.


      1. defuz Автор
        17.12.2019 22:51
        +1

        Обе реализации используют одинаковое количество памяти – 1MB.

        Вы же не думаете, что когда вы читаете в С++ «до конца строки», стандартная библиотека не буферизирует чтение?

        Более того, С++ версия вычисляет общий размер ввода, что формально нарушает правила.

        По поводу этих условий есть определенная дискуссия. Предлагается по меньшей мере переформулировать их для ясности.


        1. KanuTaH
          17.12.2019 22:57
          +2

          Я ничего не думаю, я просто вижу, что реализация, в которой все читается одним куском, «немножко» нарушает правила. В реализации на C++ буфер тоже выделяется один раз, это так, но там хотя бы чтение в него происходит line by line в соответствии с правилами.

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


          1. defuz Автор
            18.12.2019 00:21
            +2

            Конечно модерация есть, и решения которые читят по-черному не принимаются.

            Из внутренней дискуссии, конкретному по тому вопросу, который вы подняли:

            The description has said «read line-by-line» since Dec 2004, in-spite of the fact that even back then we understood that what seemed to be «read line-by-line» in-say CPython defaults to a buffered read.

            I think «read line-by-line» actually meant don't write a program that will fail when the workload increases 40x.

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


            1. technic93
              18.12.2019 01:07

              Создатель этой игры не смог сформулировать что значит line by line строго поэтому забанил половину решений как ему захотелось. И это только один момент почему этот бенчмарк ниочем.


              1. defuz Автор
                18.12.2019 01:20
                +1

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


                1. ZoNT
                  18.12.2019 12:36
                  +2

                  Написал я под этот бенч решение на nodejs, которое превосходило в два раза текущее. Его выпилил за «нарушение постановки задачи», хотя на любых тестовых данных оно выдавало правильные результаты.
                  Данный бенч (или его владелец) очень непоследователен в правилах: половина решений в некоторых тестах использует ассемблерные вставки. Что в итоге мы этим показываем: как крут ассемблер или как крут тестируемый язык?


          1. anton19286
            18.12.2019 09:22

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


    1. ilynxy
      17.12.2019 22:18
      +8

      Этак если посмотреть на реализацую бенчмарков-победителей на разных языках, то можно увидеть, что там соревнуются в основном «кто лучше распараллелил да применил нужные интринсики SSE/AVX/...» (писать на фортране можно на любом языке). В pidigits все используют gmp для арифметики, в reverse-complement rust использует unsafe memchr из libc и т.д. и т.п. Всё это далеко от идиоматического C++ или Rust. Поэтому конкретно эти бенчмарки сравнивают непонятно что с непонятно чем.

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

      В исходниках, кстати, есть идиоматические реализации алгоритмов без хитрых трюков, но так не победить =)


      1. lieff
        17.12.2019 22:38

        Ну интринзики можно было бы и в C++ и в Раст добавить, было бы понятно как модифицировать другой пример чтобы сравнять, сравнивались бы уже сами компиляторы. Хотя clang это и C++ и Раст и так =)
        Про преимущества оптимизации у мемори-сейф, ну так в С++ тоже есть всякие restrict и расширения, которые теоретически эту разницу нивелируют.
        Тут же Раст прямо нарушает условия, так что С++ даже нельзя в ответ модифицировать.


        1. KanuTaH
          17.12.2019 22:47
          -1

          Формально именно в стандартном C++ restrict нет (по крайней мере пока), это из C99. Но фактически AFAIK тот же g++ в него умеет:

          godbolt.org/z/TGuECo

          В случае с restrict он вычисляет результат в compile time.


          1. lieff
            17.12.2019 23:23

            Ну это да, потому и пишу расширений. У gcc есть еще __attribute__ с кучей всего полезного не стандартного. У msvc есть __assume() итд.


        1. Siemargl
          18.12.2019 08:53

          Ну интринзики можно было бы и в C++ и в Раст добавить, было бы понятно как модифицировать другой пример чтобы сравнять, сравнивались бы уже сами компиляторы. Хотя clang это и C++ и Раст и так =)
          См. исходники — интринсики используются и в Расте, кстати почему то их вызов unsafe.
          Еще раз — gcc это не clang — а в этом тесте C/C++ использует gcc


          1. lieff
            18.12.2019 12:55

            Да, я видел. Я пишу немного на расте и я к тому что если ипользоваь clang — у меня всегда выходило примерно "сравнять" оптимизации. Разница была лиш в том что раст вставляет проверки, а компилер по сути один и тот-же.
            Это правда могло вызывать дополнительный спиллинг регистров, что на асме может сильно сказаться. Так же на расте иногда сложно сравнять даже с интринзиками, в специфических случаях, например из-за alloca https://github.com/rust-lang/rfcs/issues/618 https://github.com/rust-lang/rust/issues/48055, асм вставка тут не поможет т.к. надо со стеком корректно играться (ну или памятью пожертвовать, если там не гигабайты в худшем случае). Когда Unsized Rvalues допилят — одна из пробелм уйдет. А вот в обратную сторону — на С/С++ можно сделать все что позволяет AST clang`а.


      1. defuz Автор
        17.12.2019 23:00
        +2

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

        Как правило, 99% производительности зависит от 0.1% кода. Считайте что бенчмарки – ответ на вопрос, на чем и как могут быть написаны эти 0.1%.

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


        1. ilynxy
          18.12.2019 00:14
          +1

          Ну то есть пишем либу на Си и вызываем её функции из <язык по вкусу>. Тем самым демонстрируя прямоту рук.
          Обычно у языка есть декларируемая сфера применения и практическая (историческая). Rust, если я правильно понимаю, предназначен для тех же целей, что и C++. Не вижу никаких причин быть медленней любого другого языка, кроме молодости. Опять же, компилируемый язык, позволящий использовать особенности целевой архитектуры (лучше неявно) будет претендовать на звание быстрейшего. Однако скорость это не главное, хотя и важное. Главное чтобы эта скорость была доступна искаропки средней руки программисту. Этак можно скомпилировать программу на C компилятором C++ (ониж совместимы) и заявить, что C++ рвёт Rust.
          А в соревновании кто напишет код ближе к Си победителей не будет.


          1. defuz Автор
            18.12.2019 00:33
            +1

            Ну то есть пишем либу на Си и вызываем её функции из <язык по вкусу>. Тем самым демонстрируя прямоту рук.
            Внезапно, Rust зависит от libc, а половина стандартной библиотеки Python – обертки на сишными либами. Так что в реальном мире все именно так и работает, только вот скорость C теряется на переходах FFI и из-за всяких GIL.

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

            Не вижу никаких причин быть медленней любого другого языка, кроме молодости.
            И тем не менее, Ada, Fortran и Pascal почему-то заметно медленнее C/C++/Rust.


            1. Gorthauer87
              18.12.2019 01:45
              +2

              Скорее всего там просто не нашлось таких же гуру по микрооптимизациям. Хотя возможно эти языки просто меньше инструментов дают для любителей подшаманить с тактами.


        1. Antervis
          18.12.2019 12:31
          +1

          Почему же непонятно? Эти бенчмарки сравнивают сильно заоптимизированный код на различных языках программирования
          и какой смысл сравнивать, скажем, написанный на интринсиках код на си и написанный на интринсиках код на с++, если их отличия будут минимальны, но при этом ни один ни второй код не будет отражать 99.9% кейсов реального программирования на языке? А если я просто напишу asm-вставку, это будет считаться?


          1. ZoNT
            18.12.2019 12:38

            Сейчас — считается


    1. defuz Автор
      17.12.2019 22:55

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


      1. lieff
        17.12.2019 23:35
        +1

        Ну это понятно, я уже выше писал что принципиально разные omp и thread::spawn — это можно сравнивать, будем сравнивать эффективность crt. Но там где сравнивается участки с принципиально большим количеством действий (fgets + strlen) и когда их можно привести к одному виду (там даже есть комментарий про fread) — так сравнивать нельзя. Как я понимаю в С++ используется не эффективный подход из-за правил, а в Раст коде правило нарушено.


      1. Dim0v
        17.12.2019 23:52
        +2

        Сравниваются наиболее эффективные подходы

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


        Для сравнения языков выбирается наиболее эффективная реализация для каждого языка.

        Как мы только что выяснили, не наиболее эффективная, а произвольная.


        Если вы посмотрите на различные реализации в одних и тех же языках, то увидите, что бывают очень разные подходы, особенно в вопросах распараллеливания.

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


        Хоть убейте, не пойму, как из таких бенчмарков с "наиболее эффективными подходами" можно делать какие-то выводы о скорости языков. Не меряют они скорость языка. Они меряют непонятно что.


        1. defuz Автор
          18.12.2019 00:40
          -2

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

          Как я уже писал выше – не столь важно, как именно вы прочитаете 1MB данных из stdin на фоне 2 секунд вычислений. Тем более, что мы даже не можем дать гарантий, что компилятор не с оптимизирует «построчное» чтение. Тем более, что stdin все равно буферизируется ОС.

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


          1. technic93
            18.12.2019 01:21
            -1

            Ну и что показывает факт что си быстрее си++ если си код скорее всего можно скопировать g++? Признайтесь, заголовок желтый, написали бы "раст показывает производительность на урвоне си и си++". В результате на чем писать simd инструкции не важно, лишь бы язык поддерживал в любом случае приходим к ручной возне с указателями и unsafe. А если сравнивать с наворотами которые помогают, то в расте пока не хватает например ручного unroll поскольку нету const generics.


            1. defuz Автор
              18.12.2019 01:36
              +1

              Признайтесь, заголовок желтый
              Не признаю, потому что я вполне сознательно прямо в заголовке указал «согласно результатам Benchmarks Game». Так что читатель вправе доверять утверждению в заголовке ровно настолько, насколько доверяет бенчмарку, и это нормально.
              В результате на чем писать simd инструкции не важно, лишь бы язык поддерживал.
              Если бы это было так, Rust и C показывали очень близкие результаты, а для некоторых задач это совсем не так. SIMD можно использовать и в Python, но вряд ли вам удасться добиться результатов близких к С/Rust и вряд ли вам захочется переписывать каждый цикл так, чтобы SIMD использовался.
              в любом случае приходим к ручной возне с указателями и unsafe
              Вы не правы. Только для 3 задач из 10 самые быстрые реализации на Rust используют unsafe.


              1. technic93
                18.12.2019 01:45

                Ну да как раз скрывать unsafe абстракции за safe интерфейсом и есть киллер фича раста, так что в других 7 там уровнем ниже :)


                А по поводу заголовока вы укавите, т.к. Benchmark Game нигде не утверждает что язык а превосходит язык б по производительности. О чем у вас написано в тексте. Но это обычное дело для заголовков ;)


          1. Antervis
            18.12.2019 12:36
            -1

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


            1. defuz Автор
              18.12.2019 13:52
              +1

              Потому что правила нарушает не только одна реализация, а почти все, в том числе на C/C++. И потому что так утверждает мейнтейнер проекта — ему виднее как исправлять ситуацию.


              1. Antervis
                18.12.2019 15:06
                -1

                Потому что правила нарушает не только одна реализация, а почти все, в том числе на C/C++
                если верить коду, это неправда


                1. Dim0v
                  18.12.2019 15:15
                  +3

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


                  1. defuz Автор
                    18.12.2019 19:07

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


          1. Dim0v
            18.12.2019 14:55
            +2

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

            И снова нет. "Используя весь арсенал доступных оптимизаций" программы на C++ были бы копипастом программ на C и работали бы со скоростью программ на C. А в случае, если бы какой-то язык обогнал си (неважно, Rust это будет или, скажем, Python) — все решения на языках, поддерживающих asm вставки, превратились бы в одну большую asm вставку с дизассемблированным кодом этого более быстрого решения.


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


      1. ZoNT
        18.12.2019 12:49
        -2

        Не вводите в заблуждение. Сравниваются именно идентичные реализации, о чём указано в правилах: например нужно ОБЯЗАТЕЛЬНО использовать хэш-мэп (https://benchmarksgame-team.pages.debian.net/benchmarksgame/description/knucleotide.html#knucleotide), хотя он реально ухудшает производительность


        1. defuz Автор
          18.12.2019 13:55
          +1

          Ну да, а еще обязательно использовать input. Потому что иначе самой быстрой реализацией будет вывод статического output.

          Наличие ограничений на реализацию != идентичные реализации, очевидно.


  1. Dogrtt
    17.12.2019 21:49

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


    1. PsyHaSTe
      17.12.2019 23:37
      +5

      <--> вы находитесь здесь <-->


      1. Dogrtt
        18.12.2019 00:00

        Да, охотно допускаю.


  1. Stas911
    17.12.2019 22:13
    +1

    Вариант для голосования «нет, but who cares» еще бы


  1. rboots
    17.12.2019 22:27

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


    1. defuz Автор
      17.12.2019 23:05
      +1

      1. PsyHaSTe
        17.12.2019 23:41

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


        1. defuz Автор
          17.12.2019 23:54

          По вашему хардкорному примеру трудно понять, какого поведения вы хотите добиться. Можете объяснить словами?


          1. PsyHaSTe
            18.12.2019 00:22
            +1

            Как известно, для массивов реализация IntoIterator бланкет-реализация обычного Iterator, из-за чего iter аналогичен into_iter и производит итерацию только по ссылкам.


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


            Иными словами, есть такой код:


            let x = vec![F(1),F(2),F(3)];
            let squardVec: Vec<i32> = x.into_iter().map(|x| F(x*x)).collect();

            где реализацию F можно увидеть по ссылке. Задача — сделать так, чтобы оно работало для массива let x = [F(1),F(2),F(3)];. Вторую строчку трогать нельзя.


            1. defuz Автор
              18.12.2019 00:54
              +2

              Уже можно, но еще не так красиво, как хотелось бы:

              #![feature(array_value_iter)]
              use std::array::IntoIter;
              
              struct F(u32);
              
              fn main() {
                  let x = [F(1), F(2), F(3)];
                  let y: Vec<F> = IntoIter::new(x).map(|x| F(x.0 * x.0)).collect();
              }

              Ждем #65819.


              1. PsyHaSTe
                18.12.2019 11:26

                Ну если вы посмотрите PR то в реализации там написано практически то же что и по моей ссылке.


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


      1. rboots
        18.12.2019 00:27

        А если мне нужно пройтись по массиву элементов и сделать некоторые операции каждого с каждым? Создавать 1e6 сплитов?


        1. PsyHaSTe
          18.12.2019 00:31

          А если мне нужно пройтись по массиву элементов и сделать некоторые операции каждого с каждым?

          Напишете for:


          fn main() {
              let mut arr = [1,2,3];
              println!("{:?}", arr);
          
              for i in arr.iter_mut() {
                  *i += 10;
              }
          
              println!("{:?}", arr);
          }


          1. Antervis
            18.12.2019 12:42

            а если мне нужен фурье, для которого в первом проходе нужен i-й элемент с reverse-bit-indexed-i-м?


            1. PsyHaSTe
              18.12.2019 12:50
              +1

              тогда делаете for i in 0..arr.len() и дальше как в старом добром си по индексам пишете что хотите.


              1. Antervis
                18.12.2019 13:12
                -1

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


                1. Siemargl
                  18.12.2019 13:26
                  +2

                  Всего лишь

                  pub fn foo(v: &mut Vec<f32>) {
                  Главное не перепутать =)


                1. PsyHaSTe
                  18.12.2019 13:27

                  Не успел написать) Но да, не путайте изменяемую ссылку на неизменяемый вектор, и ссылку на изменяемый вектор:


                  https://godbolt.org/z/cY3ubc


                  1. Antervis
                    18.12.2019 13:36

                    1. PsyHaSTe
                      18.12.2019 13:45

                      Ну во-первых так уже лучше, а во-вторых почему сравниваем неэквивалентный код?


                      1. lieff
                        18.12.2019 14:04

                        Забавно что в расте он развернул цикл на 2, но только в unsafe случае. Интересно, с чем это связано?


                        1. PsyHaSTe
                          18.12.2019 14:19

                          Скорее всего увидел, что итерации независимы. Раньше была зависимость в виде паники ведь. А теперь итерации можно делать полностью свободно.


                          1. lieff
                            18.12.2019 14:57
                            +2

                            Я нашел, вот так код 1 в 1 получается: https://godbolt.org/z/vitnPr


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


                      1. Antervis
                        18.12.2019 14:08
                        -1

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


                        1. PsyHaSTe
                          18.12.2019 14:10
                          +1

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

                          Ну потому что в плюсах у вас используется get_unchecked, а в расте — get_checked.


                          Неэквивалентность в том, что оператор [] делает разные вещи в разных языках, и это надо учитвать.


                          Точно так же, как Foo<T> в С++ и в расте означает разные вещи.


                          Эквивалент для сишного [] это растовый get_unchecked/get_unchecked_mut, поэтому если и писать "эквивалент", то надо их использовать, а не то, что "выглядит похоже".


                          1. Antervis
                            18.12.2019 14:59

                            По сути, неэквивалентность в вашем понимании состоит только в наличии заведомо ненужной проверки — i и (v.len() — 1 — i) всегда будут попадать в диапазон 0..v.len(), по которому идет итерация.

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

                            Эквивалент для сишного [] это растовый get_unchecked/get_unchecked_mut, поэтому если и писать «эквивалент», то надо их использовать, а не то, что «выглядит похоже».
                            у вас постоянно скачет мнение — то вы доказываете что эквивалентный код на расте безопасен, то утверждаете что безопасный код на расте неэквивалентен. Определитесь уже.


                            1. PsyHaSTe
                              18.12.2019 15:54
                              +2

                              По сути, неэквивалентность в вашем понимании состоит только в наличии заведомо ненужной проверки — i и (v.len() — 1 — i) всегда будут попадать в диапазон 0..v.len(), по которому идет итерация.

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


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


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

                              Растовый оптимизатор точно так же оптимизирует форыч и фор от 0 до len. Если вы заметили, я их оставил как есть, и компилятор вывел что там проверять ничего не нужно. Для len() - i - 1 он такого вывести не смог, ну что ж, когда-нибудь ему этому научат. Но это мало чего меняет: если мы сравниваем операции без проверки, пишем в обоих случаях без проверки. Сравниваем с проверкой — пишем так в обоих случаях. Писать с проверкой в расте а потом говорить что он генерирует плохой код — ну извините.


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

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


                              Разница в поведении в таком варианта действительно незаметна (в оригинальном сравнении). Но если мы воспользуемся какими-нибудь мутационными тестами, которые — 1 на + 1 заменять например то разница не заставит себя ждать.


                              1. Antervis
                                18.12.2019 16:18
                                -2

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

                                Но если мы воспользуемся какими-нибудь мутационными тестами, которые — 1 на + 1 заменять например то разница не заставит себя ждать.
                                тогда мы будем сравнивать не код A с кодом B, а код X с кодом Y. По сути, этот ваш аргумент — чистейшая контрпродуктивная демагогия.


                                1. PsyHaSTe
                                  18.12.2019 16:49
                                  +1

                                  Я не понимаю о чем спор.


                                  • Можно ли написать на расте один в один как на си? Ну да, обмазываемся ансейфом и имеем то же самое с той же производительностью. Получится чуть длиннее из-за ансейфов и того, что функции намеренно длинными названы.
                                  • Можно ли написать на расте так же коротко как на си? Ну да, можно, но тогда компилятор будет расставлять проверки что никто не стреляет по ногам.
                                  • Умеет ли компилятор раста элидить все проверки которые мы как люди видим не нужны? Ну, видимо не умеет, надо ждать пока научится. А пока не умеет — либо писать руками, либо забить на наличие этой проверки.

                                  В чем утверждение-то? Что код без проверок получается быстрее? Ну получается, что в расте, что в си. Так что, что доказывали — непонятно.


                                  Сколько кода я должен дописать, чтобы получить рабочий и безопасный эквивалент без ущерба в производительности?

                                  Вот столько:


                                  pub fn foo(v: &mut [f32]) {
                                   for i in 0..v.len() {
                                    v[i] = v[i] * unsafe {v.get_unchecked(v.len() - 1 - i)};
                                   }
                                  }


                                  1. Antervis
                                    18.12.2019 17:42

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


                                    1. PsyHaSTe
                                      18.12.2019 17:52

                                      вы готовы признать что безопасность и производительность раста часто противоречат друг другу? То есть то, что вы только что написали, но в однозначной формулировке.

                                      Иногда — противоречит, конечно. Иногда — наоборот, помогает. Какая пропорция помощи и противоречия не готов сказать.


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


                                      Мне лично это принципиально — это утверждение стоило мне всей кармы

                                      Лично моя позиция по этому вопросу такая


                                      Я вообще в карму никогда не минусую, если и несогласен, ограничиваюсь комментарием.


                                      а аналог вот этого кода?

                                      В сейф расте такой код написать невозможно.


                      1. Siemargl
                        18.12.2019 14:28
                        -1

                        Ну во-первых так уже лучше, а во-вторых почему сравниваем неэквивалентный код?
                        Прекрасный пример, который даёт однозначный ответ на вопрос голосовалки =)


    1. nexmean
      18.12.2019 00:04

      Ждём Verona от Microsoft.


      1. PsyHaSTe
        18.12.2019 00:24

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


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


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


      1. Antervis
        18.12.2019 12:56
        -1

        скорее всего верона выльется в очередной EEE — они сделают компилятор чего-то похожего на раст (с проприетарными расширениями и закрытым исходным кодом) и встроят в MSVC. Ничем хорошим для сообщества это не обернется


        1. VEG
          18.12.2019 19:22
          +1

          Как вам там, в 2001? У нас в 2019 Microsoft все свои новые инструменты для разработки открывает. У вас там же недавно .NET/C# вышел, и оно только под Windows? Тут .NET открыт под самой свободной лицензией и поддерживает Linux, MacOS, Android и iOS. Да, нам тут тоже сразу не верилось.


          1. Antervis
            17.12.2019 23:51

            Тут .NET открыт под самой свободной лицензией и поддерживает Linux, MacOS, Android и iOS. Да, нам тут тоже сразу не верилось.
            это сколько лет спустя? И на чем основана ваша уверенность что завтра не появится какой-нибудь Rust/CLI?


            1. VEG
              18.12.2019 00:40

              Как только компания изменила свою стратегию, так сразу. В 2014 открыли исходники нового компилера, через год-два открыли среду исполнения, потом и остальне компоненты, которые не легаси и они до сих пор поддерживают — тоже открыли.

              И на чем основана ваша уверенность что завтра не появится какой-нибудь Rust/CLI?
              Почему я должен переживать, что появится Rust/CLI?


  1. PsyHaSTe
    17.12.2019 23:39
    +3

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


    Я думаю одно только исправление бага LLVM связанным с NOALIAS поднимет раст на первую строчку. Что до реальных приложений — то там есть еще и ограничение по времени. Написать быстрый корректный код на расте имхо займет куда меньше времени чем на си.


    1. KanuTaH
      17.12.2019 23:44

      Про noalias не знаю, зависит от задачи (в C, как выше справедливо написали, тоже есть restrict), а вот что касается реальных приложений, то тут немаловажную роль ещё играет возможность пользоваться готовыми библиотеками, написать к которым раст-обёртку может оказаться нетривиальной задачей из-за их «неидиоматичности» с точки зрения раста. Не говоря уж о том, что из-за чрезмерных требований раста к идиоматичности подхода даже с растокодом как таковым вполне может получиться бида. Так вижу (С) :)


      1. PsyHaSTe
        18.12.2019 00:27
        +1

        Про noalias не знаю, зависит от задачи (в C, как выше справедливо написали, тоже есть restrict)

        Не встречал си разработчиков которые в программе расставляют restrict у каждого указателя, которых могут быть миллионы. Я понимаю правило про 80/20, но всё же.


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

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


        Не говоря уж о том, что из-за чрезмерных требований раста к идиоматичности подхода даже с растокодом как таковым вполне может получиться бида. Так вижу (С) :)

        На самом деле это больше желание коммьюнити иметь чистый код, но никаких обязательств тут нет. Судя по тому что я видел, довольно часто люди применяют подход "генерирует раст код через c2rust (или похожую тулзу), а потом постепенно рефакторим и переписываем". Подход, успешно применяющийся на TS/JS проектах.


        Так вижу (С) :)

        Благодарю. С вами приятно вести диалоги.


        1. 0xd34df00d
          18.12.2019 01:04
          +2

          Не встречал си разработчиков которые в программе расставляют restrict у каждого указателя, которых могут быть миллионы. Я понимаю правило про 80/20, но всё же.

          И слава Ритчи, потому что компилятор это не проверяет, и по мере взросления программы код начнёт врать.


    1. Siemargl
      18.12.2019 00:11

      В Си нет никаких накладных расходов на абстракции, т.к. нет абстракций. С этим невозможно конкурировать.

      Впрочем, на практике 30% потерь это ни о чем, и даже до 100% еще терпимо, так что спор скорее теологический.


      1. PsyHaSTe
        18.12.2019 00:28
        +5

        В Си нет никаких накладных расходов на абстракции, т.к. нет абстракций. С этим невозможно конкурировать.

        Во-первых в си есть абстракции.
        Во-вторых у си есть семантика которую компилятор вынужден соблюдать даже когда выгоднее было поступить иначе. Gcc выполняет поистине титаническую работу чтобы заставить код работать быстро на железе, семантика которого сильно отличается от PDP-11.


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


        1. technic93
          18.12.2019 01:37

          Да поэтому вопрос в том как можно сгенерировать оптимальный асм код за оптимальное время разработки и поддержки. Так то в бенчмарке можно вообще влепить асм вставку а потом говорить что язык самый быстрый :)


        1. Siemargl
          18.12.2019 08:42

          Gcc выполняет поистине титаническую работу чтобы заставить код работать быстро на железе, семантика которого сильно отличается от PDP-11
          Хотелось бы увидеть примеры. Насколько я знаю asm pdp-11, от x86 принципиально не отличается.
          Абстракции часто помогают ускорить программу, а не замедлить. Плюсовики со стажем думаю могут рассказать, как магия на шаблонах оказывается в рантайме быстрее голого си.
          Абстракция абстракции рознь. Шаблоны — zero-cost абстракция, чистый inline, а вот лямбды — нет.

          Не уходим с темы С — какие абстракции в С есть?


          1. mayorovp
            18.12.2019 10:36
            +2

            А с фига ли лямбды — не zero-cost? Или вы путаете лямбды и std::function?


            1. Siemargl
              18.12.2019 11:10

              Лямбда это создание и вызов ф-ции класса. Проверил — в С++ это оптимизируется в пыль, значит тут zero-cost.

              Но вот в C# это уже не так. Значит зависит от языка и надо смотреть каждый случай.


          1. PsyHaSTe
            18.12.2019 11:40
            +1

            Хотелось бы увидеть примеры. Насколько я знаю asm pdp-11, от x86 принципиально не отличается.

            Ну три самых очевидных:


            1. с точки зрения сишного кода регистров 8, на практике — намного больше
            2. с точки зрения сишного кода память линейная, на практике — иерархическая
            3. с точки зрения сишного кода исполнение инструкций последовательное, на практике это не так.

            Раст тут ничем не поможет, в этом плане он копирует семантику сишки, но в некоторых случаях — нет:


            1. сложение знаковых числел не является UB
            2. уникальные ссылки не алиасятся
            3. итераторы позволяют безопасно и бесплатно делать преобразования коллекций
            4. ...

            Не уходим с темы С — какие абстракции в С есть?

            Отвечу цитатой из статьи которая как мне кажется более чем подходит в данной ситуации:


            You may have heard another slogan when talking about C: “C is portable assembler.” If you think about this slogan for a minute, you’ll also find that if it’s true, C cannot be how the computer works: there are many kinds of different computers, with different architectures. If C is like a version of assembly language that works on multiple computers with multiple architectures, then it cannot function exactly how each of those computers work simultaneously. It must hide details, or else it wouldn’t be portable!

            That said, I think this fact is sort of irrelevant, because I don’t think people mean the phrase “C is how the computer works” literally. Before we can talk about that, let’s talk about the C abstract machine, and why many people don’t seem to understand this aspect of the C language.

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


            Но вот в C# это уже не так. Значит зависит от языка и надо смотреть каждый случай.

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


            1. Siemargl
              18.12.2019 11:51
              -2

              это такой философски-софистический прием?
              вместо примера _конкретной_ языковой абстракции, сказать «да весь мир — абстракция» =)

              спс. поговорили

              комментировать предыдущий бред про регистры и внезапно возникшую не-неймановскую архитектуру в компьютерах даже не буду


          1. khim
            18.12.2019 13:33
            +2

            Хотелось бы увидеть примеры. Насколько я знаю asm pdp-11, от x86 принципиально не отличается.
            А причём тут ASM? Отличие в архитектре процессора, а не в ASM.

            Вернее на RISC — и в ARM, тоже, но на x86 — отличие скрыто… что не значит, что его нет.

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

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

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

            Именно так, кстати, работали ранние компиляторы для 8086 (какой-нибудь славный Turbo C 2.01 или Microsoft C 5.0).

            Однако на современных процессорах это даст отвратительную производительность — потому да, разработчики из кожи вон лезут, чтобы автоматически перенести данные из памяти в регистры (чего изначальный C не предполагал). А пометку register вообще вроде в C++17 отменили (вроде — потому что компиляторы всё равно её ещё поддерживают… и игнорируют).


            1. Siemargl
              18.12.2019 13:54
              -2

              И какой отсюда следует вывод?

              Сейчас разработчики процессоров совместно с разработчиками компиляторов добиваются производительности собранных программ. Но как это должно влиять на язык и вообще должно ли — вот вопрос?

              Соотвественно весь язык построен, снизу доверху, на идее, что все данные лежат в памяти
              ИМХО язык привязан к логической архитектуре, для квантовой нужен новый. Но пока что у нас фон-неймановская и она внезапно с однородной памятью и последовательным выполнением команд.

              С трактовкой register не согласен. Это скорее был хинт компилятору, т.к. оптимизаторов тогда еще не было.


              1. PsyHaSTe
                18.12.2019 14:03
                +2

                ИМХО язык привязан к логической архитектуре, для квантовой нужен новый.

                Хватит пожалуйста лепить слово "квантовый" на всё без разбору. "не как в си" и "квантовый" это не одно и то же.


                Если "как в си" для вас кажется эквивалентным "единственно логичное" то вас пора немного оглянуться и посмотреть на мир вокруг.


                Но пока что у нас фон-неймановская и она внезапно с однородной памятью и последовательным выполнением команд.

                Что с последовательностью команд и фон-неймановскостью какого-нибудь SQL? Да, язык для запросов делался, но как пример пойдет.


              1. defuz Автор
                18.12.2019 14:08
                +3

                пока что у нас фон-неймановская и она внезапно с однородной памятью и последовательным выполнением команд

                Лол. Привезите мне сычуанский соус из прошлого в котором вы оказались пожалуйста.


              1. khim
                18.12.2019 18:00

                Но пока что у нас фон-неймановская и она внезапно с однородной памятью и последовательным выполнением команд.
                Выключите кеши и после этого будете рассказывать сказки про «фон-неймановскую» архитектуру.

                Фон-Неймановская архитектера у нас была полвека назад. Какой-нибудь C64 — можно считать фон-неймановским с той или иной степенью натяжки.

                И вот уже 386й с кешом — это уже не фоннеймановская архитектура, двуядерный проц — тем более.

                И да, 90% ресурсов в IT уходят на то, чтобы поддерживать иллюзию фон-неймановской архитектуры с однородной память и последовательным выполнением команд. Хотя ни того, ни другого и в помине нет.

                С трактовкой register не согласен. Это скорее был хинт компилятору, т.к. оптимизаторов тогда еще не было.
                Это он в XXI веке хинтом стал. В оригинальной версии это не было хинтом. Это было требование.


                1. Siemargl
                  18.12.2019 18:32

                  Наличие кэша не влияет на тип архитектуры, см определение. Да, мы живем в несовершенной иллюзии, но каждое ядро x86/amd64- отдельный экземпляр той же архитектуры.

                  Аналогично, про register советую сначала почитать в оригинале стандарт С89

                  3.5.1 Storage-class specifiers....A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined.[49]
                  Какие то утверждения у вас… безосновательные.

                  Просто я Си учил как раз по оригиналу K&R


                  1. khim
                    18.12.2019 20:56
                    +1

                    Наличие кэша не влияет на тип архитектуры, см определение.
                    А вы сами-то на него пробовали смотреть?

                    Вот на этот вот пассаж, к примеру:
                    Команды и данные хранятся в одной и той же памяти и внешне в памяти неразличимы. Распознать их можно только по способу использования; то есть одно и то же значение в ячейке памяти может использоваться и как данные, и как команда, и как адрес в зависимости лишь от способа обращения к нему. Это позволяет производить над командами те же операции, что и над числами, и, соответственно, открывает ряд возможностей. Так, циклически изменяя адресную часть команды, можно обеспечить обращение к последовательным элементам массива данных.


                    Да, далее там написано «такой приём носит название модификации команд и с позиций современного программирования не приветствуется». Но проблема не в том, что «этот приём не фенфуен», проблема в том, что у нас архитектура не Фон-Неймановкая и для пересылки данных между кешом данных и команд требуется использования явных инструкций синхронизации.

                    Аналогично, про register советую сначала почитать в оригинале стандарт С89
                    А C89 — в каком году вышел? В названии год вам о чём-нибудь говорит? А ничего, что Wikipedia пишет про C «появился в 1972»?

                    Изначально register было требованием, но когда Unix (или может он тогда был ещё Unics) начали портировать на разные архитектуры — это требование отменили, так как нельзя было заранее сказать сколько где регистровых переменных доступно (на MS-DOS их обычно было всего лишь две).

                    Просто я Си учил как раз по оригиналу K&R
                    Это прекрасно, но если вы про знаменитую книжку — то это уже 1978й год. То есть уже после того, как было решено «отвязать» Unix (и, соответсвенно C) от архитектуры PDP (Interdata 8/32 была закуплена AT&T как раз с этой целью в 1977м году).

                    Если вы всё ешё судите о прошлом по принципу «ну я же там был, всё же помню, зачем мне книжки» — то советую от него отказаться. Ибо, увы и ах, человеческая память — это аналоговая DRAM. Считываение воспоминания уничтожает его, а на это место записывается «воспоминание о воспоминании». Через 3-5-7 лет то, что вы помните о событии и то, что там происходило в реальности — начинают отличаться довольно-таки изрядно. А о вещах полувековой давности если вы чего и вспомните — то это будет больше похоже на легенду, чем на быль… даже если «вы лично там были и всё видели».


                    1. Siemargl
                      18.12.2019 21:12

                      Пробовал. Читать надо абзац «Принципы фон Неймана»

                      Ссылка из K&R про регистровые. Что было до K&R не знаю, кажется BCPL

                      Воды много, а вот пруфов мало.


                      1. khim
                        18.12.2019 23:12

                        Пробовал. Читать надо абзац «Принципы фон Неймана»
                        Ну да, «тут читаем, тут не читаем, тут рыбу заворачивали». Натягивание совы на глобус продолжается.

                        Что было до K&R не знаю, кажется BCPL
                        До K&R были Комментарии Лайонса к 6-й версии UNIX, с исходным кодом. Включающая в себя, помимо всего прочего, и описание и исходники C-компилятора.

                        И вот в этом самом конкретном исходном коде есть конкретная такая функция decl1 в конкретном файле c03.c которая конкретно так включает вот такой вот конкретный участок:
                        	} else if (dsym->hclass==REG) {
                        		if ((type&TYPE)>CHAR && (type&XTYPE)==0
                        		 || (type&XTYPE)>PTR || regvar<3)
                        			error("Bad register %o", type);
                        		dsym->hoffset = --regvar;
                        	}
                        
                        Который конкретно так ругается на конкретную такую попытку завесьти четвёртую регистровую переменную в одной функции (regvar инициализируется в файле c02.c в функции с названием cfunc и его начальное значание равно 5).

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

                        Или опять будете петь, что то, на чём писали Unix его разработчики — это не C, а так, поделка, раз оно священной водой комитета по стандартизации не окроплено?


                        1. Siemargl
                          17.12.2019 23:37

                          Принципы фон Неймана это как раз ключевые признаки архитектуры, остальное менее важно.

                          Сеанс некромантии это конечно интересно, хотя я уже и сказал что не в курсе тех времен в С. Завтра посмотрю, а пока просто замечу, что в PDP-11 было 6 общих регистров, а не 3 как вы тут утверждаете.


                          1. khim
                            17.12.2019 23:43

                            а пока просто замечу, что в PDP-11 было 6 общих регистров, а не 3 как вы тут утверждаете.
                            Где я это утверждаю, извините?

                            Именно потому что в PDP-11 было 6 общих регистров regvar и устанавливается изначально в 5. Потому что R6 и R7 трогать было нельзя. А вот чтобы регистровые переменные не заняли все регистры и осталось что-то для работы с обычными переменными — их количество и ограничивается тремя… жёстко ограничивается заметьте, никаких хинтов тут нету… это уже когда Unix начали портировать с PDP-11 оно хинтом стало…

                            Сеанс некромантии это конечно интересно
                            Мы сейчас спутились во времена когда я ещё не родился, на самом деле. Ну люблю я это дело просто. Когда разбираешься в разных вещах в IT, то заглянуть в историю (настоящую, а не то, что в ваших воспоминаниях осталось) часто бывает очень полезно.

                            Многие вещи, которые вызывают недоумение — становятся вдруг резко понятными, почти очевидными. Потому что в те далёкие времена, когда они были сделаны — они действительно имели смысл!

                            А сегодня, очень часто — они просто мешают…


                            1. Siemargl
                              17.12.2019 23:48

                              Ну ОК, мое утверждение про register верно только для версий С, начиная с K&R 1978г (а до этого неверно). Так годится?


                              1. khim
                                17.12.2019 23:54

                                Так, я думаю, годится. Как только ушли от архитектуры PDP-11 — так концепция начала рассыпаться. И чем дальше уходили — тем больше требовалось «подпорок»…


      1. evocatus
        18.12.2019 02:49

        Я тоже долго хейтил С++ и топил за Си. В итоге согласен с тем, что сейчас писать на Си практически нет смысла, особенно если нужен быстрый код — у С++ реально получается быстрее (и даже меньше по размеру). У него только один минус — это дьявольски раздутый и сложный язык. Но возможно оно того стоит.
        Моё мнение полностью опрокинул вот этот доклад:
        youtu.be/PDSvjwJ2M80


        1. whitemonkey
          18.12.2019 08:51
          +3

          Посмотрел. основа выступления:
          Он быстрее так как:
          — Он быстрее.
          — Это реально круто
          — Я так вижу
          — Реально прикольно
          — Бла-бла (Это цитата)
          — Он быстрее

          Ну ок. придётся поверить. Убер доводы представлены.


    1. Siemargl
      18.12.2019 08:44
      -1

      Написать быстрый корректный код на расте имхо займет куда меньше времени чем на си.
      Меня удивило, что конкретно в Benchmarks Game исходники на Расте не короче, чем на С++. Потому я сильно сомневаюсь в этом утверждении.


      1. PsyHaSTe
        18.12.2019 11:32
        +2

        При чем тут размер исходника? Если взять мою статью про сравнение го с хаскеллем то однопоточный вариант в 100 строк на го занял 15 минут, а хаскельный на 20 строк — больше получаса.


        1. Siemargl
          18.12.2019 11:42
          -1

          на расте сложнее писать, чем на с++. да и в принципе он весьма, хм, минималистичен — потому я ожидал увидеть код покороче

          именно так же, как с го и хаскелем — проще язык — быстрее писать


          1. PsyHaSTe
            18.12.2019 11:43
            +2

            на расте сложнее писать, чем на с++

            А вот с этим я категорически не согласен.


    1. Gorthauer87
      18.12.2019 13:43

      Вроде же можно в Найтли флагом включить альясинг и таки проверить насколько он круто перфоманс поднимает.


  1. xFFFF
    18.12.2019 08:11

    Было бы интересно сравнить максимально оптимизированный код для всех языков.


    1. khim
      18.12.2019 13:35

      Он будет одинаков. И будет состоять из одной (но длинной) строчки asm.

      Предельный случай тут как раз неинтересен — на практике так никто писать не будет…


  1. SirEdvin
    18.12.2019 09:21
    +2

    И да, и нет.

    На самом деле, просто нет. Особенно на Benchmarks Game


  1. antoshkka
    18.12.2019 12:24
    +2

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

    Вот например, угадайте язык программирования:

                        _mm_sub_pd(
                            _mm_mul_pd(distance, _mm_set1_pd(1.5)),
                            _mm_mul_pd(
                                _mm_mul_pd(_mm_mul_pd(_mm_set1_pd(0.5), dsquared), distance),
                                _mm_mul_pd(distance, distance),
                            ),
                        )
    

    Правильный ответ: почти все бенчмарки «n-body» на этом сайте имеют этот кусок кода.

    К несчастью в бенчмарках все языки на горячем пути как правило только вызывают C функции (см _mm_* и unsafe). Поэтому бенчмарки сравнивают как оптимизаторы разных версий GCC и Clang умеют оптимизировать C код, вместо того чтобы действительно сравнивать языки.

    Ну и ничего удивительного, что bleeding edge версия clang-9 (2019), побеждает GCC-9 (2018) на числодробильных задачах.


  1. domix32
    18.12.2019 13:08
    +1

    Тут уже целые диссертации пишут по теме производительности (тыц). unsafe rust уже чуток быстрее Си


  1. orcy
    18.12.2019 12:54
    -1

    Насколько я понимаю у Rust есть в перспективе преимущество с алиайсингом. Сейчас Rust может выигрывать в задачах потому как к этому языку есть интерес скиловых разработчиков в плане свободного времени. Однако на мой взгляд в плане разработки Rust имеет ряд тенденций создание менее производительного кода чем скиловый C++ разработчик. Вот что мне не хватало:


    • отсутствие placement new
    • отсутствие указателей на методы
    • отсутствие возможности использовать константы в растовых шаблонах (generics)
    • interior mutability чтоб написать safe код — это дополнительная runtime проверка
    • борьба с borrow checker решается при помощи clone() или штук вроде Rc
    • встроенная проверка выхода за границы массива

    Вообще borrow checker с одной стороны крутая штука, с другой стороны она довольно слабая в плане того что очень много вещей ей не поддаются на этапе компиляции и чтобы писать идиоматический rust приходится использовать штуки которые имеют runtime-cost либо делать их unsafe (а это вроде как считается стрёмным). Если использовать unsafe то вроде принципиально Rust и C++ должны быть теоретически одинаковыми. Не знаю как на это влияет тот факт что нарушение aliasing является UB в расте. С одной стороны знания об aliasing позволяет компилятору лучше оптимизировать, с другой стороны наличие мутабельных ссылок на один и тот же объект может давать преимущества в производительности?


    Не знаю что за задачки у benchmark game, если это числодробительные алгоритмы, то это не очень показательно. Допустим у вас проект 1 миллион строк кода. Мое мнение что производительность Rust будет "распухать" там, поскольку вы вряд ли будете сильно оптимизировать как число-дробилки, так что код будет содержать дополнительные runtime-косты равномерно размазанные по проекту. С другой стороны у вас таком большом проекте появится большая уверенность в memory safety, частично за счет compile-time, частично за счет рантайм.


    1. mayorovp
      18.12.2019 13:00

      отсутствие placement new

      Вроде же ещё в прошлый раз разобрались, что это не placement new отсутствует, а возможность статического выделения памяти для не-Sized объектов.


      отсутствие указателей на методы

      А куда они делись?


      борьба с borrow checker решается при помощи clone() или штук вроде Rc

      С borrow checker не надо "бороться", им надо пользоваться. И при чём тут вообще clone()? С чем вы боретесь в плюсах когда передаёте структуру по значению?