LINUX.ORG.RU

Запилите кто-нибудь нормальный обзор Rust

 , ,


6

9

Коллеги, а не мог бы кто-нибудь из вас запилить нормальное сравнение Rust с плюсами? В последнее время rust то и дело упоминают как будущего убийцу с++, вот мне и стало интересно. Но изучать новый язык у меня сейчас времени нет, а все обзоры и сравнения (вот последнее на хабре: http://habrahabr.ru/post/225507/) сводятся к следующему:

Возьмем пример стандартного кода на с++

$ cat test.cpp
int main()
{
    *((int*)nullptr) = 0xdeadbeef;
}
Давайте его запустим, и посмотрим, что получится:
$ g++ -std=c++0x -o testcpp test.cpp && ./testcpp
Segmentation fault
Как видите, с++ позволяет выстрелить себе в ногу!
А теперь давайте посмотрим, что будет, если этот же код попытаться скомпилировать rust:
rust -o testrust test.cpp
test.cpp:1:1: 1:4 error: expected item but found `int`
test.cpp:1 int main()
           ^~~
Смотрите, компилятор rust не скомпилировал этот код и сохранил нам ногу! ergo, rust - убийца с++.

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

Если где-то есть уже что-то подобное в сети, киньте ссылку.

UPD: нашел очень качественное сравнение с++ и go (http://kidoman.io/programming/go-getter.html).
tl;dr: товарищ сравнивал производительность, в качестве демонстрационной программы использовал трассировщик лучей. В первой серии go победил после множества оптимизацй, во второй серии с++ после таких же оптимизаций одолел go на одном ядре, в третьей серии в с++ впилили многопоточность, и он разорвал go пополам.
Стоит обратить внимание, что рейтрейсер на c++ в этом примере написан без единого new/delete.
Буду очень признателен, если кто-то напишет подобное сравнение с++ с rust, а еще лучше - если портирует трассировщик из примера выше на rust о объяснит, почему он лучше (там уже на несколько других языков портировали).



Последнее исправление: ymn (всего исправлений: 3)
Ответ на: комментарий от tailgunner

В обосновании. Каким-то клоунам не понравилось, что ~ отсутствует на каких-то европейских клавах

Мля, серьезно чтоль? я что-то упустил сей момент в развитии языка. Европейские программеры не умеют в деструкторы в С++?

anonymous
()
Ответ на: комментарий от anonymous

Европейские программеры не умеют в деструкторы в С++?

~ в Rust была значком owned pointer, а не деструктора. Аналог деструкторов остался.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

я не про то. я про символ ~ который якобы отсутсвует. Как они деструкторы то писали?

anonymous
()
Ответ на: комментарий от anonymous

Европейские программеры не умеют в деструкторы в С++?

А так же деструкторы C#, D. И заодно «битовое нет» в просто огромной куче языков. Так что аргумент вообще дурацки смотрится. Я бы ещё как-то понял, если бы они так за «читабельность» боролись...

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Я бы ещё как-то понял, если бы они так за «читабельность» боролись...

Этот аргумент тоже был - якобы Rust слишком «sigil-heavy». Правда, я не вижу, чем Box<SomeType> читабельнее, чем ~SomeType. Особенно, если учесть, что Box<> - это на самом деле BoxPtr<>.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

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

А что у Ruby с читаемостью не так?

theNamelessOne ★★★★★
()
Ответ на: комментарий от intelfx

избыточных, нерелевантных решению задачи символов

очевидный фикс

anonymous
()
Ответ на: комментарий от tailgunner

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

anonymous
()
Ответ на: комментарий от anonymous

Это был не хвостострел! Это авторский коллектив rust'а предал наши идеалы, братюнь!

anonymous
()
Ответ на: комментарий от tailgunner

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

Впрочем, ты привлек в тему кого-то возможно полезного.

anonymous
()
Ответ на: комментарий от anonymous

А ты вон полтора года следишь за языком и хз написал ли даже Hello World на нем

Ты слишком буквально понял «не пишу».

Мне вот интересно кроме пхп то ты на чем то писал?

Хм... у царя тоже нездоровая фиксация на PHP. Или ты - царь, принявший таблетки?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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

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

anonymous
()
Ответ на: комментарий от anonymous

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

По работе писал на PL/I, Си, Си++, REXX, VBA, Java, Python, shell, паре-тройке ассемблеров. JFF писал хелловорлды разной степени серьезности на... да всего уже и не упомню. На чем зафиксируешься? %)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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

anonymous
()
Ответ на: комментарий от anonymous

Путём системного вызова clone() (man 2 clone), если мы про ассемблер в Linux. Если же про bare metal, то многопроцессорность реализуется путём APIC и IPI (межпроцессорных прерываний), а многопоточность — посредством написания кода, перекидывающего управление между потоками по таймеру.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от anonymous

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

Не понял вопроса.

tailgunner ★★★★★
()
Ответ на: комментарий от intelfx

А можно как нибудь совсем примитивно на псевдоасм коде (мне реально очень интересно, но я асм совсем не знаю)?

Управление потоками по таймеру на 1 ядре в принципе понятно, не понятно когда ядер больше одного.

anonymous
()
Ответ на: комментарий от anonymous

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

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

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

anonymous
()
Ответ на: комментарий от anonymous

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

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

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

Да.

Если речь о юзерспейсе - вообще-то нет. clone работает как fork - выполнение продолжается от точки возврата.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Не помню, надо смотреть, как эти самые IPI работают. Ты же спрашиваешь про управление железом напрямую, без ОС?

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

Я сказал что не знаю ассемблеров и в целом представляю его как низкоуровневый си (который я тоже знаю неважно). Так что любой, но лучше псевдокодом с комментариями.

anonymous
()
Ответ на: комментарий от intelfx

Поэтому я и написал «если». Впрочем, в случае bare metal «кидаем и оно исполняет» - тоже слишком упрощенно, если речь не об учебной ОС.

tailgunner ★★★★★
()
Ответ на: комментарий от intelfx

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

anonymous
()
Ответ на: комментарий от anonymous

Чувак, ну это уже совсем оффтопик. Я, конечно, понимаю, что вы тут уже три дня пируете и все такое. Но надо же хоть немного совесть иметь.

ddos3
() автор топика
Ответ на: комментарий от ddos3

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

anonymous
()
Ответ на: комментарий от anonymous

В двух словах, чтобы закрыть эту тему.

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

ddos3
() автор топика
Ответ на: комментарий от anonymous

Про то, как происходит штатная работа, ddos3 уже написал.

А вот запуск всех ядер, кроме первого, в момент старта ОС происходит именно так, как я описал: ОС с первого ядра «пинает» все остальные посредством IPI и говорит им «проснись и начни выполнять код функции ожидания» (что-то вроде вечного цикла из HLT). Потом этим ядрам начинают приходить таймерные прерывания, в табличке потоков появляются потоки и так далее.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от intelfx

Что реально тупо общая очередь из которой ядра дергают потоки?

И еще вопрос, встречал информацию что та же Windows не может исполнять очень много процессов, если да то почему?

anonymous
()
Ответ на: комментарий от anonymous

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

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

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

anonymous
()
Ответ на: комментарий от anonymous

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

intelfx ★★★★★
()
Ответ на: комментарий от anonymous

Смотря как передавать.

Обычно используются всякие механизмы передачи данных через ядро ОС, а любое обращение к ОС требует переключения процессора в «режим ОС» и смены настроек MMU. А потом при возврате из кода ОС нужно все вернуть обратно. Это дорого.
Передача данных из юзер-спейса в кернел-спейс требует их копирования из памяти процесса в память ядра, а передача второму процессу требует копирования из памяти ядра обратно в память процесса. Еще расходы.
Иногда процессы общаются вообще через сетевой стек, что еще дороже.

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

ddos3
() автор топика
Ответ на: комментарий от ddos3

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

anonymous
()
Ответ на: комментарий от anonymous

Чтение/запись в общую память работает так же быстро, как и доступ к приватной памяти процесса.

ddos3
() автор топика
24 июля 2014 г.
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.