Pull to refresh

Comments 216

За статью, безусловно, спасибо. Однако, подача материала, а главное, цель статьи меня смущают.

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

Сила Rust не только и не столько в итераторах, ссылках и семантике перемещения, сколько в иной философии и подходу к проектированию программ. Но из статьи этого понять не выйдет.

У меня по крайней мере, сложилось бы впечатление, вроде: «Мда… ну и зачем весь этот бред? Учитесь писать на плюсах, а не выдумывайте очередного его “убийцу”».
А можно как проходящему мимо человеку запросить более обстоятельный комментарий (а возможно даже и статью) про разницу в философии проектирования программ?
Rust — уникальный проект. И как язык и как экосистема.

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

Философия языка — это сплав современных представлений computer science и хиропрактик системного программирования, повернутых, однако, лицом к программисту-прикладнику.

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

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

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

Rust пытается сделать то же самое на уровне системного программирования. Есть фундаментальная разница между утверждениями «корректность системы проверена тестами» и «корректность системы доказана математически». В первом случае, даже после десяти лет эксплуатации, мы все равно не можем быть уверены, что система надежна.

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

Все вместе позволяет без опаски реализовывать (а главное рефакторить!), сложные многопоточные программы. Здесь очень кстати будет классический уже пост Fearless Concurrency от одного из авторов языка, плюс великолепная серия статей Lin Clark про внутреннее устройство Firefox Quantum, который стал возможен исключительно благодаря возможностям Rust.

Как экосистема Rust уникален тем, что ядру команды удалось сформировать удивительное сообщество, готовое к диалогу и дружелюбное к новичкам. Буквально каждый момент взаимодействия с этим сообществом вызывает чувство глубокой благодарности. Тем удивительнее это видеть в среде, близкой к системному программированию.
Вот бы все на Хабре излагали свою точку зрения как вы! Спасибо!
Спасибо на добром слове :)
Ключевая, на мой взгляд, идея, идущая красной нитью через весь язык, — программист, как и любой другой человек не всесилен и склонен совершать ошибки. Соответственно, язык, который сваливает вину за промахи на программиста — плохой язык

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

Интересная философия, ничего не скажешь. То есть прилетят инопланетяне (придут разработчики языка) и решат все наши проблемы (в том числе клинические ошибки проектирования и тупую безалаберность). А мы будем творить, мы выше этого...! Класс!

Почему «растаманы» так уверены что этот язык — серебряная пуля от дурошлепства разработчика?

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

Чем больше ошибок учитывает язык, тем он лучше, безусловно.

Интересная философия, ничего не скажешь. То есть прилетят инопланетяне (придут разработчики языка) и решат все наши проблемы (в том числе клинические ошибки проектирования и тупую безалаберность). А мы будем творить, мы выше этого...! Класс!

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

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

Programming Defeatism: No technique will remove all bugs, so let's go with what worked in the 70s. (с)
Без аналогичного кода на С++, с которым проводилось сравнение, довольно бессмысленная статья. Предполагаю, что раз есть такая большая разница, программы написаны коряво. Оба языка компилируемые, и оптимизатор С++ уж точно не хуже Rust. Кстати, с какими флагами компилировался код на С++?
На всякий случай повторю, что цель статьи не в том, чтобы сказать, что Rust быстрее C++, а сказать, что Rust не медленней C++.

Код для C++ не представлен, чтобы не раздувать размер статьи. Код доступен на github, там же вы можете оценить корявость кода и флаги компиляции. Если у вас будут объективные замечания к коду — я обязательно их учту. Флаги:
g++ -std=c++11 -O2 -o main_cpp main.cpp
rustc -O --crate-name main_rust main.rs

Rust компилирует в LLVM код, как и clang, поэтому оптимизации, заложенные в LLVM, должны работать и там, и тут.

У Rust система типов дает оптимизатору дополнительные гарантии по владению памяти, что потенциально может привести к более оптимизированному коду.
Кстати, ради интереса, можно было бы к компиляции плюсов через gcc 7.2 добавить и clang тот же, что использовался для раста.
А замена О2 на О3 может привести к более оптимизированному коду С++.

Прежде чем хвататься за оптимизации, для начала посмотрите код теста на С++.
Да и сам автор писал, что замеры не достаточно объективны.
Плюс, насколько я понимаю, время считается и с учетом ввода/вывода данных.


P.s. я в Rust вообще не разбираюсь, но мне кажется, что тут что-то не чисто)
И первое, что я бы сделал, так это бы выбросил ввод/вывод.

А что может быть не чисто? Любой язык должен быть медленнее сей или плюсов? И кто-то сказал, что код на расте идеален? Смысл в этом посте не в том, что показать, что что-то быстрее или медленнее, а в том, чтобы показать, что они равноценны по производительности хотя бы. Так что смысла в вашем комментарии вижу мало, разве что про убирание вывода соглашусь, было бы неплохо.
UFO just landed and posted this here
Спорить глупо, да, потому что реальных и максимально честных замеров в этой статье не приводится, а приводится грубая оценка, чтобы понимать, на каком они уровне и можно ли говорить, что они равнозначны в производительности вообще, а не так, что Java и C++.

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

Интуитивно — оптимизированные до упора программы должны дать примерно одинаковое быстродействие


Спасибо за эту мысль. Я ее не смог высказать в статье.

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

Мы ведь хотим писать быстро, и чтоб работало быстро, а не зависать над ассемблером и профилировщиком :)

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

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

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

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


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


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

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


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

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

Всё не совсем так, а вернее совсем не так.

Все эти рассуждения сводятся к одному — мы сравниваем несравнимые вещи. Мы сравниваем кейсы с указателями, которые в расте точно так же требуют указателей, именно поэтому в stdlib через строчку unsafe.

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

Опять же, не так. Никакой компилятор ничего не контролирует. Схема очень простая — мы просто запрещаем использовать всё опасное, а вернее 95% языка.

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

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

В конечном итоге раст ничем не отличается от того же stl, где никакие указатели не нужны. Единственная разница в том, что в C++ писать unsafe не надо, а в расте надо. Но написать ты его можешь где угодно из его наличия ровным счётом ничего не следует.

Есть какие-то рассуждение на тему того, что «а unsafe видно лучше», но кем лучше и почему — не сообщается. Чем его лучше видно, нежели new — неясно. Все рассуждения про «new может быть где-то спрятан» — правильно, так же как и unsafe.

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

Все рассуждения о том, что unsafe что-то там — не работают. Такие же рассуждения были о том, что «мы заставляем всех проверять ошибки», но почему-то добавили unwrap() и теперь весь код — это unwrap() через unwrap() и где всё эти рассуждения? А нигде. Никому они не интересны. Остались на уровне гайдлайнов, как и в С++.

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

Ничего из этого намеренно сделано не было, никакой явности нет. Никто и никогда не расскажет о том, чем код в unsafe более явный, нежели С++. Как максимум всякие субъективные рассуждения на тему «мне так нравится», но каждый своё болото хвалит и никаких объективных предпосылок к этому нет.

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

Чего длиннее и почему — никто не расскажет и не покажет.

Почему — инфраструктура написания кастомных контейнеров на С++ всё ещё в зачаточном состоянии.

В зачаточном состоянии она на расте — без unsafe ничего не напишешь.

Да, можно накастылить семантику указателя через ссылку + option, как это сделал автор, да и кто угодно сделает. Только есть нюансы — указатель хранит состояние бесплатно, а option платно. Привет оверхед по памяти 50-100% на ноду. Естественно, что в хелвордах это мало кому интересно, но это интересно за рамками оных.

Особенно в области написания типов итераторов.

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

Вы интересный. Вам всё непонятно и неизвестно.


Для начала — что может Rust unsafe:


  1. Разыменовывать указатели (не ссылки а именно указатели)
  2. Вызывать unsafe функции, в т.ч. FFI
  3. Реализовывать для типов unsafe трейты
  4. Менять статические переменные

Нас будет интересовать в основном №1 и немножко №2, из-за операций над указателями и transmute.


Всё не совсем так, а вернее совсем не так.

А я могу сказать, что примерно так и есть. В С++ вы не можете статически гарантировать, что указатель не указывает "в молоко". Вы не можете гарантировать, что ещё жива стуктура, на которую он указывает. Имеющиеся там ссылки из-за неперемещаемости почти неюзабельны как поля структур. Так что в С++ указатели — повсеместное средство.


Все эти рассуждения сводятся к одному — мы сравниваем несравнимые вещи. Мы сравниваем кейсы с указателями, которые в расте точно так же требуют указателей, именно поэтому в stdlib через строчку unsafe.

Угу. Только потом эти указатели не будут торчать наружу. В этом отличие и есть.
По количеству — https://github.com/rust-lang/rust/search?utf8=%E2%9C%93&q=unsafe&type=
1068 вхождений просто слова. На весь проект. Натыкался где-то, что реальных использований около 200-250, но ссылку к сожалению привести не могу.


В конечном итоге раст ничем не отличается от того же stl, где никакие указатели не нужны. Единственная разница в том, что в C++ писать unsafe не надо, а в расте надо.

Отличается, причём сильно. Почитайте, какие доп. гарантии дают ссылки Rust по сравнению с его же указателями.


Есть какие-то рассуждение на тему того, что «а unsafe видно лучше», но кем лучше и почему — не сообщается. Чем его лучше видно, нежели new — неясно. Все рассуждения про «new может быть где-то спрятан» — правильно, так же как и unsafe.

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


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

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


Все рассуждения о том, что unsafe что-то там — не работают. Такие же рассуждения были о том, что «мы заставляем всех проверять ошибки», но почему-то добавили unwrap() и теперь весь код — это unwrap() через unwrap() и где всё эти рассуждения? А нигде. Никому они не интересны. Остались на уровне гайдлайнов, как и в С++.

Покажите мне код б-м серьёзного проекта, в котором повсюду применяется unwrap. В гайдлайнах чёрным по белому написано, что unwrap при неудаче грохнет поток. Что что частенько означает "грохнет процесс".


Ничего из этого намеренно сделано не было, никакой явности нет. Никто и никогда не расскажет о том, чем код в unsafe более явный, нежели С++. Как максимум всякие субъективные рассуждения на тему «мне так нравится», но каждый своё болото хвалит и никаких объективных предпосылок к этому нет.

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


Чего длиннее и почему — никто не расскажет и не покажет.

Ну да, для этого надо почитать код какого-нибудь контейнера на Rust и С++. Мой личный опыт — на С++, чтобы сделать всё правильно, писанины сильно больше.


В зачаточном состоянии она на расте — без unsafe ничего не напишешь.

Попробуйте написать итератор над любой нетривиальной структурй данных. С поддержкой всех нужных категорий. И сравните с как правило одним или двумя методами, которые надо реализовать для итератора на Rust. Я именно об этом.


Да, можно накастылить семантику указателя через ссылку + option, как это сделал автор, да и кто угодно сделает. Только есть нюансы — указатель хранит состояние бесплатно, а option платно. Привет оверхед по памяти 50-100% на ноду. Естественно, что в хелвордах это мало кому интересно, но это интересно за рамками оных.

Оптимизировано.
https://github.com/rust-lang/rust/blob/master/src/libcore/nonzero.rs
И да, известные мне реализации optional для С++ такую оптимизацию не поддерживают. Чем option+reference лучше голого указателя — отдельная тема.


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

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

Ну тут типичный набор вранья, игнорирования и манипуляций.

Я определил пару основных тезисов — никакой компилятор ничего не гарантирует и ни к чему, о чём говорилось — не имеет( это было проигнорировано), никакой компилятор ни по каким «рукам» не даст. Это всё обычная лапша и обёртки, которые есть везде, с одной лишь разницей — разделение языка на unsafe/safe, но из этого так же ничего не следует. И это мой второй тезис.

Использование указателя с припиской unsafe ничем не отличается от использования указателя без неё. А все рассуждения об unsafe в конечном итоге сводятся с тому «так надо», а не «компилятор бьёт по рукам». Это так же было проигнорировано.

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

Далее, на вопрос по теме unsafe ответа так же не последовало. Опять пошла песня про «лучше видно», при этом никаких оснований этому нет. Чем unsafe видно лучше, нежели new — непонятно. И никто и никогда этого не расскажет.

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

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

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

Что-бы в расте было safe — надо использовать эту обёртку. То же самое и в крестах.

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

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

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

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

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

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

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

Оптимизировано.

Прикостылен костыль, и чем больше будет «оптимизацией» — тем больше будет таких мест. Хотя спустя 5-7лет прикостылили оптимизацию для такого элементарного случая, а таких случаев тысячи и тысячи.

А далее мы захотим использовать индексы вместо указателей — привет bound checking и прощай производительность, либо прощай bound checking и привет unsafe.

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

Ну дак что же вы не пояснили? И опять же — очередные попытки врать и выдавать враньё за мои цитаты.

UFO just landed and posted this here
Только это ключевая разница: в крестах код по умолчанию unsafe, в расте — по умолчанию safe.

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

Код в расте по умолчанию такой же unsafe, а safe он будет тогда, когда глобально вырубить unsafe, но тогда это будет жаваскрипт, а не «альтерантива» крестам. Но и опять же, а если я разработчик stdlib, то у меня получается unsafe раст? В stdlib safe не нужно?

В конечном итоге всё сводится к проверке — написан ли у тебя unsafe код, а в каком виде этот unsafe код будет — в виде raw-pointer, либо unsafe raw-pointer — абсолютно неважно.

Я, если что, не апологет раста и вообще нежно люблю кресты. И хаскель ещё.

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

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

Это всё голубые мечты. Да и данный кейс не про обход в массиве.

UFO just landed and posted this here
Вы можете прогрепать весь ваш код на тему unsafe

Очередной идиотский тезис. Иди прогрепай stdlib на тему unsafe и расскажи об успехе, и да, по 100баксов вычти то же.

Кстати, замечаем как меняется риторика. Вначале у нас было «по умолчанию безопасный», теперь, оказывается, что надо грепать. А т.к. ты 100% что-то нагрепаешь, то оказывается нужно проводить ручной анализ.

Это типичный пример подмены понятий, вранья и манипуляций.

Никакой лоулевел код невозможен без unsafe. Любой биндинг — unsafe.

Что вы будете грепать в плюсах?

Берёшь какой-нибудь clang-tidy и пишешь за пол часа набор правил. Что надо искать? */&/new — искать не сложнее, чем unsafe.

Вполне выражаемые через зависимые типы.

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

Т.е. есть безопасность, а есть бесплатная безопасность. Это разные вещи.

А к чему было про bounds checking?

bounds checking не заканчивается на обходе массива. Вернее он там и не нужен. Он нужен при доступах по индексу.

Действительно, мы можем реализовать какие-то безопасные индексы, на С++ может это сделать статически( раст, конечно, ничего тут не сможет), только вот проблема с интеграцией в текущий safe-код, да и с реализацией есть проблемы.

Нам нужно дать следующий блок? Как? Привет bounds checking. Мы же не будет в массив записывать весь набор index_t и ходить по нему?

Как мы преобразуем порядок бита в битмапе к валидному индексу? Будем делать unsafe cast? Да, мы можем(предположим) сделать интерфейс safe, но реализация будет unsafe.

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

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

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

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

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

Да, всегда можно написать условно-безопасную обёртку, но её нужно написать. И её нужно и можно написать и в крестах. А можно не написать — и ничего не поменяется. Это можно сделать что на крестах, что в расте.
UFO just landed and posted this here
В коде за авторством себя и прочих обычных программистов я лажу находил сильно чаще, чем в stdlib. В рамках данной дискуссии проблемами stdlib можно пренебречь и взять её корректность за аксиому.

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

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

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

Потому что если вы прогрепали и не нашли unsafe, то он безопасный.

Каждый раз, когда мне кто-то говорит, что он не адепт — он адепт.

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

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

Любой код с * и &? Прям все ссылки и указатели запретить? Успех!

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

Ну и самое главное, я ведь не зря сказал про clang-tidy, но что-то вы это проигнорировали? Почему?

new? Ну буду писать вместо этого std::make_unique().release(). Такой код от некоторых карго-культуристов я видел в проде.
Для справки — результатом release() будет указатель, от которых мы выше отказались. Вы действительно не понимаете всей бессмысленности того, что пишите?

Не валялись по каким критериям?

По производительности и возможностей к написанию лоулевел кода.

Возьму Idris/Coq/ATS и докажу как теорему.

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

Кресты без ссылок и указателей

Никто вам о ссылка не говорил. & — это не ссылка, а взятие адреса, а * — это разименование и тип переменной. Я не запрещал умножение и and.

это немножко разные вещи

Это одно и то же.

по выразительности и интероперабельности

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

с внешними библиотеками.

Любая программа на расте, кроме хелвордов,, да и сам раст — это биндинги к С/С++. Что-то они пишут биндинги — напишите и вы. Никаких проблем нет.

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

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

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

И почему-то вы не задаётесь вопросом — какой смысл существования раста? Если нужно было бы добавить греп в С++ — можно было бы за месяц написать идеальный clang-tidy плагин, который бы находил всё это влёт.

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

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

UFO just landed and posted this here
Слив засчитан, а далее пошла ахинея.

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

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

То же самое и с libc — да, есть код и есть новомодные языки, которые претендуют на замену С/С++, но почему-то ни один из них не является самостоятельным, является нахлабучкой на С/С++ рантайм, на этих «языка» НИЧЕГО серьёзного не написано, кроме их компиляторов, хотя в случае раста и компилятора нет.

И что получается. Ничего нет, но трёп о том, что «выкидывай С++ хоть завтра» — есть. Дак выкинь, продемонстрируй как надо. Нет — никто этого не делает и не сделает.

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

До раста ГЦ был моден и быстр, да и сам раст на нём был. Не фортануло — выкинули, и вот он уже не моден и не быстр и без него быстрого языка не сделаешь. Хотя адепты раста — это хаскелисты/лисписты, которые буквально вчера любили ГЦ.

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

Так всегда было есть и будет. Заходишь в тред по С++ и критикуешь кресты — тебя минусуют и «ты ничего не понимаешь». Эта слепая вера в то, что наш объект обожания идеален, что раз везде написано «проблем нет» — значит их действительно нет.

Мне уже давно не интересно этим заниматься — я даже не знаю зачем я снова тут отвечают, ведь отвечаю я в пустоту.
Что вы будете грепать в плюсах?
В плюсах в недалеком будущем можно будет прогнать проверку статическим анализатором c++ core guidelines и посмотреть все места, где он выдает предупреждения.
UFO just landed and posted this here
Если вдруг не обеспечат (а я не знаю, ибо не особо знаком с системой типов в расте) и будет надо — выпустим новую версию core c++ guidelines, делов-то
Правила заимствования вы в core c++ guidelines не внесете в любом случае

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

Забыл ещё один пример привести, кроме unwrap — это immutable. У нас так же везде immutable, но в реальности же — везде mut через mut, при этом в очередной раз мы услышим отмазку про то, что «а вот в реальных проектах». И какой бы код ты не показывай — везде ответ один «это неправильный код», при этом мы сразу же забываем о том, что «компилятор должен бить по руками» и «гарантии».

При этом, когда мы говорим о крестах — у нас почему-то этот аргумент пропадает. И почему я не могу сказать «а вот в реальном коде new чрез new никто не использует», и это действительно так.

Мы берём ссылки и сравниваем их с указателями, будто бы в крестах ссылок нет. Мы сравниваем то, что удобно нам, игнорируя то, что неудобно.

И так везде — никто не скажете того, чем строки в расте более safe, нежели в крестах. Мы всегда съезжаем на тему «а можно их не использовать», дак ведь в расте их то же можно не использовать.

"Я бежала пять миль чтобы сказать вам как вы мне безразличны"


Я бы может ответил вам на ваши тезисы. Но судя по тому, как вы их подаёте, вы банально не владеете вопросом, уж простите за столь грубый пассаж. Чтобы ответить на всё это, мне пришлось бы цитировать вам главы из документации, где всё это объяснено. Держите ссылки:



Пара комментариев:


никакой компилятор ничего не гарантирует и ни к чему, о чём говорилось — не имеет( это было проигнорировано), никакой компилятор ни по каким «рукам» не даст.

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


Это всё обычная лапша и обёртки, которые есть везде, с одной лишь разницей — разделение языка на unsafe/safe, но из этого так же ничего не следует.

Изучите вопрос. Ссылки выше в комментарии.


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

http://en.cppreference.com/w/cpp/iterator
Просвещайтесь.


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

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


Мы берём ссылки и сравниваем их с указателями, будто бы в крестах ссылок нет. Мы сравниваем то, что удобно нам, игнорируя то, что неудобно.

http://en.cppreference.com/w/cpp/language/reference
Почитайте, чем отличается.

Как всегда, ничего нового. Ничего конкретно, одни обвинения «ты ничего не знаешь», «да иди читай и просвещайся» — зачем вы живёте? Какой смысл в вашем существовании и пребывании тут? Спастить мне ссылку? Просто так меня обвинять?

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

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

Это не более чем манипуляции. Компилятор компилирует код с unsafe? Компилирует. На это финиш.

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

Это в жаве я не могу написать неправильный код( и то я даже не уверен в этом), но в данном случае все гарантии — это гарантии программиста.

Это из разряда «ремни безопасности гарантируют нам безопасность», только вот ремни сами на тебе не застёгиваются, и эта безопасность — лишь следствие желания человека её использовать. Никто не выдаёт эту безопасность за «бесплатную»/пассивную — она не такая.

Изучите вопрос. Ссылки выше в комментарии.

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

en.cppreference.com/w/cpp/iterator
Просвещайтесь.

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

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

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

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

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

Вы говорите «раст, компилятор даст по рукам», а в следующем ответе уже говорите иное. Оказывается проверять ошибка не обязательно, иммутабельность — не обязательно, safe — не обязательно. И вся аргументация сводится к тому, что «а просто не пиши unsafe» — дак я то же самое могу делать и в С++.

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

en.cppreference.com/w/cpp/language/reference
Почитайте, чем отличается.

И опять то же самое. Зачем мне кидать ссылки? Что из них следует? Даже предположим, что отличия есть( на самом деле тут уже подлог, ведь в С++ ссылки — это конструкции языка, а в расте — просто обёртки и сравнивать их некорректно. Я могу сделать какую угодно обёртку и назвать её ссылкой), то что из этого следует?

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

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

Вот и тут то же самое. Вам указали на то, что сравнивать голые указатели в С++ и ссылки в расте — некорректно. Вы поплыли в сторону «они различаются» — различаются, дальше что?
Ведь вы никогда мне не скажете и не покажете — где я не прав, в чём я не прав, что и почему мне нужно смотреть. Вы где-то прочитали тезис, но не можете его аргументировать, и именно поэтому как только у вас возникают проблемы — вы пытаетесь снять с себя ответственность.

По существу у вас возражения есть?


Это не более чем манипуляции. Компилятор компилирует код с unsafe? Компилирует. На это финиш.

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


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

Но для начала неплохо бы владеть предметом. А для этого надо немножко почитать.


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

Эта ссылка была дана вам чтобы показать, что в С++ реализация итератора сводится далеко не к одному перегруженному оператору "++". Даже для простого InputIterator.


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

В Rust нет категорий итераторов в понимании С++.


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

Я даже не знаю, что на это ответить. "Безопасность уровня программиста" это как? Не давать программисту нажимать неправильные кнопки? Rust даёт определённые средства отлова и устранения определённых классов ошибок. И даёт средства локально отключать некоторые из этих ограничений — если того требует задача.


Даже предположим, что отличия есть( на самом деле тут уже подлог, ведь в С++ ссылки — это конструкции языка, а в расте — просто обёртки и сравнивать их некорректно. Я могу сделать какую угодно обёртку и назвать её ссылкой), то что из этого следует?

Эта фраза чётко говорит о том, что вы не понимаете, что такое ссылки в Rust. Уважьте, перейдите по ссылкам и прочтите.


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

Смысл в разнице, т.к. из неё следует тот самый вывод. А разница в том, что в С++ ссылка не может быть перемещена и переназначена — что делает её применение сильно ограниченным. Что в Rust ссылка может менять указуемый объект, может быть перемещена и может свободно храниться в поле структуры, не "пригвождая" структуру к одному месту. И что не может пережить объект, на который указывает.


Вот и тут то же самое. Вам указали на то, что сравнивать голые указатели в С++ и ссылки в расте — некорректно. Вы поплыли в сторону «они различаются» — различаются, дальше что?

Как раз корректно. Ссылки в Rust несут ту же нагрузку, что и в С++ — и в дополнение бОльшую часть нагрузки указателей.


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


Засим откланиваюсь.

По существу у вас возражения есть?

Возражения на что? У вас нет «по существу» ничего, и вам об этом уже сообщили. А возражать «по существу» на нечто — нельзя. «существу» нет.

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

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

Причины того, почему он не настучит — мне не интересны и к делу отношения не имеют, мне важен факт «не настучит», а значит вы соврали — всё просто.

Но для начала неплохо бы владеть предметом. А для этого надо немножко почитать.

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

Вот у вас есть требования какого-то «предмета», но почему и на каком основании вы его требуете — у вас ничего этого нет. Это обыкновенный слив и ничего более.

Я чего-то не понимаю? — Что? И даже если будет что-то, чего не будет, то дальше будет финиш. Ведь вам нужно будет вывести из факта непонимания чего-то то, что вы из него выводите «необходимость и невозможность о чём-то рам рассуждать».

Эта ссылка была дана вам чтобы показать, что в С++ реализация итератора сводится далеко не к одному перегруженному оператору "++". Даже для простого InputIterator.

Во-первых, об этом никто не говорил. А во-вторых — en.cppreference.com/w/cpp/concept/InputIterator По это же ссылке чётко указанно — что нужно. ++/*/== — всё, при этом всё это необходимо для итератора.

В Rust нет категорий итераторов в понимании С++.

Причём тут понимание С++? О нём никто не говорил, говорилось о том, что абсолютно неважно что и как там называется. Факт остаётся фактом — всё, что сверху базового ++ — это дополнительный функционал, который в расте так же есть.

И это функционал к С++ отношения не имеет. Хочешь быстрое +10 — никто не будет долбить ++ 10раз. А хочешь долбить 10раз — что-то кроме InputIterator тебе не нужно и С++ никак к этому не обязывает.

В конечном итоге — что мы имеем? В руста * и ++ — это одна функция, а в С++ — две. Поэтому в С++ нужно реализовать лишь ==, что в ходит в первоначально определение «одну-две функции».

Я даже не знаю, что на это ответить. «Безопасность уровня программиста» это как? Не давать программисту нажимать неправильные кнопки? Rust даёт определённые средства отлова и устранения определённых классов ошибок. И даёт средства локально отключать некоторые из этих ограничений — если того требует задача.

Раст ничего не даёт, вы никогда не покажете то, что он даёт и причина проста — это невозможно.

Отключить глобально unsafe вы не можете, а значит оно будет включено, а значит все гарантии — гарантии уровня рантайм/обёрток/etc, но никак не языка. Все те же гарантии реализуются на С++.

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

Эта фраза чётко говорит о том, что вы не понимаете, что такое ссылки в Rust. Уважьте, перейдите по ссылкам и прочтите.

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

Смысл в разнице, т.к. из неё следует тот самый вывод. А разница в том, что в С++ ссылка не может быть перемещена и переназначена — что делает её применение сильно ограниченным.

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

В расте нет ссылок — в расте есть обёртка в stdlib. Никаким образом эту обёртку нельзя сравнивать с конструкцией языка.

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


В расте нет ссылок, повторю это ещё раз. Подобное поведение реализуется и на С++, если нужно. И никаким образом дефолтные ссылки это не ограничивают.

Как раз корректно. Ссылки в Rust несут ту же нагрузку, что и в С++ — и в дополнение бОльшую часть нагрузки указателей.

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

С чего вы взяли, что ваша «ссылка», которая «не имеет ограничений» лучше, нежели ссылка, которая их имеет? Для меня — лучше та, которая имеет?

Да и рассуждения про ограничения — смысла не имеют, ведь никаких ограничений у С++ нет, весь фокус в том, что мы сравниваем несравнимое, а если мы сравним обёртку над указателем и обёртку над указателем, то получить что? А ничего не получиться. Обёртка не имеет ограничений.

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

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

По поводу ваших обвинений в отрицании — это подмена понятий. Это стандартный приём, когда мы выкатываем тезис и наделяем его нужным нам следствием. Например — «у вас ник на T — вы идиот». И далее играть в игру «но ведь он на T».

Именно поэтому в рамках дискуссии принято чётко и ясно выводить все следствия из своих требования/тезисов. Чего от вас нет. Вы кидаетесь примитивными шаблонами «не понимаешь», «а это так», при этом — это демагогия. Это не аргументы, не тезисы — это трёп.

Поясню на примере.

Что в Rust ссылка может менять указуемый объект,

Вот тут мы объявлением тезис, что ссылка чего-то там не может. Но тут есть ошибка, мы берём какое-то свойство, говорим о том, что оно есть. И далее делаем вывод о том, что «лучше», хотя из наличия какого-то свойства «лучше» не следует.

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

Что из этих требований следует? Ничего.

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

Ну и самое главное — почему мы ссылку в расте сравниваем с ссылкой в С++, а не с std::reference_wrapper? А причина проста — мы мало того, что делаем сравнения из которых ничего не следует — мы заведомо ограничиваем одну из сторон сравнивая несравнимое.

В конечном итоге всё сводится к тому, что везде всё должно быть как в расте. Почему? Просто так. Хочешь как в расте — сделай как в расте, это С++ позволяет. Но путём подмены понятий, мы выдаём за альтернативу в С++ ссылку(&), а на самом деле не должны ничего выдавать — ведь в расте этого попросту нет.

В конечном итоге «ссылка» в С++ может быть такой же, как «ссылка» в расте. С единственной разницей в том, что там она будет в stdlib, а в С++ нет( и то не факт). Но из этого ровным счётом ничего не следует — ведь никаких свидетельства за то, что надо так, а не иначе — нет.

Поэтому, стандартная реализации из С++ имеет та кое же право на существование, как и реализация раста. А если необходим функционал «как в расте» — он реализуется, да и уже есть.

Извините, а с чего вы взяли, что в Rust ссылки являются «обёртками в stdlib», а не конструкцией языка?

Ну и вы полностью проигнорировали всё, я вам повторил свои два тезиса, вдруг я первый раз сформулировал это непонятно. Но вы опять их проигнорировали. И тут уже нельзя сослаться на то, что вы сделали это не специально — нет, вы делаете это специально.

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

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

Вся эта тема — это сплошные манипуляции. В stl есть много мест для совместимости с си, которые «не безопасны», но они там взялись не из-за того, что С++ — это «100% небезопасно», а из-за того, что есть множество сишного кода, который нужно интегрировать.

То же самое происходит и с растом — раст без unsafe не позволяет интегрировать в себя сишный/крестовый код, а раст без него попросту существовать не может. llvm, libc, шланговые рантайм для исключений и прочее и прочее. Сколько биндингов к сишному коду в том же серво?

Каким образом этот коде безопасней? Никаким. В идеально мире, где у нас весь код расте/смартпоинтерах — всё хорошо, и в хелвордах на ресте — то же всё хорошо, как и в хелвордах на смартпоинтерах. Но реальность она сложнее.

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

Ну попробуй со мной похоливарить. Rust — это язык с контролируемой возможностью отключения гарантий и фич.


1)


Я вам сказал про пример оверхеда на option, вы мне показали, что спустя 5-7 лет существования языка — кастыль впилили.

Он тебя дезинформировал, увы. Кроме Option было оптимизировано ну просто много чего. http://camlorn.net/posts/April%202017/rust-struct-field-reordering.html.
Хочешь отключить, чтобы сохранить прямой порядок для взаимодействия с C — используй repr(C). Приятно иметь такую оптимизацию из коробки, не правда ли?


2)


в реальности же — везде mut через mut

Может, ты перепутал c ключевым словом unsafe? Я еще не видел людей, которые бы хаяли изменяемость данных :D


Константность — это тоже фича(снимается добавлением ключевого слова mut). Константность, которую не предоставляет C++, в котором есть псевдо, которая легко снимается const_cast, либо заметается под ковёр ключевым словом mutable либо очень "очевидным" implicit кастом const T -> T&&, который, вообще-то, мутабельный.


3)


раст без unsafe не позволяет интегрировать в себя сишный/крестовый код, а раст без него попросту существовать не может

Может =)


4)


разделение языка на unsafe/safe, но из этого так же ничего не следует.

Звучит как "The flat earth society has members all around the globe".

Может =)

А можно ссылочку на документацию? Меня интересует эта тема.

Rust'у вообще C++ не нужен. C — вот лингва франка нашей профессии, но и от него можно отказаться. Да даже от стандартной библиотеки самого Rust.


Кроме Option было оптимизировано ну просто много чего.


Ничего. Это капля в море. Да и опять же, дело не в этом — дело в том, что все это прошлогоднее, хотя о мистической оптимальности рассуждалось десять лет.

Приятно иметь такую оптимизацию из коробки, не правда ли?

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

Да и это хелворд, цена которому ноль.

Может, ты перепутал c ключевым словом unsafe? Я еще не видел людей, которые бы хаяли изменяемость данных :D

Я про то, про что я.

Константность — это тоже фича(снимается добавлением ключевого слова mut).

Это не просто фича, это хайпилось как основной локомотив «безопасности», а теперь уже «просто фича». Хотя и safe — это просто фича.

Константность, которую не предоставляет C++, в котором есть псевдо, которая легко снимается const_cast, либо заметается под ковёр ключевым словом mutable либо очень

safe, которую не предоставляет rust, в котором есть псевдо, которая легко снимается unsafe.

«очевидным» implicit кастом const T -> T&&, который, вообще-то, мутабельный.

Очевидный кому? Никакой const T в T && не превратится — в T входит const. Поэтому это будет const T &&&, для константных lvalue.

Кстати, про constexpr в расте расскажешь?

Может =)

Не может, иначе бы unsafe был выключен. Загляни в свою stdlib, либо в любую, не хелвордистый, код — ты там увидишь unsafe через unsafe.

Звучит как «The flat earth society has members all around the globe».

Опять какая-то ахинея и ноль аргументации.

В крестах так же есть разделение unsafe/safe, только оно не декларируется через unsafe. На это различия заканчиваются. А декларация unsafe как unsafe не имеет смысла, вернее ни ты, ни кто-либо ещё никогда о нём не расскажет.

Написания raw-pointer и unsafe raw-pointer ничем друг от друга не отличаются, ведь raw-pointer итак unsafe в любом его проявлении.

Подожди. Давай будем объективны.


1)


Ничего. Это капля в море.
Какой коробки и какую оптимизацию?

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


Оптимизировали представление структур в памяти. Изначально в Rust, как и в C++ был прямой порядок полей, что значит, что каждое поле находилось в памяти в том порядке, в котором было объявлено.


Пример не оптимальной структуры(C++, занимает 12 байт)


struct MyStruct
{
  uint8_t var0;
  uint32_t var1;
  uint8_t var2;
  uint8_t var3;
  uint8_t var4;
};

Но можно оптимизировать структуру, переставив 0 и 1 поле местами(C++, теперь структура занимает 8 байт):


struct MyStruct
{
  uint32_t var1;
  uint8_t var0;
  uint8_t var2;
  uint8_t var3;
  uint8_t var4;
};

Таким образом, потребление памяти данной структурой снижается на 33.3% без потери информации. Впечатляющий результат! Но Rust коду не требуется ручного вмешательства, компилятор это делает сам(Rust, выведет sizeof MyStruct 8):


struct MyStruct
{
  var0: u8,
  var1: u32,
  var2: u8,
  var3: u8,
  var4: u8,
}

fn main() {
  println!("sizeof MyStruct {}", std::mem::size_of::<MyStruct>());
}

Эту оптимизацию и встроили в коробку, т.е. в компилятор Rust. Он это делает за тебя. C++ так не может и не сможет, потому что считается, что каждая структура в C++ обязана маппиться на C. Это древнее наследие, которое никак не сломать.


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


При этом программисту оставили возможность представлять структуру в памяти так, как в C, чтобы можно было взаимодействовать с существующими библиотеками, написанными на C. Для этого компилятору Rust нужно явно указать repr(C) перед объявлением структуры, подробнее читайте в документации.


2)


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

Мой развернутый ответ в 1 пункте покрывает это утверждение с лихвой.


3)


Константность — это не просто фича, это хайпилось как основной локомотив «безопасности», а теперь уже «просто фича».

Звучит неубедительно, предоставь пруфы.


4)


Что надо искать? */&/new — искать не сложнее, чем unsafe.
Хотя и safe — это просто фича.
safe, которую не предоставляет rust, в котором есть псевдо, которая легко снимается unsafe

Ты думаешь, что внутри unsafe происходит что-то ну совсем страшное. На самом деле, там пишут то, что пишут в обычном С++. Весь код вокруг unsafe проверяет компилятор, а внутри unsafe проверяет сам программист. Если ты накосячил внутри unsafe — это твои проблемы. Тебя предупреждали. Программисты C++ любят повторять: "Писать безопасный код на С++ можно, для этого надо придерживаться определенных правил", ну так вот, этих правил и стоит придерживаться внутри unsafe. И всего лишь. Для желающих узнать больше.


5)


«очевидным» implicit кастом const T -> T&&, который, вообще-то, мутабельный.

Прости, был не прав. C++ предпочитает мутабельное инстанцирование иммутабельному. Спонсор западни — Константин Крамлин из Яндекса. Как много опытных плюсоидов завалилось на этом примере, просто жуть.


6)


Кстати, про constexpr в расте расскажешь?
Да, конечно.

7)


То же самое происходит и с растом — раст без unsafe не позволяет интегрировать в себя сишный/крестовый код, а раст без него попросту существовать не может.
Может =)

Моё утверждение, что Rust может существовать без C или C++ непоколебимо. Что в вопросе, то и в ответе =)

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

Я не буду это комментировать — нужны публике клоуны — пусть будут клоуны.

Оптимизировали представление структур в памяти. Изначально в Rust, как и в C++ был прямой порядок полей, что значит, что каждое поле находилось в памяти в том порядке, в котором было объявлено.

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

godbolt.org/g/4cC91L

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

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

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

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

Звучит неубедительно, предоставь пруфы.

Иммутабельности по умолчанию уже достаточно, ну и основа хайпа mt-safety, а он базируется на иммутабельности.

Ты думаешь, что внутри unsafe происходит что-то ну совсем страшное. На самом деле, там пишут то, что пишут в обычном С++.

Никакого «обычного С++» не существует. Это не имеет смысла. В unsafe используются небезопасные конструкции, которые являются такими же небезопасными и в С++.

Весь код вокруг unsafe проверяет компилятор

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

Тебя предупреждали. Программисты C++ любят повторять: «Писать безопасный код на С++ можно, для этого надо придерживаться определенных правил», ну так вот, этих правил и стоит придерживаться внутри unsafe. И всего лишь. Для желающих узнать больше.

Опять какой-то бред.

Никакого «безопасного» кода в С++ не существует. В С++ так же существуют безопасные и небезопасные конструкции и эти правила относятся к небезопасным.

Написание безопасного кода в С++ — это не правила написания unsafe кода — это правила написания safe кода, который ничем не отличается от раста.

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

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

Прости, был не прав.

Для меня это не новость.

C++ предпочитает мутабельное инстанцирование иммутабельному.

То, что «ты» написал — это не «мутабельное инстанцирование » — ты сам придумал эту ахинею. Оно УНИВЕРСАЛЬНОЕ. Читай по слогам до просветления.

Зачем ты в «иммутабельномое инстанцирование» передаёшь не иммутабельный объект? В этом твои проблема, что делаешь непонятно что и непонятно зачем. Если ты передашь иммутельный, то у тебя будет первая перегрузка.

Да, конечно.

Это не constexpr, а хрен пойми что. Никакого описания того, что оно может и зачем существует — нет.

Моё утверждение, что Rust может существовать без C или C++ непоколебимо.

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

А того, что у тебя, в мире хелвордов, может существовать какой-то раст без С/С++ — из этого ровным счётом ничего не следует, ведь это не общий случай.

В мире существуют, на самом деле их почти нет, «реальные» проекты на расте и ни один из них без С/С++ не существует. Начиная от компилятора раста, заканчивая его stdlib, сервой и прочим.

Но ты можешь им помочь и спасти их от С/С++, а то видишь как — они не могут, а ты можешь. Нужно срочно пойти и их научить.

godbolt.org/g/4cC91L
А тут мы видим рядовую картину того, что рядовой эксперт — это просто ретранслятор того, что он где-то увидел, либо прочитал в интернете. Он ретранслирует какую-то ахинею, и казалось бы — ну пойди ты и проверь, но нет.

Ну и?


movl $8, %edx

Что является подтверждением моих слов.


https://godbolt.org/g/idN4Uz


movl $12, %edx

Опять всё, как я сказал. Ты бы и рад соврать, да никак не получается, да? Обидно, небось)))

Факты:



И еще один факт: ты тролль, который апеллирует к субъективным величинам. Вон из профессии.

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

И заметьте, я вам показал, кто есть реальный тролль, и чьё призвание мыть полы. Ведь я не стал использовать её обсёр с T && как основание для игнорирование и закидывания дерьмом.

А вы не отвлекайтесь, минусуйте/плюсуйте — ведь вам же не интересно то, кто говорит что-то сознанное, а кто просто трепится и пытается свой слив замаскировать под «ты тролль, я с тобою не играю».
Ты бы и рад соврать, да никак не получается, да? Обидно, небось)))

Обрадовался и решил всё остальное проигнорировать, впрочем, как и всегда.

godbolt.org/g/GxbPZa

Давай я чутка тебя расстрою. Ну ты это, давай, побольше скобочек.
Warning: the -fpack-struct switch causes GCC to generate code that is not binary compatible with code generated without that switch. Additionally, it makes the code suboptimal.  Use it to conform to a non-default application binary interface.

Т.е. о чем я и говорил. Ломает древнее наследие совместимости с C. Глобально.


Объясни мне, в чем толк этой оптимизации C++, от которой код падает? Как ты можешь передать указатель на структуру во внешний C код, если у тебя внутри порядок изменился?

Т.е. о чем я и говорил.

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

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

Объясни мне, в чем толк этой оптимизации C++, от которой код падает?

Никакой код не падает. Иди передай мне во внешний код, собранный растом до оптимизации.

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

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

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

По поводу твоих супер-раст оптимизаций. Мне лень гуглить, но ir умеет структуры и скорее всего раст их форвардит в ir, а ir — это llvm.org/doxygen/group__LLVMCCoreTypeStruct.html Т.е. судя по всему шланг так же форвардит упаковку на llvm.

И очень высока вероятность того, что великие оптимизаторы раста просто включили флажок в llvm и раструбили это среди адептов как «супер-оптимизацию». Это не ново.

UFO just landed and posted this here
Технически, наверное, имеет право для то ли не POD-, то ли не trivially layout-типов (там определения с каждым стандартом меняются, не знаю точно).

Звучит логично. Только в примере от phponelove MyStruct — POD, и представлление этой структуры было оптимизировано.


godbolt.org/g/GxbPZa

Прости, был не прав. C++ предпочитает мутабельное инстанцирование иммутабельному. Спонсор западни — Константин Крамлин из Яндекса. Как много опытных плюсоидов завалилось на этом примере, просто жуть.

Интересно, а почему компилятор должен предпочитать обратное когда параметр — мутабельная переменная?

https://youtu.be/oXw2vXOUr1g?t=29m23s


Понимаешь, в чем дело, на зал из 100 человек правильно ответили 5. Это спустя 3 года работы с C++14 тебе эти вещи кажутся очевидными, но они контринтуитивны.

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

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

Я вот все еще никак не могу привыкнуть что Bar && — это ссылка на временный объект (простите, rvalue reference), а ID && — универсальная ссылка.
UFO just landed and posted this here
Я еще не видел людей, которые бы хаяли изменяемость данных

Ну я, например. Я чситаю важным достижением Rust, что он поощеряет иммутабельность, а мутабельность заставляет указывать явно.
UFO just landed and posted this here

Да я как бы про них в курсе. Меня смущает ситуация, когда стандартная библиотека построена таким образом, что для решения созданных ею проблем требуется или писать кучу бойлерплейта, или втаскивать стороннюю библиотеку в несколько раз большего объёма. Что не всегда возможно. И никаких изменений уже лет 20 в этом направлении. ranges-v3 может и поменяют ситуацию, но последний раз когда я в них лазил, я не нашёл адапторов, аналогичных бустовским.

UFO just landed and posted this here
У вас в С++ даже ассерты не ушли, надо -DNDEBUG добавить чтобы они исчезли. Код ужасно оптимизирован. Кстати в clang, rust использует то же AST дерево, так что добиться на С++ той же оптимизации обычно можно, я другого не встречал. А вот в обратную, как я ни старался, у меня не получалось в низкоуровневом коде. Там есть проблемы, например пока отсутствует alloca.
Про -DNDEBUG спасибо! Я обязательно исправлю этот недочет. Возможно, цифры изменятся.

Код ужасно оптимизирован

Этот комментарий нам никак не поможет, напишите конкретное место и как его улучшить.
Ну тут довольно много писать просто придется. Попробую которко. Во первых указателями на ноды оперировать слишком жирно, особенно в x64. В классисеском оптимизированном варианте там либо 2 таблицы lson\rson заводят, либо вообще одну state, где кодируют признак листа и смещения. Второй основно хак — это входные биты слать от старшего в байте\инте или 64бит регистре, и ипользовать что то типа add eax, eax для вытеснения бита в carry и использовать инструкцию adc без бранчей. Потом к текущему стейту прибавляем этот adc (обычно умноженный на 2\4), делаем запрос в таблицу state по вычисленному смещению, выясняем лист ли это, если да — то output byte, если нет то циклимся. Все, внутренний цикл очень короткий. Надо оптимизировать си код, пока инннер луп не будет выглядеть близким к тому что надо, это можно сделать, я проделовал чтобы было хорошо под всеми мажорными компиляторами.

Сходу совсем хороших примеров не найду, но нашел вот это www.virtualdub.org/blog/pivot/entry.php?id=205 специфический кейс для yuv.
Этот комментарий нам никак не поможет

Ну что там далеко ходить — я взял и решил добить huffman_encoding. У меня не особо было время, поэтому я не стал заморачиваться и исправлять всю лапшу — я просто поменял encode() и уже в 5раз быстрее раста, а это оптимизация ещё даже не началась — я просто выпилил ненужную там хешмапу.

Замена подсчёта частоты на нормальный — уже в 10раз быстрее. И оптимизация всё ещё даже не начиналась.

struct string {
  
  string() {}
  
  string(const std::string & str) {
    if(str.length() > data.size()) throw std::runtime_error{"bad length"};
    len = str.length();
    strncpy(std::data(data), std::data(str), str.length());
  }
  
  operator std::string_view() { return {std::data(data), len};}
protected:
  std::array<char, 32 - 1> data;
  uint8_t len = 0;
};

struct encoder {
  encoder(const std::unordered_map<char, std::string> & map) {
    for(size_t i = 0; i < m.size(); ++i) {
      auto it = map.find(i);
      m[i] = (it != std::end(map)) ? it->second : "";
    }
  }
  
  std::string_view encode(char c) {
    return m[uint8_t(c)];
  }
  
  std::string encode(const std::string & str) {
    std::string out;
    out.reserve(str.length() * 32);
    for(auto & x : str) {
      out += encode(x);
    }
    return out;
  }
  
  std::array<string, 256> m;
};


Ничего особо не проверял — если работает, замените им своё.

У меня не особо было время, поэтому я не стал заморачиваться и исправлять всю лапшу — я просто поменял encode() и уже в 5раз быстрее раста, а это оптимизация ещё даже не началась — я просто выпилил ненужную там хешмапу.
Замена подсчёта частоты на нормальный — уже в 10раз быстрее. И оптимизация всё ещё даже не начиналась.

Я выпилил ненужную хэшмапу, сделал норм подсчет частоты и Раст стал в два раза быстрее. https://gist.github.com/kpp/1217e694b6beeb08db6b72ebc3e8b9a8

Для наглядности было бы неплохо добавить голый C. И сравнение с -O2 и -O3. И clang для C/C++. А то в данном случае не совсем понятно, что с чем сравнивается.
Есть некоторые недочеты в статье, один из них — Option — это не «типа исключение», а вполне себе отдельный тип для своих нужд. В качестве типа для содержаний ошибки он никогда не используется, так как для этого есть тип `Result`. Далее, дурить borrow checker не нужно, он в 95% случаев все правильно говорит, и городить приходится не «хитрый» код, а именно *правильный* код с точки зрения всех возможных проблем.
и городить приходится не «хитрый» код, а именно правильный код с точки зрения всех возможных проблем.

Всё-таки над non-lexical lifetimes не зря работают. Иногда borrow checker действительно заставляет делать "дополнительные приседания", хотя, как по мне, это не особо страшно.

Тут можно видеть странный синтаксис наследования #[derive(Debug,Eq)]

Не знаю, знаете вы или нет, по этому предложения для меня непонятно. Но это не синтаксис наследования, это автоматическая реализация трейтов Debug и Eq.

Идиоматически правильно использовать Option, чтобы показать, нашли мы что-то или нет. Поэтому результат возврата binary_search будет Option, а не i32. И пропадут все явные касты, которые совершенно не нужны. Советую посмотреть реализацию в libcore.

Удачи в изучении Rust и спасибо за статью!

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


Бинарный поиск:


  1. Передавать &Vec<u32> нет никакого смысла — в таких случаях нужно передвать срез &[u32], это делает сигнатуру немного чище и обобщённее.
  2. Для индексов стоит использовать usize вместо u32, и возвращать Option<usize>.

fn binary_search(vec: &[u32], value: u32) -> Option<usize> {
    let mut l = 0;
    let mut r = vec.len() - 1;
    while l <= r {
        let i = (l + r) / 2;
        if vec[i] == value {
          return Some(i + 1);
        } else if vec[i] > value {
          r = i - 1;
        } else if vec[i] < value {
          l = i + 1;
        } 
    }
    None
}

Должен отметить, что возвращать 1-based индекс немного странно, ну да ладно :) Как видите, код стал чуть чище.


Слияние.


Данная конструкция не взлетит в Rust без unsafe кода, т.к. тут мы передаем два изменяемых подмассива, которые располагаются в исходном массиве. Система типов в Rust не позволяет иметь две изменяемых переменных на один объект (мы знаем, что подмассивы не пересекаются по памяти, но компилятор — нет).

Это не совсем верно. На срезах в Rust есть метод split_at_mut(), который возвращает два непересекающихся изменяемых среза из одного исходного. Но в общем случае да, вы правы — без специального кода и возможно unsafe сделать две мутабельные ссылки в один объект, даже если они не пересекаются, не получится.


Кодирование Хаффмана:


  1. Передавать String как аргумент, как правило, не нужно — но в вашей ситуации это оправдано, потому что вы этот объект немедленно добавляете в HashMap. Во втором match, однако, вызывать clone() на ней не нужно, потому что дальше по коду строка нигде не используется и её можно переместить в оператор конкатенации и дальше в HashMap.
  2. Конструкцию типа match &self.left { &Some(ref leaf) => ... } вполне можно заменить на match self.left { Some(ref leaf) => ... }, и символов станет немного поменьше.
> Есть несколько замечаний по коду, но в целом у вас вполне себе идиоматичный Rust получился.

Не соглашусь. Использование -1 для индикации отсутствия результата вместо Option<usize> — грубая ошибка. В Rust столько усилий положили на то, чтобы было легко выражать и обрабатывать случаи отсутствия значений, а автор всё это игнорирует. Ценность этого примера для людей, которые хотят изучить Rust — даже не нулевая, а отрицательная. И это не единственная проблема такого рода в статье.
> Использование -1 для индикации отсутствия результата вместо Option — грубая ошибка.

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

Насчет возврата -1 из binary_search, как так получилось: изначально в задаче в онлайн курсе было такое требование (возвращать -1, индексация с 1), изначально я решал данную задачу на C++ и, недостаточно подумав, переписал так же на Rust. Действительно, лучше возвращать Option, а при записи результата делать unwrap_or(-1)

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

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

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

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


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

Снова предположение без фактов и доказательств.
Реализации и тесты алгоритмов открыты, вы можете сами убедиться в честности и способностях участников.
Два замечания:
1. Использование gcc для компиляции C++ в данном случае не правильно. Дело в том, что Rust использует LLVM в качестве бекенда, а gcc нет. Надо было использовать Clang, иначе твой бенчмарк сравнивает не столько Rust vs C++ сколько LLVM vs gcc.
2. В статье об этом не сказано, но в коде я кажется вижу, что вы замеряете время вызова функций руками (вижу в этом файле common/measure/src/main.rs ). В Rust в ночной сборке есть модуль для бенчмарков и надо использовать его, что бы получить более надежные результаты. Я не C++ программист, но уверен у них тоже есть своя либа для бенчмарков. Руками такое делать не стоит т.к. есть миллион граблей на которые можно наступить и получить неправильный результат.
Я не C++ программист,
Заметно.
но уверен у них тоже есть своя либа для бенчмарков.
На чём, извините, эта уверенность построена?

Да, в C++ есть такая либа. И не одна. Десятки их, если не сотни. Если мы сейчас начнём между ними выбирать, то «утонем».

SPECи, coremarkи и прочие замеряют время работы программы. Тупо. По секундомеру. 5-7 запусков, в зачёт идёт лучший результат (идея в том, что OS может замедлить программу 100500 разными способами, а вот ускорить — так это вряд ли). Разумеется программы подобраны так, чтобы работать несколько минут — что позволяет пренебречь временем загрузки программ и временем ввода-вывода.

Руками такое делать не стоит т.к. есть миллион граблей на которые можно наступить и получить неправильный результат.
Если вы сравниваете отдельные процедуры с целью микрооптимизаций — то да, не стоит. Если же вы хотите сравнить два языка, то лучше всего — программа time(1), так как она точно работает одинаково для разных программ.
1. Согласен, как только доберусь до машинки, на которой проводил замеры, соберу данные по clang. Постараюсь взять clang такой версии, чтобы версия LLVM совпадала с Rust.
2. Я измеряю не время вызова функций — это действительно сложная история и есть много готовых решений. Я измеряю полное время выполнения программы. Я отказался от программы time, т.к. она под Mac os x не выдавала результат с нужной мне точностью. Программа measure, на которую вы указали, — это лишь попытка обойти это ограничение и иметь возможность проводить замеры на Linux & Mac os x.
Полное время выполнения программы это вообще нестабильная история. Для близких пар языков вроде C++ и Rust это может быть не особо существенно, но конечно Java и Rust так уже сравнивать совсем нельзя. Но и в данном случае я бы перестраховался — вроде Rust генерирует бинарники по больше чем C++ так как больше тянет из стандартной библиотеки и это может влиять на время запуска.
Так же не очень стабильно ведет себя единичный запуск. Даже для языков, где нет сложных адаптивных JIT-ов и GC, всё равно второй запуск выполнится быстрее потому, что CPU имеет некоторый элемент адаптивности внутри (предсказатель переходов и кэш). Поэтому по хорошему надо гонять бенчмарк несколько раз пока время не перестанет изменяться, и взять уже только стабильные замеры времени для ответа.
И ещё я забыл сказать: у вас в замерах не хватает погрешности. Всё таки время выполнения это случайная величина. Насколько я помню JMH для Java при подсчете погрешности считает, что время распределено нормально. Думаю это приемлемо, хотя конечно я бы взял какие-нибудь квантили — чисто на всякий случай.
Тут ещё такой момент, о которым вы тоже пишете, от которого я сам не знаю как избавиться: у языков всё таки разные стандартные библиотеки и результаты больше говорят о разницы в реализации структур данных в библиотеке, чем о чем-то другом. По хорошему надо отдельный бенчмарк сделать для этих структур в обоих языках, а потом оценить их вклад в общее время выполнения теста и как-то его вычесть наверное.
Для близких пар языков вроде C++ и Rust это может быть не особо существенно, но конечно Java и Rust так уже сравнивать совсем нельзя.
А почему нельзя-то? Что вы такое собрались делать, что время запуска для вас неважно, а скорость работы — важна?

Что-то долгоживущее — это, обычно, что-то в чём вы язык выбирать по скорости не будете. Писать плагин для emacs'а вы будете на Lisp'а, а для CLion'а — на Java или Kotlin'е. Rust ни там, ни там не подходит. И не из-за того, что он медленный.

А если вы делаете отдельную утилитку — то скорость её работы будет определяться, в том числе, временем запуска. Писать её на языках, в которых рантайм запускается настолько долго, что на этом фоне незаметно, сколько времени сортируется массив на несколько миллионов элементов — просто глупо. И да, если в результате этих замеров «неожиданно» окажется, что python или perl лучше и быстрее java… то это потому, что так оно и есть — для подобных задач, разумеется…
Все зависит от цели. Если цель стоит измерить время запуска приложения — его и надо мерить. А если цель — это измерить производительность конкретного участка кода — надо мерить его, уже без времени запуска. Тут вариантов очень много разных и конечно не зная юзкейса делать абстрактный бенчмарк в некотором роде бессмысленно. Скажем атомарные операции стоят дороже в зависимости от того как часто они выполняются — все это надо учитывать когда вы беретесь за реальную работу.

Rust на данный момент подразумевает использование LLVM, а в C++ можно использовать как gcc так и clang. Поэтому если задачей является сравнение производительности, то сравнение c++ скомпилированого g++ и rust скомпилированого llvm вполне корректно. Если бы вопрос был в том какой из языков llvm лучше оптимизирует, тогда Ваше замечание было бы справедливо.

UFO just landed and posted this here
Процитирую статью:
Делалось 10 прогонов каждой задачи на каждом наборе данных, далее результаты усреднялись


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

Но самое главное — выставить правильный governor, иначе у вас и после 20 запусков программа будет всё ускоряться и ускоряться…
UFO just landed and posted this here
Есть и ещё более патологические случаи. Мы как-то пытались померить скорость работы на ARM'е. Оказалось, что ни на одном доступном нам телефоне ничего толком померить нельзя с точностью до 1% — ибо перегреваются они, собаки.

Спас ChromeBook (уже не помню какой модели): он достаточно велик для того, чтобы на однопоточных тестах, по крайней мере, охлаждения хватало и тормозов бы не было.
Будет ли программа считать 10 или 5 секунд — обычно не так важно. А вот будем ли мы её писать и отлаживать 3 дня или 10 дней это важно.

Что у rust со скоростью написания и отладки?
Будет ли программа считать 10 или 5 секунд — обычно не так важно

В системных языках, каким себя позиционирует Rust, важно.
Если тиражи штучные — неважно. Мне намного важнее лишние 2-3 тысячи долларов за написание кода, чем 5 долларов за более мощный проц.

P.S. не говоря уже о том, что если скорости не хватает — то изменением алгоритма получается выигрыш в разы, а не на проценты.

P.P.S Скорость — это всего лишь фетиш с++ников.
От задач зависит. Не бывает серебряных пуль или «no free lunch».
Алгоритмы менять до бесконечности невозможно — найдется предел около текущего state of the art.
5 долларов на лишний проц — это прекрасно, пока речь не идет о системах с сотнями, тысячами и более серверов.
Думаю, любой инженер должен понимать, что «от задачи зависит».
Алгоритмы менять до бесконечности невозможно — найдется предел около текущего state of the art.

Поскольку у нас один и тот же LLVM и для С++ и для Rust, то не так сложно поменять код для достижения ровно того же результата. Лет 50 назад это было любимой темой — как поменять код, чтобы компилятор его оптимальней скомпилировал. :-) Так что какие-нибудь обработчики прерываний в итоге будут иметь одинаковый бинарный код и для Rust и для С++.

5 долларов на лишний проц — это прекрасно, пока речь не идет о системах с сотнями, тысячами и более серверов.
Но это уже не штучные тиражи. И потом — ну сэкономили вы 5 тысяч долларов на тысяче серверов. Сколько недель работы команды вы этим деньгами оплатите?

Если я не ошибся в подсчетах — то всего лишь неделю для команды в 5-10 человек.

Думаю, любой инженер должен понимать, что «от задачи зависит».
Угу. Только приведите мне пример задачи, где реально выгоднее замедлить на 50% разработку ради получения на 50% более быстрого кода? Мне как-то кажется, что это или экзотика или opensource, где разработчикам не платят.
Но это уже не штучные тиражи. И потом — ну сэкономили вы 5 тысяч долларов на тысяче серверов. Сколько недель работы команды вы этим деньгами оплатите?
Давайте пересчитаем.
Угу. Только приведите мне пример задачи, где реально выгоднее замедлить на 50% разработку ради получения на 50% более быстрого кода?
На 50% более быстрый код, говорите? Ну так значит вместо тысячи серверов у нас будет уже 500. Предположим, сервера у нас — середнячки, по 2500$ каждый. Экономия — 1250000$.
на 50% более быстрый — это 667 серверов, вместо 1000. Увеличения числа серверов нам не нужно — затраты по памяти и сети те же. Так что увеличиваем число ядер. Замена 8-ядерных I7 на 14ядерные обойдется примерно в 500 долларов. 500 на 333 = 166 500 долларов. В 7.5 раз меньше.

Теперь рассмотрим вашу сумму. 1.25 миллиона долларов это 830 тысяч долларов зарплаты (если работаем по-белой бухгалтерии). Разработчик получает 2 тысячи долларов месяц. Итого 415 человеко-месяцев. Проект большой, работает команда из 50 человек. Так что на 8 месяцев этих денег хватит. Ну или на месяц с хвостиком, если мы ставим более быстрые процессоры.

Теперь посмотрим, что мы теряем за эти 8 месяцев. предположим проект приносит 12 миллионов долларов дохода в год (дохода, а не прибыли). Значит за эти 8 месяцев мы потеряем 8 миллионов.

Так что баланс — не в вашу пользу. Лучше запуститься с неоптимизированным кодом, чем ждать оптимизации. Тем более, что иметь запас по скорости — очень полезно. Мало ли что бизнесу потребуется. Да и сервера дольше живут, если недогружены. И на пиковую нагрузку запас надо оставить. И не 50%, а 200-300%.

Так что пример не катит…

Если важно именно как можно скорее выкатить продукт — то можно оптимизацию отложить на потом. Если же продукт уже на рынке, и выполняет свои задачи, то можно потратить время и на оптимизацию. Facebook писался сразу на обычном PHP, а потом они начали вкладывать деньги штуки типа HHVM для оптимизации. Последние версии PHP также гораздо быстрее предыдущих — разработчики потратили немало времени на оптимизацию. Правда, конечные веб-разработчики часто съедают возросшую проворность PHP использованием тяжёлых фреймворков.

Хороший антипример: Skype на мобильных платформах. Он похож на неповоротливого монстра. В итоге этим IM я практически не пользуюсь на телефоне. Только при крайней необходимости запускаю, и каждый раз это боль. Кажется, я не один такой: на мобильных платформах популярны совершенно другие IM, которые гораздо более шустры. И все они появились гораздо позднее Skype.
Напомню:
приведите мне пример задачи, где реально выгоднее замедлить на 50% разработку ради получения на 50% более быстрого кода?
Вот и получается, что таких примеров мало. Я ведь не про оптимизацию «вообще», а про конкретный баланс.

Много примеров, где стоит потратить 5% времени ради оптимизации на 200-300%. А вот 50% времени разработки на 50% скорости — редкость. Тому же скайпу 50% не помогут — он все равно будет тормозить.

Котенок в свое время ускорил PHP в 10 раз. Это дало общий прирост скорости… всего в два раза. Трудоемкость оценить сложнее, но видимо где-то не большее 10-15 процентов.

Вот такую оптимизацию — я понимаю. А 50% ускорения в обмен на 50% трудоемкости — не понимаю.
А вот 50% времени разработки на 50% скорости — редкость. Тому же скайпу 50% не помогут — он все равно будет тормозить.
Если прирост производительности будет 50% за год разработки даже с 75% замедлением появления новых фишечек из-за этого, то глядишь за столько лет сколько он тормозит — им стало бы приятно пользоваться. И кто-то даже может быть начал бы рассматривать его как адекватный мобильный IM. А тормозит он с самого появления в 2010 (говорю про версию под Android).
Увы, 50% — это разовый прирост. И то, поскольку и С++ и Rust используют один и тот же LLVM, то достичь того же прироста можно без смены языка — просто написав более удобно для оптимизатора.

Это вы круто Rust разрабам 2к зп приписали… ))) Умножайте на 3

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

Примеры такие редки, не спорю. И в подавляющем большинстве случаев стоит отдать предпочтение языку и технологиям, на которых разработка быстрее и стабильнее.
Но вы просили пример: допустим у меня нет возможности выбрать железо — мобильная разработка, в ней преимущество в скорости может быть оправдано, поскольку увеличит круг пользователей. Пример: есть ли разница во времени прокладки маршрута по карте между 5 секундами и почти 8ю, когда у твоих конкурентов — 2секунды?
Или обработка данных на локальной машине, чтобы попробовать модели разные: разница между 10часами и 5ю будет значительной.
Стоит войти в условия ограниченности ресурса по вычислениям, например, и задача выжать еще 50% даже ценой смены языка перестает выглядеть фантастично.

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

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

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

На это ответит только бизнес и конкретные обстоятельства. Всякое бывает. У мобильных устройств не всегда есть связь или мы делаем систему с нулевым доверием.
Или у нас уже есть тройка спецов на чудо быстром языке, которых схантили с успешного проекта конкурента, и мы можем использовать их опыт.
Задачи и условия бывают разные. Чаще всего выигрыш по скорости в 2-3 раза относительно C или C++ не стоит значительно большей разработки и увеличения рисков на поддержке (мало спецов). Но от этого ваше изначальное утверждение верным не становится.

Повторю свое изначальное утверждение:
Будет ли программа считать 10 или 5 секунд — обычно не так важно. А вот будем ли мы её писать и отлаживать 3 дня или 10 дней это важно.
Не вижу в вашем ответе опровержения этого тезиса. Более того, с первой половиной тезиса вы уже согласись, сказав Примеры такие редки, не спорю.

Вы хотите опровергнуть вторую половину тезиса?
Сама Mozilla, которая развивает Rust, сейчас тратить немало денег на оптимизацию и переписывание своего браузера. Firefox 57 реально ощутимо проворнее Firefox 52. Впереди ещё много работы в этом направлении. Может быть, это поможет Mozilla предотвратить потерю пользователей, которая происходит уже много лет. Браузер пишет, скажем, 50 разработчиков. А пользуются миллионы.

Любой код, который выполняется на миллионах машин, имеет смысл оптимизировать даже на самом низком уровне, под конкретные архитектуры и наборы инструкций. Например, libjpegturbo — оптимизированный декодер JPEG — спонсируется и используется Mozilla, Google и рядом других компаний. Наблюдаю за разработкой Opus. Периодически вижу, что разработчики (помимо повышения качества кодирования) отдельно занимаются оптимизацией кодека даже для конкретных наборов инструкций конкретных процессоров (SSE, AVX и т.д.), сейчас проект спонсируется Mozilla. В разработке видеокодек AV1 — более десятка гигантов мира IT собралось для того, чтобы сделать самый лучший в мире видеокодек. И уж поверьте, без вкладывания денег в оптимизацию оно не обойдётся. Миру нужен эффективный кодек, который и жмёт хорошо, и за разумное время.

Всё зависит от задачи. Если вы пишете сайтик — его можно и на PHP написать, и то что код на этом языке, грубо говоря, в 100 раз медленнее — не так страшно, так как основной тяжёлый код (та же БД), который будет выполняться — всё равно достаточно оптимальный код на C/C++. Вот код той же MySQL имеет смысл оптимизировать. Если бы её можно было бы магически ускорить на 50% — этим стоило бы заняться. Потому что 50% ускорение для такой штуки — это реально очень много и ощутимо. Выигрыш от такой оптимизации сложно оценить в цифрах, потому что от неё по сути выиграли бы вообще все. Проспонсировать такую оптимизацию мог бы кто-нибудь, кто сам очень активно использует эту БД, и хотел бы в первую очередь сократить свои расходы на сервера, а польза для остального мира — это уже как бонус.
Rust ощутимо выгодней во времени отладки. Он страхует от кучи ошибок. И это даст возможность Мозилле не бежать вдогонку за хромом, а обогнать его.

А задач, где есть смыcл оптимизировать именно сменой языка — безумно мало. Напоминаю, что речь идет об одном и том же LLVM. То есть выигрыш есть лишь на одном из вариантов написания кода. Чуть подпилить код напильником — и мы получим одинаковый бинарный код и для C++ и для Rust.
Вот с Mozilla крайне неудачный пример. 57 проворнее 52 из-за того, что в 57 не работают хоть и устаревшие, но все же популярные расширения, что вызывает негативную реакцию пользователей (поиск альтернатив, уход на форки или радикальная смена браузера). И это не проблема того или иного языка, а проблема с менеджементом проекта.
Я лично сравнивал Firefox 52, PaleMoon и Firefox 57. Всё без расширений. Firefox 57 был ощутимо проворнее. Обратное, думаю, можно будет увидеть на слишком старых машинах — требования к RAM, конечно же, несколько подросли. Накладные расходы на поддержку нескольких процессов.
Они отключили устаревшие возможности и на этом получили выигрыш по скорости.
Вы не следите за развитием Firefox, поэтому делаете такие неверные выводы. Они переделывают движок.

hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo
hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank

Старые расширения имели доступ ко всем потрохам браузера (никакого стабильного внешнего API не было), и это связывало разработчикам браузера руки. Поэтому сейчас, во время больших внутренних изменений в браузере, они отключили поддержку старых расширений. На самом деле она ещё имеется в браузере, и в ночнушках (и Developer Edition) можно активировать её обратно. Но разработчики сейчас активно выпиливают старые технологии из браузера. Например, вот прогресс по выпиливанию XBL-компонентов из браузера. Как видите, подавляющее большинство всё ещё на месте. Такие изменения не делаются за одну ночь. Тут возможно на несколько лет работы. Но на данный момент поддержка старых расширений отключена искусственно. Код их поддержки и всех соответствующих технологий (XUL, XBL) всё ещё в браузере. Впрочем, это не они замедляли работу браузера. Их выпиливают просто для унификации и упрощения движка. XBL заменяют на стандартные Web Components, а XUL заменяют на HTML. Вот когда XBL и XUL не останется внутри кода интерфейса браузера — тогда их поддержку можно будет убрать из кода движка. Но это случится не так скоро.
Вы не следите за развитием Firefox, поэтому делаете такие неверные выводы. Они переделывают движок.


Всё это — никакого отношения к firefox не имеет. Обе ссылки про серво, которое до сих пор не умеет к css.

В firefox добавили ксс-парсер, при этом никаких объективных свидетельств за то, что он быстрее — нет. Да и парсинг ксс — это копейки от времени рендеринга страницы. На этом нововведения(глобальные) заканчиваются. По крайней мере ни одна из ваших ссылок к ним отношения не имеет.

Обе ссылки про Firefox. Mozilla переносит некоторые наработки из Servo в Gecko, и обозвали этот проект Quantum. Если бы вы прочитали хоть немного то что по ссылке, то поняли бы, что это это не только про парсинг. Это ещё и про быстрый ответ на вопрос «нужно ли применять это правило для этого вот элемента?», которое браузер задаёт этому движку CSS каждый раз при каждом рендеринге любого элемента на странице. Там же объясняется почему этот процесс не так тривиален, как кому-то могло бы показаться.
Обе ссылки про Firefox.

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

Mozilla переносит некоторые наработки из Servo в Gecko, и обозвали этот проект Quantum.

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

Если бы вы прочитали хоть немного то что по ссылке, то поняли бы, что это это не только про парсинг.

Во-первых, по ссылки ничего не написано про firefox — и это ясно и понятно, ведь вы ничего мне не показали, и не покажете. Во-вторых, что за привычка такая — болтать и не показывать?

которое браузер задаёт этому движку CSS каждый раз при каждом рендеринге любого элемента на странице.

Никакого, хоть как-то работающего, движка css в серво не существует, именно поэтому его не может существовать в firefox. Никакой движок ни в какой firefox никто не добавлял.
Во-первых, по ссылки ничего не написано про firefox — и это ясно и понятно, ведь вы ничего мне не показали, и не покажете. Во-вторых, что за привычка такая — болтать и не показывать?
Похоже, что вы всё же не читали что там написано. А там чётко сказано:
This is the first big technology transfer of Servo tech to Firefox. Along the way, we’ve learned a lot about how to bring modern, high-performance code written in Rust into the core of Firefox.
We’re very excited to have this big chunk of Project Quantum ready for users to experience first-hand. We’d be happy to have you try it out, and let us know if you find any issues.
Более того, в описании новшеств Firefox 57 также чётко упоминается, что Quantum CSS уже в Firefox:
Firefox's new parallel CSS engine — also known as Quantum CSS or Stylo — is enabled by default in Firefox 57 for desktop, with Mobile versions of Firefox to follow later on. Developers shouldn't notice anything significantly different, aside from a whole host of performance improvements.

По второй ссылке, где описывается следующий компонент, почти готовый к внедрению (Quantum Render), чётко сказано, что оно ещё не в релизе, но войдёт в него в скором времени:
But there’s another big piece of Servo technology that’s not in Firefox Quantum quite yet, though it’s coming soon.
Не вижу причин не верить Mozilla и здесь.

Я уже сказал, что серво не умеет в css
Как же он тогда работает? Quantum CSS не занимается рендерингом вообще. Он занимается разбором CSS и быстрыми ответами на вопрос какие из правил применимы к каждому из тысяч элементов на каждой странице. Это всё что он делает. Если демка Servo (которая действительно очень сыра) не может отрендерить какое-то правило правильно — это не вина этого компонента. Он не отвечает за это.
Похоже, что вы всё же не читали что там написано. А там чётко сказано:

Нет, нет. Это так не работает. Недостаточно что-то кинуть и сказать, что «я прав» — надо вывести это «прав» из ссылки. Вы утверждали то, что там что-то есть, кидая ссылки. В которых ничего про «есть» не сказано, а то, что описывается — никакого отношения к firefox не имеет.

Это я ещё вас пожалел и уж не стал играть ту карту, что вы слились на то, что есть только Quantum CSS, хотя вы пастили Quantum Render. Из этого уже следует то, что вы пастите всё подряд.

Более того, в описании новшеств Firefox 57 также чётко упоминается, что Quantum CSS уже в Firefox:

Из этого булшита равным счётом ничего не следует — это рекламная агитка, такая же, как и эта hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank, перевод который я уже смешивал с дерьмом тут. Как оказалось — эта агитка состоит из вранья, манипуляций, ахинеи невероятных масштабов и просто не работает.

По второй ссылке, где описывается следующий компонент, почти готовый к внедрению (Quantum Render), чётко сказано, что оно ещё не в релизе, но войдёт в него в скором времени:

Зачем вы несёте эту ахинею? Никакого Quantum Render в природе не существует, существует нерабочая поделка, которая умеет рендерить 2-3 ксс свойства.

Quantum CSS — это парсер примитивного ксс, объективных свидетельств за то, что он где-то там быстрее — в природе не существует. Всё. Таких «Quantum CSS», разной степени паршивости, написано десятки, если не сотни.

Не вижу причин не верить Mozilla и здесь.

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

Как же он тогда работает? Quantum CSS не занимается рендерингом вообще.

Естественно, и я об этом вам уже сообщил. Только вот вы пастили не только Quantum CSS, а и рендер, но почему-то слились на парсер.

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

Зачем вы как попугай повторяете то, что увидели в агитке? Я это могу прочитать из без вас. Никаких критериев «быстро» не определено, объективной информации нет — это всё пустой трёп.

Отдавать computed style может любой хелворд, хотя это и есть хелворд.

Если демка Servo (которая действительно очень сыра) не может отрендерить какое-то правило правильно — это не вина этого компонента.

Опять какое-то враньё. Это не демка servo — это Quantum Render, который «уже готов к внедрению».

И опять же, враньё. Вы всё перепутали — демка не не может отрендерить какую-то часть правил — она не может отрендерить НИЧЕГО. А даже те пару правил, которые работают, которые и показали в демки — на самом деле не работают, они рисуются с артефактами и дерьмо анлиалиасингом. А видео, на котором запечатлена работа демки — каким-то странным образом дерьмово пожато, что никаких артефактов и качество отрисовки — на нём не видно. Вот ведь какое совпадение, да?

Ну и да, она побеждает хром только во влажных мечтах. В конечном итоге — что мы имеем? Враньё про то, что побеждает хром. Враньё про то, что она что-то там умеет. Враньё про то, что она где-то там быстрее. Враньё про то, что она где-то там( в параллельной вселенной, наверное) готова ко внедрению.

Но вы продолжайте, нет причин не верить мозилле. Блаженны верующие — верьте дальше, ведь объективная реальность — это не для вас.
Вы опять мимо. Quantum Render — это не про поддержку каких-то свойств в CSS. Это более низкоуровневый компонент, который браузер использует для «сборки» конечной картинки. Цитата:
The goal of the Quantum Render project is to take the WebRender compositor in Servo and embed it in Firefox. It will replace Gecko's existing compositor, interfacing with Gecko's main-thread layout code. As WebRender is written in Rust and uses a very different design approach, we expect to get stability and performance benefits from this switch.
Вам стоило бы немного немного разобраться в тех вещах, о которых вы пытаетесь говорить. На этом я завершаю разговор с вами, ибо уже очевидно, что продолжать его нет смысла.
Вы опять мимо.

Опять ответов нет, а трёп есть.

Quantum Render — это не про поддержку каких-то свойств в CSS.

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

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

И на основании вранья делаются какие-то выводы, а вернее делается слив.

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

Что мы имеем в конечном итоге? Рассуждения о движках, которых нет. Спустя десять лет впилили ксс-парсер на расте. Никаких бенчмарков нет, никаких объективных данных нет. Ничего нет, а трёп о том, что «супер-быстрый» есть.

Далее мы пытаемся впиливать ещё один хелворд, который как-бы супер-быстр и работает, а самом деле он сливает хрому и не работает. Никаких объективных свидетельств за работает — нет, а за не работает — есть.

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

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

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

Ну и как всегда, мы сравниваем раст и кресты, но при этом сравниваем ни хром и firefox, а firefox с firefox — т.е. калеку с новыми изваяниями. Почему-то хрому для того, чтобы уделывать firefox никакого раста не нужно. Почему мы сравниваем ни с хромом? А причина проста — вранья, манипуляции и маркетинг. Что он делает на техническом ресурсе? Непонятно.

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

Если в коде была скомпилирована такая конструкция (далее идет условный код):
if UseOldAPI {
//Тут работаем со старым API};

то менеджер памяти, увидев, что UseOldAPI == false, не будет переводить блок, который выполняется при UseOldAPI == true, из долговременной памяти в оперативную, поскольку исходит из парадигмы «Оперативная память в многозадачных ОС – очень ценный ресурс, его нужно расходовать экономно». И менеджеру памяти ОС все равно компилятором с какого языка была собрана программа, с Rust/C++/Pascal.
Это всё равно не скажется на производительности.

Во-первых, менеджер памяти может выгружать из RAM неиспользуюмую память только страницами, по 4 килобайта. То есть если по коду разбросано много вот таких вот блоков «if», и все они не слишком велики — то большая часть этого кода всё равно будет оставаться в памяти.

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

К слову, этот код загрузки старых расширений на самом деле даже не отключён. Это я дезинформировал вас. Он работает. Ему просто запретили загружать пользовательские расширения. А вот расширения, подписанные самой Mozilla (не addons.mozilla.org!) — работают, и Mozilla этим пользуется для расширений, которые нельзя сделать средствами WebExtensions. Подробности вот тут, смотрите «legacy extension», как видите даже в обычных релизах они доступны, но только для расширений от самой Mozilla.
Думаю, если задаться целью, то можно станцевать как-нибудь так, и таки получить возможность устанавливать старые расширения в обычных релизных версиях браузера.
Это вы сильно погорячились. Ну как пример: FreeRTOS написана на Си, причем портируется куда угодно. И так и поставляется — в исходных кодах на Си. Хотя, если её скомпилировать на ассемблер, а потом подчистить получившийся код руками — можно будет получить 50% ускорение ценой даже не 50, а 5% времени разработки. И что? Разве такое часто делают?

Процессорная мощность, которая тратится на RTOS — это обычно сильно меньше 1% времени задачи. Так что нету смысла её переписывать на ассемблер.

Во-первых, не надо все системы системы реального времени сводить только к операционным системам.

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

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

А если это не ОС, то нам нужны резервы производительности:

  • на пиковую нагрузку
  • на дальнейшее развитие алгоритмов
  • на долговечность процессора (при использовании 100% CPU обычные процессоры живут годы, а не десятилетия)

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

Лично я ускорял ассемблерный код всего дважды. Один раз по делу — 300 строк для OMRON CV1000. Это была добавка к уже работающим программам контролера, обеспечивающая запись переключений в кольцевой буфер. Ну и там действительно брались таблицы выполнения инструкций и оптимизировалось. В остальной части системы (на PC) резерв по производительности был 10 тысяч раз и достигнут он был изменением алгоритмов, а не оптимизацией кода.

Второй раз — это было в молодости. Чисто спортивная развлекуха. Я сократил Flist (однооконный файловый менеджер) на 40% бинарного кода вместе с исправлением ошибок и добавлением функций. Но он изначально был на ассемблере.
Только когда у нас миллионные тиражи ширпотреба, себестоимость изделия становится важнее резервов.

Значит мы встречались с разными задачами. То, что я видел — камень используется почти под 100%. а более мощный нельзя поставить по целой куче причин.
А на пиковой нагрузке реалтайму кранты? Или это такой реалтайм, что на пике можно сильно медленней работать?
А третий вариант того, что временные диаграммы хорошо просчитаны и пик немногим отличается от нормы исключаем?
Ну или пик — или «немногим отличается от нормы». Как бы московские холмы — это все-таки не горные пики. :-) Ну да, иногда бывают системы без пиков.
UFO just landed and posted this here
Не противоречит, вы бы ещё FOCAL привели как пример. А в данном случае мы имеем два фронтенда к одному и тому же LLVM с одинаковой моделью памяти. Если у нас есть исходный код низкого качества — то мы получим отличия в бинарном коде. Но если мы начнем оптимизировать код — бинарный код для обоих языков будет практически одинаков.

При этом выигрыш одного или другого языка больше зависит от версии LLVM, чем от самого языка. А то, что мы видим в статье — это преимущества LLVM перед GCC,

Если не согласны — объясните, в чем принципиальные отличия бинарного кода, сделанного одним и тем же компилятором?
UFO just landed and posted this here
у хаскеля — другая модель памяти. Там ведь сборка мусора, вроде?

Мы вообще что сравниваем? качество библиотек, стиль написания, качество компиляции или язык?

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

Другой момент, если вы сравниваете типичные стили. Но кто вам мешает писать на С++ в стиле Rust?

Мораль в том, что скорость исполнения — второстепенны параметр. А вот скорость написания и отладки — важна. И если Rust обгоняет С++ по скорости отладки раза в 2 — значит есть смысл на него переходить.
UFO just landed and posted this here
иногда нельзя докупить ещё процессоров, ещё памяти, ещё чего-нибудь.
Вот именно, что иногда. Да, иногда бывает, что никакого запаса по мощности на развитие нет, иногда бывает, что процессоры используется со 100% загрузкой, то есть в очень тяжелом режиме по долговечности. Но и то и то — это просчет менеджмента или нехватка денег.

Э… мне кажется, нельзя в принципе говорить "один и тот же алгоритм" применительно к программам на плюсах и Хаскеле.

UFO just landed and posted this here
Измерялось полное время работы программы, в которое включено чтение данных, полезные действия и вывод в /dev/null

А причёт тут тогда заголовок, который гласит «Rust vs. C++ на алгоритмических задачах», когда ТС сравнивает Rust vs C++ на операциях ввода-вывода, которые обычно занимают больше всего времени.
UFO just landed and posted this here
Мне кажется странным: записывать в плюсы языка жёсткое ограничение строкового типа. По-моему это надо относить к нейтральному замечанию, как и в этом случае:
+-В Rust отсутствуют исключения, обработку ошибок необходимо делать после вызова каждой функции. Причем Rust не позволит получить возвращаемое значение, если вы не обработаете случай неуспешного завершения функции. Есть макросы и встроенные конструкции языка, которые позволяют упростить эту обработку и не сильно раздувать код проверками.
Вы про UTF-8 строки? Если вы используете только ASCII символы, то вы не заметите разницы, и кол-во байт в строке будет равно кол-ву символов в строке.
Мне кажется странным: записывать в плюсы языка жёсткое ограничение строкового типа.
Потому что совместимость. В языках, в которых есть больше одного строкового типа преобразование между ними — это страшная головная боль.

Вариант, принятый в rust — близок к оптимальному: есть один строковый тип, который используется везде, где только можно — и есть дополнительные, которые используются там, где нужно. Про WTF-8 кодировку слышали? Вот она из rust'а…
Это не только не плюс и не нейтральное замечание, а очень крупный недостаток, так как из-за (потенциально) переменных размеров элементов все операции доступа к определённому элементу в строках выполняются за O(n), вместо O(1), что напрочь убивает всю производительность.

Не доступ, а поиск позиции. Доступ по известной позиции тот же O(1)
По той банальной причине, что во всех вменяемых системах юникод.
И UTF-8, и UTF-16 активно используют суррогатные пары.
UTF-32? Окей, 2Х-4Х оверхед по памяти. Но можно.
Вспоминаете про std::string? Там ровно те же проблемы, если вы положили внутрь многобайтную кодировку. Только при этом начисто отсутствуют средства доступа к символам.

В C++ полная свобода — всегда можно выбрать (или написать самому) реализацию, максимально соответствующую задаче.
А в Rust — как решить проблему вроде разбивания текста на строки по 80 символов?

В Rust точно так же есть свобода написать свою реализацию.
Не совсем понимаю вас, в чём проблема либо держать счётчик знаков и добавлять конец строки на каждом 80-м, либо просто искать позицию 80-го символа.

Не совсем понимаю вас, в чём проблема либо держать счётчик знаков и добавлять конец строки на каждом 80-м, либо просто искать позицию 80-го символа.
Это типичный разговор с «апологетами UTF-16/UTF-32».

У них есть фича, с которой они носятся, как с «писаной торбой»: возможность обращаться к code pointам за O(1) (с ограничениями в UTF-16). Когда задаётся разумный вопрос: «ну и зачем оно вам?» — в ответо обычно предьявляется несколько «естественных» задач где это, якобы, помогает.

«Разбор полётов» неизменно показывает, что «помощь» оказывается ложной и, в результате, задача решается неправильно — но даже если после долгих поисков кому-то и удастся предьявить задачу, которая на UTF-16/UTF-32 решается действительно удобнее — это не изменит того факта, что UTF-8 (или WTF-8) строки Rust'а лучше. Именно потому, что не провоцирует делать быстро, но неправильно.
В C++ полная свобода — всегда можно выбрать (или написать самому) реализацию, максимально соответствующую задаче.
Ага. А внешние библиотеки уже отменили? Да и редко кто способен выбрать правильное представление строки для задачи.

Давайте поговорим о супер-мега-важной «фиче» std::basic_string<char32_t>, раз уж вы так настаиваете.

А в Rust — как решить проблему вроде разбивания текста на строки по 80 символов?
О! Ну наконец-то какая-то конкретика. Отлично. Давайте сначала напишем код для ваших UTF-32 строк, а потом сравним с UTF-8 строками.

Я так понимаю вы терминальный эмулятор собрались сотворять?

Контрольный пример: строка содержащая сначала XYZT 10 раз, потом XYZT 20 раз. Разбить её хорошо бы посередине, правильно?

Ну так для этого в любом случае нужно пройтись по всей строке и посчитать длину — что в UTF-8, что в UTF-16, что в UTF-32!

Я пока не видел ни одной задачи, где так восхваляемые вами свойства UTF-16/UTF-32 строк позволили бы что-то сделать быстро и правильно.

В лучшем случае получается что-то быстрое, но работающее для русского и английского, но напрочь не работающее для арабского или японского (символы "X", "Y" и прочие к нам из Японии пришли, да).

Однако для этого вам вообще никакой Unicode не нужен — достаточно windows-1251 или даже KOI8-R.
Контрольный пример: строка содержащая сначала XYZT 10 раз, потом XYZT 20 раз. Разбить её хорошо бы посередине, правильно?

Неправильно.
Требуется банальный text wrap для Lister'а. Поэтому режется ровно после 80 символа.
Однако для этого вам вообще никакой Unicode не нужен — достаточно windows-1251 или даже KOI8-R.

И это замечательно, что в C++ можно перейти к ним и не мучаться с потенциальной переменной шириной символов.
В Rust'е же её нужно учитывать всегда, даже если у вас текст из A-Z.
Как показывает печальный пример SQL Injection'ов, чем меньше свободы в таких вещах, тем безопаснее и надёжнее результат.
И это замечательно, что в C++ можно перейти к ним и не мучаться с потенциальной переменной шириной символов.

В Rust'е же её нужно учитывать всегда, даже если у вас текст из A-Z.

Как показывает печальный пример SQL Injection'ов, чем меньше свободы в таких вещах, тем безопаснее и надёжнее результат.

Мне кажется, или вы сами себе противоречите?


Или вопрос в том, что вам нужно работать нативно с не-UTF кодировкой?
C-строки: https://doc.rust-lang.org/std/ffi/struct.CString.html, https://doc.rust-lang.org/std/ffi/struct.CStr.html
OS-строки: https://doc.rust-lang.org/std/ffi/struct.OsString.html, https://doc.rust-lang.org/std/ffi/struct.OsStr.html
Посмотрите, есть ли там то, что вам нужно.

Требуется банальный text wrap для Lister'а.
Ok.

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

Так почти вся иероглифика так устроена! Японцы с китайцами вас за такое не по головке точно не погладят!

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

Использование же готовых библиотек типа ICU и сохранённых процедур — позволят сохранить ваши волосы мягкими и шелковистыми… и даёт возможность спать по ночам, а не ловить 100500 ошибок в коде, который вроде как, «давно отлажен».

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

И если заказчик именно за это не платил, то лучше в принципе запретить такие вещи чем получать баг-репорты «у нас был ученик со странным именем и вся база данных накрылась» или думать «почему страница текста отрисовывается полсекунды?».
Вопрос не в баг-репортах, а в алгоритмах.

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

или думать «почему страница текста отрисовывается полсекунды».
А вот здесь, как раз, UTF-8 спасает. Ибо у вас нет соблазна делать 100500 раз вещи, которые, вообще говоря, при полной поддержке юникода требуют O(N), но конкретно у вас, кокретно у вас, конкретно в первой версии программы — укадываются в O(1).

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

То, что код на UTF-8 «ломается» в случае использования не только русского языка, но и даже каких-нибудь банальных ö или ü, если пользоваться индексом в строке как позицией на экране… это же прекрасно! Ошибки обнаружатся быстрее и быстрее же будут исправлены.

Та же идея, что и со статической типизацией, современным C++ и rust'ом вообще: чем раньше будет обнаружена ошибка — тем дешевле её исправить!

В UTF-16 же только эмодзи и спасают.
UFO just landed and posted this here

Если вам нужен прозрачный интероп с ОС, всегда можно
https://doc.rust-lang.org/std/ffi/struct.OsString.html и https://doc.rust-lang.org/std/ffi/struct.OsStr.html
Если вам не хватает std::string/std::wstring, то зря — принудительная юникодная локаль для всех стандартных строк это хорошо. Как вспомню танцы с ними на Windows — так вздрогну.

Увы и ах std::string/std::wstring не удовлетворяют условиям задачи.
принудительная юникодная локаль для всех стандартных строк это хорошо. Как вспомню танцы с ними на Windows — так вздрогну.

А что не так с Юникодом ыв Windows?

Тут я пожалуй слукавил, что проблема только в std::basic_string
Там несколько проблем, связанных конкретно с С++:


  1. std::string — в произвольной кодировке, зависящей от системы. При этом почти весь код принимает именно её.
  2. Ад <locale> и всего, что связано с ними
  3. UTF-8 как локаль для консоли не работает по-моему до сих пор. Но утверждать не буду.
UTF-8 как локаль для консоли не работает по-моему до сих пор. Но утверждать не буду.
Кстати, в Microsoft рассматривают вариант обучения рантайма работать в UTF-8. И вы можете поставить свой голос там (и тут тоже). Идеальным был бы вариант, если бы ещё и все *A функции из Win32 API научились бы понимать UTF-8 как одну из локалей (которую программа выбирает сама себе при загрузке, например). К сожалению, сильно надеяться на такой вариант расширения Win32 API не приходится.
Да. Про них. Если работать только с ASCII/UTF-8, да, никаких проблем. Но что делать если надо будет работать с UTF-16/UTF-32 (да еще и с разными порядками байтов)?

Вам это приходится делать прямо в условной бизнес-логике, "по ходу пьесы"?

У вас кажется неэквивалентный способ чтения из stdin int-ов (в binary_search смотрел). В rust вы читаете сразу все в большой буффер, потом парсите его. В C++ вы читаете по одному, используя способности форматированного ввода std::istream. Это заведомо медленнее

Согласен. Когда-то экспериментировал с чтением файла 8мб размером:


  • чтение по 2 байта ~ 400 мсек
  • чтение по 2кб ~ 9 мсек
За неумелые бенчмарки пора уже бить по голове, чтобы неповадно было.
По rust есть еще очень хорошее видео от дропбокса.
UFO just landed and posted this here
Просто убили тесты длительностью в сотые доли секунды.
При таких мизерных временах работы может столько всего мешающего вылезать, что ни о каком реальном сравнении и речи быть не может.
UFO just landed and posted this here
Наверное, вы не так поняли. Задача называется binary_search и состоит из ввода/вывода + сортировку массива A + поиск значений из массива B в массиве A. В таблице честно указана асимптотика задачи O(N logN).

Хотя нехорошо, что сортировка, занимающая O(N logN), может занимать больше времение, чем основная деятельность программы, описанная в ее названии (тут мы имеем O(M logN), где M — кол-во элементов в массиве B). Возможно, в следующей ревизии статьи и кода, я буду подавать на вход программы уже отсортированные данные.
Или же можно сделать в тестах M >> N

Автору не помешает послушать выступление Алексея Шипилева, осознать всю бренность бытия, а заодно и то, что большинство людей, которые пытаются сравнивать производительность, на самом деле сравнивают что-то типа глубины с температурой… И в общем больше не заниматься глупостями.
Там есть специальная профессия мерять производительность, называется Performance Engineer.

Вы так говорите, будто эти ваши «Performance Engineer» появляются из Великого Ничто по указанию обученного волшебника, делают свою работу, а потом туда же исчезают.
ИТ — это про науку, а не волшебство. Человек что-то сделал — ну и хорошо. Желаете улучшить — код открыт. Чего же боле?
Мне интересно, зачем в памяти держать именно в виде UTF8?
WideChar оптимален для этих целей, а конвертить в UTF8 можно при необходимости, вроде записи в файл.

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


  • Компактней чем UTF-16. Где-то натыкался, что одна и та же статья на японской вики в UTF-8 занимает 83Кб, а в UTF-16 144Кб. А составные символы что там, что там будут.
  • Совместима со стандартным ASCII по представлению. И там, и там байты.
  • Совместима со стандартным ASCII по кодированию. Нижняя половина таблицы ASCII в UTF-8 выглядит точно так же.
  • Не зависит от порядка байт. Может быть проблемой при передаче по сети.
Не вижу причин, почему UTF-16 мог бы быть оптимальнее. С первыми версиями стандарта Unicode казалось, что UTF-16 удобнее, потому что один кодпоинт гарантированно занимал 2 байта. Можно было легко получить длину строки из количества занимаемых ей байт, нельзя было случайно обрезать строку посреди символа. Но так как теперь один кодпоинт в UTF-16 может занимать и 4 байта, то эти преимущества исчезают. Зато появляется зависимость от порядка байт и повышенный расход памяти.
Кстати, а почему максимально возможный кодпоинт возрос до 4 байтов? Новых символов добавили или в чём-то другом причина?
Ну да, 65535 кодпоинов оказалось маловато. В последнем стандарте уже 136755 символов. Текущая схема кодирования UTF-16 позволяет использовать до 1112064 кодпоинтов.
ru.wikipedia.org/wiki/Юникод
41 эмодзи, а также пять символов изменения цвета кожи для эмотиконов
По-моему, юникод забрёл куда-то не туда.
По-моему, юникод забрёл куда-то не туда.
Туда-туда. Эмодзи эти использовались почти во всех популярных мессенджерах ещё до появления в Юникоде — и отказ от этих костылей в пользу стандарта можно только приветствовать…

P.S. На самом деле там одних иероглифов столько, что в два байта всё не влазит. Эмодзи же оказались первыми действительно распространёнными символами, которые потребовали исправить-таки программы, которые трактуют UTF-16 как UCS-2. А символы для изменения цвета кожи заставили-таки отказаться от иллюзии, что одна буква — это один code point. Спасиба им хотя бы за это…
Rust поддерживает итераторы (как генераторы в Python).

Не как генераторы, а как итераторы в Python. Генератор помнит контекст выполнения, а итератор нет. Генераторов в Rust вроде бы нет.


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

Возможно имелись какие-то конкретные функции, которые возвращают Option, но вообще заменой исключениям в большинстве случаев являются https://doc.rust-lang.org/std/result/ .

Я если что даже вторую версию книги по Rust не дочитал до конца. Для тех кому лень смотреть по ссылке в Rust есть экспериментальные API для создания генераторов. Лень разбираться, но похоже удобства как в питоне, где есть специальный синтаксис (yield) не предвидится. Если ошибаюсь, поправьте.

Выдержка по ссылке:


    let mut generator = || {
        yield 1;
        return "foo"
    };

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

Возможно имелись какие-то конкретные функции, которые возвращают Option, но вообще заменой исключениям в большинстве случаев являются https://doc.rust-lang.org/std/result/ .

Тоже бросилось в глаза, но вы уже написали про это.

Автор, спасибо за вторую порцию тестов!

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

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


Правда, выбранные решения заставляют рассматривать Rust скорее не как язык на котором будут использоваться библиотеки на Фортране, а как язык на которым было бы неплохо писать новые библиотеки. Тут у него больше преимуществ.

Угу. Будем ждать реализованных крупных проектов для вычислительных задач.
А можно в сравнение добавить clang? Интересно, в среднем Rust проигрывает из-за более высокого уровня языка или из-за более слабой оптимизации llvm. По идее, генерируемый Rust код должен лучше поддаваться оптимизации.
С последней версией (1.32), Rust на моем ноуте либо выигрывал, либо слегка отставал на всех тестах, кроме задачи «Кодирования Хаффмана». На ней Rust проигрывал плюсам аж в 2 раза. Однако, после замены HashMap на FxHashMap (фактически я изменил только хешер) и включения opt-level = 3, производительность выросла в 4 раза, и теперь стала отставать в 2 раза уже реализация на C++. Так что многое зависит от используемых алгоритмов.
Время — штука сложная, с ней надо осторожно работать. Сразу скажу, что в код я не вникал, а зачем я это все спрашиваю — написано вконце.

1. Можно ли узнать, каким именно образом измерялось время выполнения функций? Какая гранулярность вашего таймера?

Не было ли миграции потока на другое ядро во время выполнения функции, делалось ли thread affinity?

На основании какого источника времени (tsc? hpet?) принималось решение о текущем моменте времени (cat /sys/devices/system/clocksource/clocksource0/current_clocksource)?
Если это tsc, то поддерживает ли проц функции:

constant_tsc — все tsc ядер работают на одной частоте (подробности можно найти в intel's Designer's vol3b). По сути, получается, когда этой фишки нет, то tsc бесполезен, т.к. разные ядра будут жить с разной скоростью.

nonstop_tsc — фишка, которая добавлена в микроархитектуре Nehalem и старше. Означает, что часы не приостанавливаются в ACPI P-,C-,T-состояниях (Cx-режимы сна, Px-режимы частоты/напряжения, Tx — режимы пропуска тактов). В intel's designer's vol3b эта фишка называется «Invariant TSC».

rdtscp (cat /proc/cpuinfo | grep flags) — наличие специальной инструкции для правильного чтения регистра tsc.

И правильно ли снимались показания счетчика времени (наличие rdtcsp в системе команд еще не означает, что мы этой командой пользуемся. rdtsc выполняется на общем конвеере команд, в то время как rdtcsp обеспечивает hb)?

2. работа с данными — штука, которая нагружает определенным образом подсистему памяти. Если меряешь время алгоритмов, которые перелопачивают массивы, то нужно мерять разные длины массивов. Потому, что из за эффектов с кэшом, решающую роль могут оказывать совсем не те эффекты, которые были при тестах с другим набором или размером данных.
Сложность так же вызывает то, что эффекты с кэшом сильно зависят от того, как именно в памяти лежат данные. Так, известно, что перемножение матриц 512х512 работает медленней, чем 513х513. Если не понимаешь, почему так — лучше сразу признать, что правильно мерять производительность ты не умеешь. Можно так же нарваться на коллизии адресов TLB страниц — и этим просадить производительность. Маппинг физического адреса в логический хранится в хардварной хэшмапе!

Это все что я насобирал, просто чтобы понимать, что задача не такая простая, как кажется на первый взгляд. Мне очевидно, что список подводных камней при измерении производительности, который я знаю, не является полным, и скорей всего я даже не обозначил 5% того, о чем нужно знать, когда это меряешь. В мире не так много людей, которые могут правильно все измерять, и правильно это сравнить. Мы с вами не входим в число этих людей. Поэтому, скорей всего, измеренные результаты не будут отражать именно то, что мы думали что измеряли. Особенно, если алгоритм выполнялся меньше нескольких секунд (и несколько прогонов алгоритма не всегда могут быть решением проблемы). Какие-то более сложные вещи, которые выполняются больше 1..2сек уже можно более-менее корректно сравнивать без учета большого кол-ва тонкостей и хитрых эффектов.

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

Есть утилита perf. Можно ее погонять на разных тестах и сравнить показания счетчиков. Если счетчики будут сильно разными — это подозрение, что реализации алгоритмов не идентичны. Скорей всего, если приложить усилия, более медленный код можно как-то переписать, чтобы он был равен быстрому. Но это нужно «закапываться» в вопрос.
1. Можно ли узнать, каким именно образом измерялось время выполнения функций? Какая гранулярность вашего таймера?

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

Не было ли миграции потока на другое ядро во время выполнения функции, делалось ли thread affinity?

Зачем? Сколько бы там их ни было — это копейки на фоне десятых долей секунды.

Если это tsc, то поддерживает ли проц функции:

Там написано какой проц.

И правильно ли снимались показания счетчика времени (наличие rdtcsp в системе команд еще не означает, что мы этой командой пользуемся. rdtsc выполняется на общем конвеере команд, в то время как rdtcsp обеспечивает hb)?

Абсолютно не имеет значение. Длинна конвейера ну десятки тысяч тактов, а измеряем мы миллиарды тактов.

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

Из этого, ровным счётом, ничего не следует.

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

Это итак делается.

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

Какие такие эффекты с кешом? Каким образом увеличение датасета повлияет на кеш? Если у нас есть решение А и решение Б, которое обладают определённым паттерном обращения к памяти, то увеличение датасета паттерн не поменяет. Они могут в разное время, с увеличением датасета, вылезти за какой-то левел кеша/памяти, но из этого ровным счётом ничего не следует — всё это сгладиться.

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

Абсолютно неважно то, как они там лежат. Важно то, как к ним обращаются, а это уже следствие того, как они лежат. Да и из этого так же ничего не следует — если решение А лежит криво, то на каком угодно датасете оно будет криво.

Так, известно, что перемножение матриц 512х512 работает медленней, чем 513х513.

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

Можно так же нарваться на коллизии адресов TLB страниц — и этим просадить производительность.

Показать сможете?

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

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

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

Особенно, если алгоритм выполнялся меньше нескольких секунд (и несколько прогонов алгоритма не всегда могут быть решением проблемы). Какие-то более сложные вещи, которые выполняются больше 1..2сек уже можно более-менее корректно сравнивать без учета большого кол-ва тонкостей и хитрых эффектов.

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

Вы рассуждали о каких-то там кешах, но сейчас показали даже не 5%, а 0. Если у нас датасет один и тот же — абсолютно неважно сколько секунд мы его долбим. Хоть час — это ничего не изменит. Время работы может быть следствием увеличением датасета, но не всегда и тут вам просто повезло.

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

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

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

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

Существует множество ручек, которые можно крутить. Начиная от организации структур данных, свободную организацию которых — позволяют единицы языков. Заканчивая всяким nt. И то, это только относительно летенси-цепочек.

Ручек же для трупута ещё больше — там уже есть параллельность. Это не говоря о том, что данные надо не только читать.

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

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

Зачем? Сколько бы там их ни было — это копейки на фоне десятых долей секунды.

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

В тот то и дело, что наоборот.

Иллюстрации про кэш: igoro.com/archive/gallery-of-processor-cache-effects
Не всегда — кэш сбрасывается.

И? Уже после выявляются новые обстоятельства? Пусть он хоть 10раз сбрасывается — это ничего не изменит.

В тот то и дело, что наоборот.

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

А по поводу матриц, кто-то спастив число из статьи очень сильно себя подставил, ведь он не дочитал до:
Why do the vertical lines stop at 4MB array length?

И вот если бы он дочитал, а потом посчитал, что 512х512 даже из даблов — это в 2раза менее 4мб, то он бы понял, что просто так пастить цифры их статьи глупо — можно попасть в лужу. Он в неё и попал.

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

Да и к тому же, есть транспонирование.
Как говорится, «не прошло и пол-года» :) Автору респект за статью, это по-любому +1 в жизненную карму. Но, увы, статья необъективна совсем. Вы наверное возмутитесь, мол как-так, там же тесты! Много, доступно каждому, гит-хаб, тру-ля-ля…

Но я предлагаю сперва задуматься над понятием «сравнение языков программирования». Тут же не так все просто! В разрезе данной статьи сравнение напоминает, IMХО, кусочек знаменитой рифмы:

«Смешались в кучу кони, люди,
И залпы тысячи орудий
Слились в протяжный вой...»

Почему? Отвечу… первое, что бросилось в глаза — то, что автор только спустя «пару итераций» осознал, что ввод/вывод и предварительная подготовка данных не должны участвовать в замерах. Ввод-вывод это вообще «белый шум», а «подготовка данных» — это отдельно тестируемый алгоритм.

Но дело не в этом. Дальше-больше… Что значит «Rust vs C++ на алгоритмах»? Автор забывает, что:

  1. Есть разные компиляторы и линкеры (со своими особенностями) относительно вендоров по оптимизации «по-умолчанию»
  2. Должны быть подобраны цели и максимальные к ним флаги оптимизации. И не просто по документации, а по ряду тестов — с выбором лучшего. Интуристы часто врут!
  3. Есть разная реализация Стандартных Либ. На счет Раста — не скажу (сам пока только довольный читатель), а вот для С++ видел три независимых, от разных вендоров. Собственно, мы что тестируем? «Мой тулчейн» и «Мою Стандартную Либу» для топика? Так это же те самые «кони-люди»


РЕЗЮМЕ: Тест компилятора и тест Стандартной Либы — нужно разносить! Не нравится компилятор — пиши разрабам телегу со слезами. Не устраивает быстродействие какой-то части Стандартной Либы — сделай лучше и напиши разрабам.

Автору статьи — за попытку пять! Такие, как ты — нужны человечеству.
Sign up to leave a comment.

Articles