Rust
. Прочитав Rustbook
, изучив код некоторых популярных проектов, я решил своими руками попробовать этот язык программирования и своими глазами оценить его преимущества и недостатки, его производительность и эко-систему.Язык
Rust
позиционирует себя, как язык системного программирования, поэтому основным его vis-a-vis следует называть C/C++
. Сравнивать же молодой и мультипарадигмальный Rust
, который поддерживает множество современных конструкций программирования (таких, как итераторы, RAII
и др.) с «голым» C
я считаю не правильно. Поэтому в данной статье речь пойдет об сравнении с C++
.Чтобы сравнить код и производительность
Rust
и C++, я взял ряд алгоритмических задач, которые нашел в онлайн курсах по программированию и алгоритмам.Статья построена следующим образом: в первой части я опишу основные плюсы и минусы, на которые я обратил внимание, работая с
Rust
. Во второй части я приведу краткое описание алгоритмических задач, которые были решены в Rust
и C++, прокомментирую основные моменты реализации программ. В третьей части будет приведена таблица замера производительности программ на Rust
и C++.Данная статья достаточно субъективная. Даже сравнение производительности программ, которые делают одинаковые вещи, но написаны на разных языках, подвержены авторскому подходу. Поэтому я не претендую на объективные замеры, и, чтобы не навязывать свои результаты, предлагаю всем ознакомится с кодом программ и с подходом к замеру производительности. Код выложен на github. Буду рад любой критике и замечаниям. Начнем.
Что плохого и хорошего в Rust
+ Разработчики
Rust
поставляют свой компилятор уже с «батарейками внутри» тут есть: компилятор, менеджер пакетов (он же сборщик проектов, он же отвечает за запуск тестов), генератор документации и отладчик gdb
. Исходный код на Rust
может включать в себя сразу тесты и документацию, и чтобы собрать все это не требуется дополнительных программ или библиотек. +Компилятор строг к тексту программы, который подается ему на вход: в его выводе можно увидеть какой код не используется, какие переменные можно изменить на константный тип, и даже предупреждения, связанные со стилем программирования. Часто для ошибок компиляции приведены варианты ее устранения, а ошибки при инстанциировании обобщенного кода (шаблонов) лаконичны и понятны (привет ошибкам с шаблонами
STL
в C++
).+ При присваивании или передачи аргументов по умолчанию работает семантика перемещения (аналог
std::move
, но не совсем). Если функция принимает ссылку на объект необходимо явно взять адрес (символ &
, как в C++
).+ Все строки — это
юникод в кодировке UTF-8
. Да, для подсчета количества символов нужно О(N)
операций, но зато никакого зоопарка с кодировками.+ Есть поддержка итераторов и замыканий (лямбда функций). Благодаря этому можно писать однострочные конструкции, которые выполняют множество операций с сложной логикой (то, чем славится
Python
).+-В
Rust
отсутствуют исключения, обработку ошибок необходимо делать после вызова каждой функции. Причем Rust
не позволит получить возвращаемое значение, если вы не обработаете случай неуспешного завершения функции. Есть макросы и встроенные конструкции языка, которые позволяют упростить эту обработку и не сильно раздувать код проверками.- Нужно писать программы так, чтобы компилятор (точнее borrow checker) поверил, что вы не делаете ничего плохого с памятью (или с ресурсами в целом). Часто это работает как надо, но иногда приходится писать код в несколько хитрой форме только для того, что бы удовлетворить условиям borrow checker'а.
- В
Rust
нет классов, но есть типажи, на основе которых можно построить объектно ориентированную программу. Но скопировать реализацию с полиморфными классами в C++
в язык Rust
напрямую не получиться.-
Rust
достаточно молод. Мало полезного материала в сети, на StackOverflow. Мало хороших библиотек. Например, нет библиотек для построения GUI, нет портов wxWidgets
, Qt
.Алгоритмические задачи на Rust
Бинарный поиск
Нужно для для каждого значения из вектора B найти его позицию в векторе A. По сути нужно применить n раз бинарный поиск, где n — кол-во элементов в B (массив A предварительно отсортирован). Поэтому тут я приведу функцию бинарного поиска.
// return position of the element if found
fn binary_search(vec: &[u32], value: u32) -> Option<usize> {
let mut l: i32 = 0;
let mut r: i32 = vec.len() as i32 - 1;
while l <= r {
let i = ((l + r) / 2) as usize;
if vec[i] == value {
return Some(i);
} else if vec[i] > value {
r = i as i32 - 1;
} else if vec[i] < value {
l = i as i32 + 1;
}
}
None
}
Кто первый раз видит
Rust
, обратите внимание на пару особенностей:- Тип возвращаемого значения указывается в конце объявления функции
- Если переменная изменяемая, то нужно указывать модификатор
mut
Rust
не переводит типы неявно, даже числовые. Поэтому нужно писать явный перевод типаl = i as i32 + 1
Сортировка слиянием
Формально задача звучит так: для заданного массива подсчитать кол-во перестановок, необходимых для сортировки массива. По факту нам требуется реализовать сортировку слиянием, подсчитывая по ходу длину перестановок элементов.
Давайте рассмотрим код чтения массива с
stdin
fn read_line() -> String {
let mut line = String::new();
io::stdin().read_line(&mut line).unwrap();
line.trim().to_string()
}
fn main() {
// 1. Read the array
let n: usize = read_line().parse().unwrap();
let mut a_vec: Vec<u32> = vec![0; n as usize];
for (i, token) in read_line().split_whitespace().enumerate() {
a_vec[i] = token.parse().unwrap();
}
...
- У классов нет конструкторов, но можно делать статические методы-фабрики, которые возвращают объекты классов, как
String::new()
выше. - Функции, которые могут ничего не вернуть, возвращают объект
Option
, который содержитNone
или результат корректного завершения функции. Методunwrap
позволяет получить результат или вызываетpanic!
, если вернулсяNone
. - Метод
String::parse
парсит строку в тип возвращаемого значения, т.е. происходит вывод типа по возвращаемому значению. Rust
поддерживает итераторы (как генераторы в Python). Связкаsplit_whitespace().enumerate()
генерирует итератор, который лениво читает следующий токен и инкрементирует счетчик.
Приведу сначала неправильную сигнатуру вызова функции
_merge
, которая сливает in place два отсортированных подмассива.fn _merge(left_slice: &mut [u32], right_slice: &mut [u32]) -> u64
Данная конструкция не взлетит в
Rust
без unsafe
кода, т.к. тут мы передаем два изменяемых подмассива, которые располагаются в исходном массиве. Система типов в Rust
не позволяет иметь две изменяемых переменных на один объект (мы знаем, что подмассивы не пересекаются по памяти, но компилятор — нет). Вместо этого надо использовать такую сигнатуру:fn _merge(vec: &mut [u32], left: usize, mid: usize, right: usize) -> u64
Кодирование Хаффмана
Для заданной строки нужно построить беспрефиксный код и вывести закодированное сообщение. Для данной задачи нужно построить дерево на основе частотных характеристик символов в исходном сообщении. Построение деревьев, списков, графов на
Rust
— задачи не из простых, т.к., например, в случае двусвязного списка нам необходимо иметь два mut
указателей на один нод, а это запрещено системой типов. Поэтому эффективно написать двусвязный список без unsafe
кода не получится. В данной задаче мы имеем однонаправленное дерево, поэтому эта особенность нас не коснется, но есть свои сложности реализации.Заведем класс Node:
// Type of the reference to the node
type Link = Option<Box<Node>>;
// Huffman tree node struct
#[derive(Debug,Eq)]
struct Node {
freq: u32,
letter: char,
left: Link,
right: Link,
}
impl Ord for Node {
// reverse order to make Min-Heap
fn cmp(&self, b: &Self) -> Ordering {
b.freq.cmp(&self.freq)
}
}
- Можем использовать типы до их описания.
- Тут можно видеть странный синтаксис наследования
#[derive(Debug,Eq)]
.Debug
— поддерживаем форматированную печать объекта по-умолчанию.Eq
— определяем операцию сравнения на равенство. - Для Node определяется типаж сравнения на больше/меньше
Ord
. Типажи позволяют расширять возможности объектов. В частности, здесь мы сможем использоватьNode
для хранения Min-куче.
Метод для посещения нод дерева сверху вниз и для составления таблицы кодирования.
impl Node {
...
// traverse tree building letter->code `map`
fn build_map(&self, map: &mut HashMap<char, String>, prefix: String) {
match self.right {
Some(ref leaf) => leaf.build_map(map, prefix.clone() + "1"),
_ => { },
}
match self.left {
Some(ref leaf) => { leaf.build_map(map, prefix + "0"); },
_ => { map.insert(self.letter, prefix); },
}
}
}
- Рекурсивно вызывает ветки бинарного дерева, если их указатель не пустой
&Some(ref leaf)
. - Конструкция
match
похожа наswitch
вC
.match
должен обработать все варианты, поэтому тут присутсвует_ => { }
. - Помните про семантику перемещения по-умолчанию? Поэтому нам нужно писать
prefix.clone()
, чтобы в каждую ветвь дерева передалась своя строка.
Декодирование Хаффмана
Обратная задача: для известной таблицы кодирования и закодированной строки получить исходное сообщение. Для декодирования удобно пользоваться бинарным деревом кодирования, поэтому в программе нам нужно из таблицы кодирования получить дерево декодирования. На словах задача простая: нужно перемещаться вниз по дереву (0 — влево, 1 — вправо), создавая промежуточные узлы при необходимости, и в листья дерева помещать символ исходного сообщения. Но для
Rust
задача оказалась сложная, ведь нам нужно перемещаться по изменяемым ссылкам, создавать объекты, и при этом избегать ситуации, когда объектом владеет более одной переменной. Код функции заполнения бинарного дерева:fn add_letter(root: &mut Link, letter: char, code: &str) {
let mut p: &mut Node = root.as_mut().unwrap();
for c in code.chars() {
p = match {p} {
&mut Node {left: Some(ref mut node), ..} if c == '0' => {
node
},
&mut Node {left: ref mut opt @ None, ..} if c == '0' => {
*opt = Node::root();
opt.as_mut().unwrap()
},
&mut Node {right: Some(ref mut node), ..} if c == '1' => {
node
},
&mut Node {right: ref mut opt @ None, ..} if c == '1' => {
*opt = Node::root();
opt.as_mut().unwrap()
},
_ => { panic!("error"); }
}
}
p.letter = letter;
}
- Тут
match
используется для сравнения структуры переменнойp
.&mut Node {left: Some(ref mut node), ..} if c == '0'
означает «еслиp
это изменяемая ссылка на объектNode
у, которого полеleft
указывает на существующийnode
и при этом символc
равен '0'». - В
Rust
нет исключений, поэтомуpanic!("...")
раскрутит стек и остановит программу (или поток).
Расстояние Левенштейна
Нужно для двух строк посчитать расстояние Левенштейна — кол-во действий редактирования строк, чтобы из одной получить другую. Код функции:
fn get_levenshtein_distance(str1: &str, str2: &str) -> u32 {
let n = str1.len() + 1;
let m = str2.len() + 1;
// compute 2D indexes into flat 1D index
let ind = |i, j| i * m + j;
let mut vec_d: Vec<u32> = vec![0; n * m];
for i in 0..n {
vec_d[ind(i, 0)] = i as u32;
}
for j in 0..m {
vec_d[ind(0, j)] = j as u32;
}
for (i, c1) in str1.chars().enumerate() {
for (j, c2) in str2.chars().enumerate() {
let c = if c1 == c2 {0} else {1};
vec_d[ind(i + 1, j + 1)] = min( min( vec_d[ind(i, j + 1)] + 1
, vec_d[ind(i + 1, j)] + 1
)
, vec_d[ind(i, j)] + c
);
}
}
return vec_d[ind(n - 1, m - 1)];
}
str1: &str
— это срез строки. Легковесный объект, который указывает на строку в памяти, аналогstd::string_view
C++17.let ind = |i, j| i * m + j;
— такой конструкцией определяется лямбда функция.
Замеры производительности Rust vs C++
В конце, как обещал, прикладываю таблицу сравнения времени работы программ, описанных выше. Запуск производился на современной рабочей станции Intel Core i7-4770, 16GB DDR3, SSD, Linux Mint 18.1 64-bit. Использовались компиляторы:
[>] rustc --version
rustc 1.22.1 (* 2017-11-22)
[>] g++ -v
...
gcc version 7.2.0
Пару замечаний по результатам:
- Измерялось полное время работы программы, в которое включено чтение данных, полезные действия и вывод в
/dev/null
- В * (core) измерялось только время работы алгоритмической части (без ввода/вывода)
- Делалось 10 прогонов каждой задачи на каждом наборе данных, далее результаты усреднялись
- Все скрипты, производящие компиляцию, подготовку тестовых данных и замеры производительности, представлены в репозитории. К ним есть описание.
- По моему мнению, на ряде задач
C++
проигрывает из-за библиотеки потокового чтения/записиiostream
. Но эту гипотезу еще предстоит проверить.И да, в коде естьstd::sync_with_stdio(false)
- По моему мнению,
Rust
сильно проигрывает в тестеHuffman encoding
по причинемедленных хешей в HashMap
- На полном времени выполнения задач
Rust
показал, что по производительности он не уступаетC++
. В каждом языке есть свои особенность реализации стандартной библиотеки, которые сказывают на скорости работы задач. - На замерах только алгоритмической части,
Rust
проигрывает порядка 10%, но, думаю ситуация бы исправилась, если будем использовать хэши побыстрее в первой задаче.
Заключение
Повторюсь, что статья субъективна, и признана в первую очередь оценить в грубом масштабе, где находится
Rust
по отношению к C++
. Если у вас есть пожелания, идеи или замечания, которые позволят добавить статье объективности, пишите в комментарии.Вы можете ознакомиться со всеми кодами, необходимыми для повторения замеров на github: https://github.com/dmitryikh/rust-vs-cpp-bench.
Спасибо, что дочитали до конца!
Обновление от 11.12.2017
Спасибо за вашу работу в комментариях!
Я учел ряд ваших замечаний:
- Исправил функцию
binary_search
. Теперь она возвращаетOption
- Задача
Binary_search
теперь принимает на вход отсортированный массив A, поэтому во время работы теперь не входит сортировка A - Добавил
-DNDEBUG
при компиляцииC++
. Посыпаю голову пеплом.. - Исправлена грубая ошибка в задаче
Huffman_decoding
, из-за который программа на C++ ДВАЖДЫ декодировала строку. Прошу прощения за это. - Добавлен замер времени выполнения только алгоритмической части, без учета операций ввода-вывода и загрузки программы.
Теперь ситуация стала более объективной. Но в целом, оба языка держаться в поле ±10% на данных задачах.
Комментарии (201)
VioletGiraffe
09.12.2017 11:57Без аналогичного кода на С++, с которым проводилось сравнение, довольно бессмысленная статья. Предполагаю, что раз есть такая большая разница, программы написаны коряво. Оба языка компилируемые, и оптимизатор С++ уж точно не хуже Rust. Кстати, с какими флагами компилировался код на С++?
dmitryikh Автор
09.12.2017 12:21+1Код для 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 система типов дает оптимизатору дополнительные гарантии по владению памяти, что потенциально может привести к более оптимизированному коду.broken
09.12.2017 13:07Кстати, ради интереса, можно было бы к компиляции плюсов через gcc 7.2 добавить и clang тот же, что использовался для раста.
VioletGiraffe
09.12.2017 13:17А замена О2 на О3 может привести к более оптимизированному коду С++.
dev96
09.12.2017 14:42Прежде чем хвататься за оптимизации, для начала посмотрите код теста на С++.
Да и сам автор писал, что замеры не достаточно объективны.
Плюс, насколько я понимаю, время считается и с учетом ввода/вывода данных.
P.s. я в Rust вообще не разбираюсь, но мне кажется, что тут что-то не чисто)
И первое, что я бы сделал, так это бы выбросил ввод/вывод.broken
09.12.2017 19:01+1А что может быть не чисто? Любой язык должен быть медленнее сей или плюсов? И кто-то сказал, что код на расте идеален? Смысл в этом посте не в том, что показать, что что-то быстрее или медленнее, а в том, чтобы показать, что они равноценны по производительности хотя бы. Так что смысла в вашем комментарии вижу мало, разве что про убирание вывода соглашусь, было бы неплохо.
qRoC
10.12.2017 11:21Спорить о быстродействии глупо, т.к. разные реализации стандартной библиотеки.
И без тестов можно было бы предположить что С++ может быть медленней(в std упор на универсальность, особенно это касается hashmap), но вот разогнать производительность можно без проблем.broken
10.12.2017 11:36+1Спорить глупо, да, потому что реальных и максимально честных замеров в этой статье не приводится, а приводится грубая оценка, чтобы понимать, на каком они уровне и можно ли говорить, что они равнозначны в производительности вообще, а не так, что Java и C++.
aamonster
09.12.2017 14:15Того же можно добиться и на крестиках.
Интуитивно — оптимизированные до упора программы должны дать примерно одинаковое быстродействие, и сравнивать надо будет уже сложность программ (количество boilerplate кода и т.п.)dmitryikh Автор
09.12.2017 15:25Интуитивно — оптимизированные до упора программы должны дать примерно одинаковое быстродействие
Спасибо за эту мысль. Я ее не смог высказать в статье.
Действительно, всегда можно получить оптимальный машинный код на компилируемом языке, хотя бы ассемблерными вставками. Статья же призвана показать, какую производительность можно достичь, используя идиоматический подход и высокоуровневые конструкции того, или иного языка.
Мы ведь хотим писать быстро, и чтоб работало быстро, а не зависать над ассемблером и профилировщиком :)aamonster
09.12.2017 16:37+1Ну вот вам и идея для второй статьи: выслушать все замечания тут, оптимизировать оба комплекта программ до упора (возможно, от стандартных функций и структур данных придётся отказаться в пользу самописных или сторонних, лучше подходящих для конкретных задач), проверить, ушла ли разница в скорости, и показать разницу в сложности.
TargetSan
09.12.2017 22:32+2Не вижу смысла. Тогда "крестики" выиграют автоматом, т.к. на расте придётся обложиться ансэйфом. Который не лаконичен намеренно.
aamonster
09.12.2017 23:39+1Если так — то нафига он такой красивый нужен?
Но мне почему-то кажется, что вы заблуждаетесь. Иначе с чего бы расту претендовать на ту же нишу?
Опять же, на плюсах есть куча мест, где для написания предельно эффективного кода — надо или писать кучу бойлерплейта, или использовать совсем адское метапрограммирование на темплейтах (зовите экзорцистов, пусть изгонят дух Александреску!). Казалось бы, раст посвежее, и авторы должны были постараться найти решение получше.
Но я раст не знаю, так что подожду, пока кто-то не сделает такое сравнение. Или пока мне самому раст не понадобится.TargetSan
10.12.2017 00:15+4Поясню. В С++ у вас вся программа представляет собой один сплошной unsafe блок. Где-то промахнулись с указателем — и всё, приехали. Причём учтите, что с тривиальными ошибками помогают смарт-указатели. С нетривиальными — всё по старому.
Теперь — чем лучше rust конкретно здесь. Возможностью сказать компилятору "конкретно здесь я сделаю сам, а вокруг уж будь добр проконтроллируй ты". Именно поэтому я и говорю, что если, как вы пишете,
оптимизировать оба комплекта программ до упора (возможно, от стандартных функций и структур данных придётся отказаться в пользу самописных или сторонних, лучше подходящих для конкретных задач), проверить, ушла ли разница в скорости, и показать разницу в сложности
у вас получится, по факту, полотнище Си-стайл небезопасного кода. В таком случае на ржавчине код будет длиннее, т.к. подразумевает больше явности. И это было сделано намеренно.
Если же вы имели ввиду написание максимально оптимального кода, но с соблюдением С++-стайл — на С++ будет, скорее всего, длиннее. Почему — инфраструктура написания кастомных контейнеров на С++ всё ещё в зачаточном состоянии. Особенно в области написания типов итераторов.
phponelove
10.12.2017 08:28Поясню. В С++ у вас вся программа представляет собой один сплошной 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% на ноду. Естественно, что в хелвордах это мало кому интересно, но это интересно за рамками оных.
Особенно в области написания типов итераторов.
Что это такое, какие тут есть проблемы — не знаю ни я, ни, скорее всего, все остальные.TargetSan
10.12.2017 13:57Вы интересный. Вам всё непонятно и неизвестно.
Для начала — что может Rust unsafe:
- Разыменовывать указатели (не ссылки а именно указатели)
- Вызывать unsafe функции, в т.ч. FFI
- Реализовывать для типов unsafe трейты
- Менять статические переменные
Нас будет интересовать в основном №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 лучше голого указателя — отдельная тема.
Что это такое, какие тут есть проблемы — не знаю ни я, ни, скорее всего, все остальные.
Тогда зачем вообще комментируете эту часть? Если вы хотели пояснений — написали бы "Поясните". А не "Я не знаю, значит никто не знает, значит никому не нужно".
phponelove
11.12.2017 00:06Ну тут типичный набор вранья, игнорирования и манипуляций.
Я определил пару основных тезисов — никакой компилятор ничего не гарантирует и ни к чему, о чём говорилось — не имеет( это было проигнорировано), никакой компилятор ни по каким «рукам» не даст. Это всё обычная лапша и обёртки, которые есть везде, с одной лишь разницей — разделение языка на 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.
Тогда зачем вообще комментируете эту часть? Если вы хотели пояснений — написали бы «Поясните». А не «Я не знаю, значит никто не знает, значит никому не нужно».
Ну дак что же вы не пояснили? И опять же — очередные попытки врать и выдавать враньё за мои цитаты.
0xd34df00d
11.12.2017 20:27+3Единственная разница в том, что в крестах unsafe пишется без приписки unsafe — на это разница заканчивается.
Только это ключевая разница: в крестах код по умолчанию unsafe, в расте — по умолчанию safe.
Я, если что, не апологет раста и вообще нежно люблю кресты. И хаскель ещё.
А далее мы захотим использовать индексы вместо указателей — привет bound checking и прощай производительность, либо прощай bound checking и привет unsafe.
Никто не мешает статически доказать, что после максимум одной данной проверки вне цикла индекс никогда за границу не вылезет.
Вру, мешают невыразительные системы типов. Увы, языки, где это решено, считаются чересчур академическими.phponelove
11.12.2017 20:58-2Только это ключевая разница: в крестах код по умолчанию unsafe, в расте — по умолчанию safe.
Неверно. Очередная агитка, из которой ровным счётом ничего не следует. Никакого «умолчания» нету, вы придумали ахинею и повторяете её.
Код в расте по умолчанию такой же unsafe, а safe он будет тогда, когда глобально вырубить unsafe, но тогда это будет жаваскрипт, а не «альтерантива» крестам. Но и опять же, а если я разработчик stdlib, то у меня получается unsafe раст? В stdlib safe не нужно?
В конечном итоге всё сводится к проверке — написан ли у тебя unsafe код, а в каком виде этот unsafe код будет — в виде raw-pointer, либо unsafe raw-pointer — абсолютно неважно.
Я, если что, не апологет раста и вообще нежно люблю кресты. И хаскель ещё.
Не надо за апологетами повторять убогие тезисы. Они пустые и ничего не значат, из них ничего не следует.
Никто не мешает статически доказать, что после максимум одной данной проверки вне цикла индекс никогда за границу не вылезет.
Это всё голубые мечты. Да и данный кейс не про обход в массиве.
0xd34df00d
11.12.2017 21:26+2Код в расте по умолчанию такой же unsafe, а safe он будет тогда, когда глобально вырубить unsafe, но тогда это будет жаваскрипт, а не «альтерантива» крестам.
Эм, нет.
Вы можете прогрепать весь ваш код на тему unsafe, прогрепать все ваши зависимости, поставить в CI хук, вычитающий сто баксов из зарплаты коммитящего unsafe, и так далее. А те unsafe, что останутся, доказать руками с карандашом и бумагой (ну или с маркером и доской, смотря что вам милей).
Что вы будете грепать в плюсах?
Не надо за апологетами повторять убогие тезисы. Они пустые и ничего не значат, из них ничего не следует.
Я не за ними повторяю. Я просто по своему опыту знаю, что в хаскеле я могу прогрепать код на предмет unsafe-функций (их там условно три), а также на предмет error и undefined, и в чём-то убедиться. Могу даже с библиотеками так сделать, и, например, выкинуть clang-pure, ибо это говнокод какой-то с тамошнимunsafePerformIO
в каждой второй функции. А ещё Гугл в авторах, тоже мне.
Это всё голубые мечты.
Вполне выражаемые через зависимые типы.
Да и данный кейс не про обход в массиве.
А к чему было про bounds checking?phponelove
11.12.2017 22:00-3Вы можете прогрепать весь ваш код на тему 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 раста. Идите и прогрепайте её. И так будет с любым лоулевел кодом.
Да, всегда можно написать условно-безопасную обёртку, но её нужно написать. И её нужно и можно написать и в крестах. А можно не написать — и ничего не поменяется. Это можно сделать что на крестах, что в расте.0xd34df00d
11.12.2017 22:10+2Иди прогрепай stdlib на тему unsafe и расскажи об успехе, и да, по 100баксов вычти то же.
В коде за авторством себя и прочих обычных программистов я лажу находил сильно чаще, чем в stdlib. В рамках данной дискуссии проблемами stdlib можно пренебречь и взять её корректность за аксиому.
Вначале у нас было «по умолчанию безопасный», теперь, оказывается, что надо грепать.
Потому что если вы прогрепали и не нашли unsafe, то он безопасный.
А т.к. ты 100% что-то нагрепаешь, то оказывается нужно проводить ручной анализ.
Я в своём коде не писалunsafePerformIO
ни разу. Так что нет, не «полюбас что-то нагрепаешь».
Берёшь какой-нибудь clang-tidy и пишешь за пол часа набор правил. Что надо искать? */&/new — искать не сложнее, чем unsafe.
Любой код с * и &? Прям все ссылки и указатели запретить? Успех!
new? Ну буду писать вместо этогоstd::make_unique<Meh>().release()
. Такой код от некоторых карго-культуристов я видел в проде.
Всё эти типы существуют, если существуют(что будет работать в этом кейсе), в языках, которые, мягко говоря, даже рядом с растом( я уж не говорю о си) не валялись.
Не валялись по каким критериям?
Нам нужно дать следующий блок?
Этот пример я не понял.
Как мы преобразуем порядок бита в битмапе к валидному индексу?
Возьму Idris/Coq/ATS и докажу как теорему.
В крестовом коде может быть то же ноль указателей, ноль new и прочее.
Кресты без ссылок и указателей и раст без unsafe — это немножко разные вещи по выразительности и интероперабельности с внешними библиотеками.phponelove
12.12.2017 01:14-2В коде за авторством себя и прочих обычных программистов я лажу находил сильно чаще, чем в stdlib. В рамках данной дискуссии проблемами stdlib можно пренебречь и взять её корректность за аксиому.
Слив засчитан. stdlib — это не единственный пример — таких примеров тысячи. Начиная от серво и всех его зависимостей — кончая от биндингов к libc — без которой раст существовать не может, как и остальному сишному рантайму.
Большинство unsafe скрыто именно в сишном рантайме, который не переписан и никогда не будет переписан на раст. И даже не смотря на то, что основная часть айсберга невидна — наверх торчат тысячи и тысячи unsafe.
Ну и самое главное — что там вы находили — никого не интересует, ведь из этого ничего не следует. Это просто пустой трёп.
Потому что если вы прогрепали и не нашли unsafe, то он безопасный.
Каждый раз, когда мне кто-то говорит, что он не адепт — он адепт.
Вам действительно так сложно в логику, что вы неспособны понять того, чем отличается «раст по умолчанию безопасный» и «раст безопасный после того, как мы его погрепаем и не найдёт unsafe», хотя их мы найдём.
С вами дискутировать не имеет смысла, вы за 10строк 10 раз родите взаимоисключающие параграфы. Вы сами же опровергли свой тезис о том, что «раст по умолчанию безопасный», но продолжаете делать вид, что это не так.
Любой код с * и &? Прям все ссылки и указатели запретить? Успех!
Опять же, либо полная неспособность понять то, что вам пишут, либо неадекват. В расте нет указателей и это никого не заботит, а тут вдруг уже подавай указатели. & — это не ссылка, есличто.
Ну и самое главное, я ведь не зря сказал про clang-tidy, но что-то вы это проигнорировали? Почему?
new? Ну буду писать вместо этого std::make_unique().release(). Такой код от некоторых карго-культуристов я видел в проде.
Для справки — результатом release() будет указатель, от которых мы выше отказались. Вы действительно не понимаете всей бессмысленности того, что пишите?
Не валялись по каким критериям?
По производительности и возможностей к написанию лоулевел кода.
Возьму Idris/Coq/ATS и докажу как теорему.
Нет. Я уже вам тысячи раз говорил о том, что недостаточно написать рандомную фразу и выдать её за аргумент, либо тезис. Возьми и напиши. Пока этого никто ещё не сделал, но я знаю — ты сможешь. Удачи. Не забудь потом законтрибутить в раст, а то бедный раст так и не осилил родить свой аллокатор — приходиться использовать подлый, небезопасный аллокатор на крестах.
Кресты без ссылок и указателей
Никто вам о ссылка не говорил. & — это не ссылка, а взятие адреса, а * — это разименование и тип переменной. Я не запрещал умножение и and.
это немножко разные вещи
Это одно и то же.
по выразительности и интероперабельности
Это вы только что придумали и это ни из чего не следует.
с внешними библиотеками.
Любая программа на расте, кроме хелвордов,, да и сам раст — это биндинги к С/С++. Что-то они пишут биндинги — напишите и вы. Никаких проблем нет.
А захотите написать что-то новое — напишите без указателей, ведь на расте же пишут.
В любом случае, все эти разговоры не имеют смыслы, ведь вы уже слились. Вы либо этого не замечаете, либо делаете это намеренно.
Вы вначале говорили о какой-то там безопасности по умолчанию, повторяли какие-то идиотские тезисы, а потом всё свелось к тому, что смысл раста и его безопасности в том, чтобы удобней было грепать исходник. Ничего более вы предоставить не смогли.
И почему-то вы не задаётесь вопросом — какой смысл существования раста? Если нужно было бы добавить греп в С++ — можно было бы за месяц написать идеальный clang-tidy плагин, который бы находил всё это влёт.
И так со всем, куда бы вы не пошли, если вы начнёте анализировать то, что вы говорите, то, что вам предлагают — вы поймёте, что все тезисы, которые вы повторяете — ничего не стоят. За ними ничего нет. Всё это трёп.
И тут основной вопрос заключается в том — чего вы хотите. Понять, либо убедить себя в том, что всё так, как вам рассказали. Объективная реальность — это сложно. Все уверены, что они не повторяют пустые тезисы, но именно это они и делают.
0xd34df00d
12.12.2017 04:27+1Да, вы правы. Различий между безопасностью и выразительностью языков нет, всё давно заляпано сишным неверифицированным ядром и libc. Бери хоть хаскель, хоть жс — всё будет так, исхода нет. Всё тлен. И проблему останова не решить, и арифметика Пеано не полна.
phponelove
12.12.2017 05:55Слив засчитан, а далее пошла ахинея.
Различий между безопасностью и выразительностью языков нет
Опять же, схема проста — подмени понятия в рамках тезиса, который определён в локальном контексте — на глобальные. И получается так, что я утверждал не то, что ты их не показал, не то, что ты врал и манипулировал, а говорил вообще — нет, я говорил не вообще, а о конкретных примерах.
То же самое и с libc — да, есть код и есть новомодные языки, которые претендуют на замену С/С++, но почему-то ни один из них не является самостоятельным, является нахлабучкой на С/С++ рантайм, на этих «языка» НИЧЕГО серьёзного не написано, кроме их компиляторов, хотя в случае раста и компилятора нет.
И что получается. Ничего нет, но трёп о том, что «выкидывай С++ хоть завтра» — есть. Дак выкинь, продемонстрируй как надо. Нет — никто этого не делает и не сделает.
Каждый год появляется новый убийца, каждый код появляются его адепты. В начале нулевых везде вещали о том, что жава все догнала и С/С++ ненужен. Потом релизнулся v8 и вещали о том, что С/С++ не нужен.
До раста ГЦ был моден и быстр, да и сам раст на нём был. Не фортануло — выкинули, и вот он уже не моден и не быстр и без него быстрого языка не сделаешь. Хотя адепты раста — это хаскелисты/лисписты, которые буквально вчера любили ГЦ.
И так было всегда, и будет всегда. Пока хайп есть — вы посторяете медийные тезисы, как только он сойдёт на нет и появится новое — будет новое. Вы же ничего не предлагаете, ничего не говорите — вы повторяете хрен пойми что и хрен пойми за кем.
Так всегда было есть и будет. Заходишь в тред по С++ и критикуешь кресты — тебя минусуют и «ты ничего не понимаешь». Эта слепая вера в то, что наш объект обожания идеален, что раз везде написано «проблем нет» — значит их действительно нет.
Мне уже давно не интересно этим заниматься — я даже не знаю зачем я снова тут отвечают, ведь отвечаю я в пустоту.
phponelove
11.12.2017 00:15Забыл ещё один пример привести, кроме unwrap — это immutable. У нас так же везде immutable, но в реальности же — везде mut через mut, при этом в очередной раз мы услышим отмазку про то, что «а вот в реальных проектах». И какой бы код ты не показывай — везде ответ один «это неправильный код», при этом мы сразу же забываем о том, что «компилятор должен бить по руками» и «гарантии».
При этом, когда мы говорим о крестах — у нас почему-то этот аргумент пропадает. И почему я не могу сказать «а вот в реальном коде new чрез new никто не использует», и это действительно так.
Мы берём ссылки и сравниваем их с указателями, будто бы в крестах ссылок нет. Мы сравниваем то, что удобно нам, игнорируя то, что неудобно.
И так везде — никто не скажете того, чем строки в расте более safe, нежели в крестах. Мы всегда съезжаем на тему «а можно их не использовать», дак ведь в расте их то же можно не использовать.TargetSan
11.12.2017 01:29+2"Я бежала пять миль чтобы сказать вам как вы мне безразличны"
Я бы может ответил вам на ваши тезисы. Но судя по тому, как вы их подаёте, вы банально не владеете вопросом, уж простите за столь грубый пассаж. Чтобы ответить на всё это, мне пришлось бы цитировать вам главы из документации, где всё это объяснено. Держите ссылки:
- Время жизни и заимствование: https://doc.rust-lang.org/book/second-edition/ch04-00-understanding-ownership.html
- Обработка ошибок: https://doc.rust-lang.org/book/second-edition/ch09-00-error-handling.html
- Ансэйф, кратенькое введение: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html. Там же — что такое указатели в Rust, чем они отличаются от ссылок и т.п.
- Глубокое погружение в ансэйф: https://doc.rust-lang.org/nomicon/
Пара комментариев:
никакой компилятор ничего не гарантирует и ни к чему, о чём говорилось — не имеет( это было проигнорировано), никакой компилятор ни по каким «рукам» не даст.
Гарантии выражаются в отказе компилировать код, в котором они нарушаются. Этого недостаточно?
Это всё обычная лапша и обёртки, которые есть везде, с одной лишь разницей — разделение языка на unsafe/safe, но из этого так же ничего не следует.
Изучите вопрос. Ссылки выше в комментарии.
Итератор — это интерфейс, а реализация никакого отношения к итератору не имеет. И сложность абсолютно не зависит от интерфейса. Хочешь ++ — сделай ++ в крестах, если оптимальный интерфейс реализовать не можешь. И это сложность именно оптимальности, а не крестов.
http://en.cppreference.com/w/cpp/iterator
Просвещайтесь.
И какой бы код ты не показывай — везде ответ один «это неправильный код», при этом мы сразу же забываем о том, что «компилятор должен бить по руками» и «гарантии».
В этом топике речь если шла, то о неидиоматичном коде. И вы, кстати, ни одного примера кода не привели.
Мы берём ссылки и сравниваем их с указателями, будто бы в крестах ссылок нет. Мы сравниваем то, что удобно нам, игнорируя то, что неудобно.
http://en.cppreference.com/w/cpp/language/reference
Почитайте, чем отличается.phponelove
11.12.2017 02:12-1Как всегда, ничего нового. Ничего конкретно, одни обвинения «ты ничего не знаешь», «да иди читай и просвещайся» — зачем вы живёте? Какой смысл в вашем существовании и пребывании тут? Спастить мне ссылку? Просто так меня обвинять?
Ведь вы никогда мне не скажете и не покажете — где я не прав, в чём я не прав, что и почему мне нужно смотреть. Вы где-то прочитали тезис, но не можете его аргументировать, и именно поэтому как только у вас возникают проблемы — вы пытаетесь снять с себя ответственность.
Гарантии выражаются в отказе компилировать код, в котором они нарушаются. Этого недостаточно?
Это не более чем манипуляции. Компилятор компилирует код с unsafe? Компилирует. На это финиш.
Гарантии в расте — это такие же рукотворные гарантии, как и везде. Есть точно такие же требования, как и везде. Хочешь писать код — делай это правильно, но это не значит, ты неправильно его написать не можешь — можешь. И пишешь.
Это в жаве я не могу написать неправильный код( и то я даже не уверен в этом), но в данном случае все гарантии — это гарантии программиста.
Это из разряда «ремни безопасности гарантируют нам безопасность», только вот ремни сами на тебе не застёгиваются, и эта безопасность — лишь следствие желания человека её использовать. Никто не выдаёт эту безопасность за «бесплатную»/пассивную — она не такая.
Изучите вопрос. Ссылки выше в комментарии.
Это очень хорошо, когда вы не можете ничего ответить и пытаетесь делегировать эту обязанность на какие-то ссылки. Ведь я же говорю не с ссылка, а с вами.
en.cppreference.com/w/cpp/iterator
Просвещайтесь.
И опять — ответов нет. К чему и что следует из этой ссылки, к чему вы её спастили и какие мои слова она опровергает — неясно. Недостаточно просто кинуть ссылку — надо связать её с контекстом и вывести следствие. У вас нет ни того, ни другого.
Категории итераторов — это расширение функционала и не более того. Если вы в расте взяли и определили, что у нас итератора есть InputIterator, то руководствуйтесь и в С++ такой же логикой. InputIterator такой же итератор — всё остальное — сверху.
Вы не получите поведение аналогичное другим категориям, реализую одну/две функции. Будь то С++, раст, пхп, либо что угодно.
В этом топике речь если шла, то о неидиоматичном коде. И вы, кстати, ни одного примера кода не привели.
Вот опять непонятно что и непонятно к чему. Я сразу же указал, что евангелисты раста часто путают безопасность уровня языка и безопасность уровня программиста, это же касается не только безопасности.
Вы говорите «раст, компилятор даст по рукам», а в следующем ответе уже говорите иное. Оказывается проверять ошибка не обязательно, иммутабельность — не обязательно, safe — не обязательно. И вся аргументация сводится к тому, что «а просто не пиши unsafe» — дак я то же самое могу делать и в С++.
Я могу точно так же нарушить unsafe как угодно, и осознания того, что это «неправильный код» — ничего мне не даст. И никакой компилятор мне ничего не скажет.
en.cppreference.com/w/cpp/language/reference
Почитайте, чем отличается.
И опять то же самое. Зачем мне кидать ссылки? Что из них следует? Даже предположим, что отличия есть( на самом деле тут уже подлог, ведь в С++ ссылки — это конструкции языка, а в расте — просто обёртки и сравнивать их некорректно. Я могу сделать какую угодно обёртку и назвать её ссылкой), то что из этого следует?
Вы понимаете, что нельзя просто так взять и сказать «разница есть». Ведь смысл не в разнице, а в том, что из неё следует. И это следствие вы не вывели.
Кстати, у них названия разные — вот вам ссылка, прочитайте. Только что из этого следует? Ничего.
Вот и тут то же самое. Вам указали на то, что сравнивать голые указатели в С++ и ссылки в расте — некорректно. Вы поплыли в сторону «они различаются» — различаются, дальше что?TargetSan
11.12.2017 02:54+2Ведь вы никогда мне не скажете и не покажете — где я не прав, в чём я не прав, что и почему мне нужно смотреть. Вы где-то прочитали тезис, но не можете его аргументировать, и именно поэтому как только у вас возникают проблемы — вы пытаетесь снять с себя ответственность.
По существу у вас возражения есть?
Это не более чем манипуляции. Компилятор компилирует код с unsafe? Компилирует. На это финиш.
Вы таки не понимаете, почему и для чего это сделано. Вы не понимаете, что системный язык вообще без прямой работы с памятью — не системный язык.
Это очень хорошо, когда вы не можете ничего ответить и пытаетесь делегировать эту обязанность на какие-то ссылки. Ведь я же говорю не с ссылка, а с вами.
Но для начала неплохо бы владеть предметом. А для этого надо немножко почитать.
И опять — ответов нет. К чему и что следует из этой ссылки, к чему вы её спастили и какие мои слова она опровергает — неясно. Недостаточно просто кинуть ссылку — надо связать её с контекстом и вывести следствие. У вас нет ни того, ни другого.
Эта ссылка была дана вам чтобы показать, что в С++ реализация итератора сводится далеко не к одному перегруженному оператору "++". Даже для простого InputIterator.
Категории итераторов — это расширение функционала и не более того. Если вы в расте взяли и определили, что у нас итератора есть InputIterator, то руководствуйтесь и в С++ такой же логикой. InputIterator такой же итератор — всё остальное — сверху.
В Rust нет категорий итераторов в понимании С++.
Вот опять непонятно что и непонятно к чему. Я сразу же указал, что евангелисты раста часто путают безопасность уровня языка и безопасность уровня программиста, это же касается не только безопасности.
Я даже не знаю, что на это ответить. "Безопасность уровня программиста" это как? Не давать программисту нажимать неправильные кнопки? Rust даёт определённые средства отлова и устранения определённых классов ошибок. И даёт средства локально отключать некоторые из этих ограничений — если того требует задача.
Даже предположим, что отличия есть( на самом деле тут уже подлог, ведь в С++ ссылки — это конструкции языка, а в расте — просто обёртки и сравнивать их некорректно. Я могу сделать какую угодно обёртку и назвать её ссылкой), то что из этого следует?
Эта фраза чётко говорит о том, что вы не понимаете, что такое ссылки в Rust. Уважьте, перейдите по ссылкам и прочтите.
Вы понимаете, что нельзя просто так взять и сказать «разница есть». Ведь смысл не в разнице, а в том, что из неё следует. И это следствие вы не вывели.
Смысл в разнице, т.к. из неё следует тот самый вывод. А разница в том, что в С++ ссылка не может быть перемещена и переназначена — что делает её применение сильно ограниченным. Что в Rust ссылка может менять указуемый объект, может быть перемещена и может свободно храниться в поле структуры, не "пригвождая" структуру к одному месту. И что не может пережить объект, на который указывает.
Вот и тут то же самое. Вам указали на то, что сравнивать голые указатели в С++ и ссылки в расте — некорректно. Вы поплыли в сторону «они различаются» — различаются, дальше что?
Как раз корректно. Ссылки в Rust несут ту же нагрузку, что и в С++ — и в дополнение бОльшую часть нагрузки указателей.
В целом, разговор защёл в тупик. Вы не приводите аргументов, только игнорируете либо отрицаете мои.
Засим откланиваюсь.
phponelove
11.12.2017 04:32По существу у вас возражения есть?
Возражения на что? У вас нет «по существу» ничего, и вам об этом уже сообщили. А возражать «по существу» на нечто — нельзя. «существу» нет.
Вы таки не понимаете, почему и для чего это сделано. Вы не понимаете, что системный язык вообще без прямой работы с памятью — не системный язык.
Дело не в том, что я понимаю, а что не понимаю. Вы там пытались рассуждать о том, что какой-то компилятор вам настучит, но теперь оказалось, что нет.
Причины того, почему он не настучит — мне не интересны и к делу отношения не имеют, мне важен факт «не настучит», а значит вы соврали — всё просто.
Но для начала неплохо бы владеть предметом. А для этого надо немножко почитать.
Опять же — ответа нет, а какой-то пустой трёп есть. Это основная проблема бесед в подобными евангелистами, вы не понимаете того, что нельзя просто так брать и требовать чего-то.
Вот у вас есть требования какого-то «предмета», но почему и на каком основании вы его требуете — у вас ничего этого нет. Это обыкновенный слив и ничего более.
Я чего-то не понимаю? — Что? И даже если будет что-то, чего не будет, то дальше будет финиш. Ведь вам нужно будет вывести из факта непонимания чего-то то, что вы из него выводите «необходимость и невозможность о чём-то рам рассуждать».
Эта ссылка была дана вам чтобы показать, что в С++ реализация итератора сводится далеко не к одному перегруженному оператору "++". Даже для простого InputIterator.
Во-первых, об этом никто не говорил. А во-вторых — en.cppreference.com/w/cpp/concept/InputIterator По это же ссылке чётко указанно — что нужно. ++/*/== — всё, при этом всё это необходимо для итератора.
В Rust нет категорий итераторов в понимании С++.
Причём тут понимание С++? О нём никто не говорил, говорилось о том, что абсолютно неважно что и как там называется. Факт остаётся фактом — всё, что сверху базового ++ — это дополнительный функционал, который в расте так же есть.
И это функционал к С++ отношения не имеет. Хочешь быстрое +10 — никто не будет долбить ++ 10раз. А хочешь долбить 10раз — что-то кроме InputIterator тебе не нужно и С++ никак к этому не обязывает.
В конечном итоге — что мы имеем? В руста * и ++ — это одна функция, а в С++ — две. Поэтому в С++ нужно реализовать лишь ==, что в ходит в первоначально определение «одну-две функции».
Я даже не знаю, что на это ответить. «Безопасность уровня программиста» это как? Не давать программисту нажимать неправильные кнопки? Rust даёт определённые средства отлова и устранения определённых классов ошибок. И даёт средства локально отключать некоторые из этих ограничений — если того требует задача.
Раст ничего не даёт, вы никогда не покажете то, что он даёт и причина проста — это невозможно.
Отключить глобально unsafe вы не можете, а значит оно будет включено, а значит все гарантии — гарантии уровня рантайм/обёрток/etc, но никак не языка. Все те же гарантии реализуются на С++.
На это финиш и ваши аргументы исчерпаны, хотя я это уже несколько раз писал и каждый раз вы это игнорировали.
Эта фраза чётко говорит о том, что вы не понимаете, что такое ссылки в Rust. Уважьте, перейдите по ссылкам и прочтите.
Как всегда я вижу одно и тоже. Бесполезный трёп из которого ровным счётом ничего не следует. Ну и самое главное, вы нигде мне не рассказали — что именно я не понимают, что именно в моей логике ошибочно, но и самое главное — с чего я вообще должен понимать непонятно что.
Смысл в разнице, т.к. из неё следует тот самый вывод. А разница в том, что в С++ ссылка не может быть перемещена и переназначена — что делает её применение сильно ограниченным.
Я уже заранее помножил на ноль эту попытку, но опять игнорирование.
В расте нет ссылок — в расте есть обёртка в stdlib. Никаким образом эту обёртку нельзя сравнивать с конструкцией языка.
Что в Rust ссылка может менять указуемый объект, может быть перемещена и может свободно храниться в поле структуры, не «пригвождая» структуру к одному месту. И что не может пережить объект, на который указывает.
В расте нет ссылок, повторю это ещё раз. Подобное поведение реализуется и на С++, если нужно. И никаким образом дефолтные ссылки это не ограничивают.
Как раз корректно. Ссылки в Rust несут ту же нагрузку, что и в С++ — и в дополнение бОльшую часть нагрузки указателей.
Неверно. Нельзя сравнивать обёртку над указателем и ссылку, у которой есть чёткая семантика. Нельзя выдавать своё «хочу» за правильно.
С чего вы взяли, что ваша «ссылка», которая «не имеет ограничений» лучше, нежели ссылка, которая их имеет? Для меня — лучше та, которая имеет?
Да и рассуждения про ограничения — смысла не имеют, ведь никаких ограничений у С++ нет, весь фокус в том, что мы сравниваем несравнимое, а если мы сравним обёртку над указателем и обёртку над указателем, то получить что? А ничего не получиться. Обёртка не имеет ограничений.
В целом, разговор защёл в тупик. Вы не приводите аргументов, только игнорируете либо отрицаете мои.
Я могу показать где и что вы игнорировали, но вы не сможете показать ни одного. Почему? Потому что это пустой трёп.
По поводу ваших обвинений в отрицании — это подмена понятий. Это стандартный приём, когда мы выкатываем тезис и наделяем его нужным нам следствием. Например — «у вас ник на T — вы идиот». И далее играть в игру «но ведь он на T».
Именно поэтому в рамках дискуссии принято чётко и ясно выводить все следствия из своих требования/тезисов. Чего от вас нет. Вы кидаетесь примитивными шаблонами «не понимаешь», «а это так», при этом — это демагогия. Это не аргументы, не тезисы — это трёп.
phponelove
11.12.2017 04:54Поясню на примере.
Что в Rust ссылка может менять указуемый объект,
Вот тут мы объявлением тезис, что ссылка чего-то там не может. Но тут есть ошибка, мы берём какое-то свойство, говорим о том, что оно есть. И далее делаем вывод о том, что «лучше», хотя из наличия какого-то свойства «лучше» не следует.
Мы просто так, на основании хрен пойми чего — требует от других то, что есть у нас. При этом я точно так же могу требовать от раста то, почему у меня ссылка не привязывается к объекту?
Что из этих требований следует? Ничего.
Ну и как я уже говорил — в расте нет никаких ссылок, да и вообще нет почти ничего — всё это stdlib. Поэтому мы не может сравнивать конструкции языка и обёртки.
Ну и самое главное — почему мы ссылку в расте сравниваем с ссылкой в С++, а не с std::reference_wrapper? А причина проста — мы мало того, что делаем сравнения из которых ничего не следует — мы заведомо ограничиваем одну из сторон сравнивая несравнимое.
В конечном итоге всё сводится к тому, что везде всё должно быть как в расте. Почему? Просто так. Хочешь как в расте — сделай как в расте, это С++ позволяет. Но путём подмены понятий, мы выдаём за альтернативу в С++ ссылку(&), а на самом деле не должны ничего выдавать — ведь в расте этого попросту нет.
В конечном итоге «ссылка» в С++ может быть такой же, как «ссылка» в расте. С единственной разницей в том, что там она будет в stdlib, а в С++ нет( и то не факт). Но из этого ровным счётом ничего не следует — ведь никаких свидетельства за то, что надо так, а не иначе — нет.
Поэтому, стандартная реализации из С++ имеет та кое же право на существование, как и реализация раста. А если необходим функционал «как в расте» — он реализуется, да и уже есть.
aqrln
11.12.2017 19:40+2Извините, а с чего вы взяли, что в Rust ссылки являются «обёртками в stdlib», а не конструкцией языка?
phponelove
11.12.2017 02:26Ну и вы полностью проигнорировали всё, я вам повторил свои два тезиса, вдруг я первый раз сформулировал это непонятно. Но вы опять их проигнорировали. И тут уже нельзя сослаться на то, что вы сделали это не специально — нет, вы делаете это специально.
Я вам сказал про пример оверхеда на option, вы мне показали, что спустя 5-7 лет существования языка — кастыль впилили. Но ведь кастыль ничего не значит — это показывает фундаментальную дыру, когда у нас есть дырявые абстракции, для которых мы каждый раз должны нарушать логику, нарушать инкапсуляцию.
И я вас привёл следующий пример, который вы проигнорировали. Есть проблемы те, которые решает раст, но есть множество проблем, которые он производит. При этом, большинство эти решений — это решения не уровня языка, а уровня программиста, хотя почему-то раст их декларирует как решения уровня языка. Что странно.
Вся эта тема — это сплошные манипуляции. В stl есть много мест для совместимости с си, которые «не безопасны», но они там взялись не из-за того, что С++ — это «100% небезопасно», а из-за того, что есть множество сишного кода, который нужно интегрировать.
То же самое происходит и с растом — раст без unsafe не позволяет интегрировать в себя сишный/крестовый код, а раст без него попросту существовать не может. llvm, libc, шланговые рантайм для исключений и прочее и прочее. Сколько биндингов к сишному коду в том же серво?
Каким образом этот коде безопасней? Никаким. В идеально мире, где у нас весь код расте/смартпоинтерах — всё хорошо, и в хелвордах на ресте — то же всё хорошо, как и в хелвордах на смартпоинтерах. Но реальность она сложнее.
И не стоит заниматься этими манипуляциями и подменой понятий, сравнивая несравнимое. Это хорошо звучит, люди любят популизм, но далеко на нём не уедешь.humbug
11.12.2017 08:17Ну попробуй со мной похоливарить. 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".
DrLivesey
11.12.2017 13:31+1Может =)
А можно ссылочку на документацию? Меня интересует эта тема.
humbug
11.12.2017 14:22+1Rust
'у вообщеC++
не нужен.C
— вот лингва франка нашей профессии, но и от него можно отказаться. Да даже от стандартной библиотеки самогоRust
.
- https://doc.rust-lang.org/book/first-edition/using-rust-without-the-standard-library.html
#no std
- https://github.com/alexcrichton/rlibc
#описывает основные примитивы, которые могут потребоваться
- https://doc.rust-lang.org/unstable-book/language-features/start.html
#entry point для программы
- https://www.youtube.com/watch?v=pKe1ww1TZcvM
#очень качественный доклад про низкоуровневые штуки с Rust
- https://github.com/warfish/xvm16
#загрузчик и проч штуки от докладчика
- https://zinc.rs/
#вроде бы мертвый
- https://www.redox-os.org/
#аж целая ОС
- https://www.reddit.com/r/rust/comments/467phw/could_a_libc_implementation_be_written_in_rust/
#обсуждение переписывания libc на Rust с предоставлением биндингов наружу
- https://brage.bibsys.no/xmlui/bitstream/handle/11250/2352353/12925_FULLTEXT.pdf
# bare metal Rust
humbug
11.12.2017 14:29Странно… у меня ссылка на видео не открывается. Попробуйте эту: https://youtu.be/pKe1ww1TZcM
- https://doc.rust-lang.org/book/first-edition/using-rust-without-the-standard-library.html
phponelove
11.12.2017 20:50Кроме 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 в любом его проявлении.humbug
12.12.2017 01:01+1Подожди. Давай будем объективны.
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++
непоколебимо. Что в вопросе, то и в ответе =)phponelove
12.12.2017 01:58-1Я отвечу на эти вопросы не тебе, но будущим читателям, потому что ты поленился пройти по ссылке и ознакомиться с предметом. Садись, два, плохой тролль, плохой!
Я не буду это комментировать — нужны публике клоуны — пусть будут клоуны.
Оптимизировали представление структур в памяти. Изначально в 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, сервой и прочим.
Но ты можешь им помочь и спасти их от С/С++, а то видишь как — они не могут, а ты можешь. Нужно срочно пойти и их научить.
humbug
12.12.2017 02:16+1godbolt.org/g/4cC91L
А тут мы видим рядовую картину того, что рядовой эксперт — это просто ретранслятор того, что он где-то увидел, либо прочитал в интернете. Он ретранслирует какую-то ахинею, и казалось бы — ну пойди ты и проверь, но нет.Ну и?
movl $8, %edx
Что является подтверждением моих слов.
movl $12, %edx
Опять всё, как я сказал. Ты бы и рад соврать, да никак не получается, да? Обидно, небось)))
humbug
12.12.2017 02:24+1Факты:
И еще один факт: ты тролль, который апеллирует к субъективным величинам. Вон из профессии.
phponelove
12.12.2017 02:42-1Мне было интересно — побежит ли этот клоун обвинять меня, делая вид, что мы обсуждаем только один тезис.
И заметьте, я вам показал, кто есть реальный тролль, и чьё призвание мыть полы. Ведь я не стал использовать её обсёр с T && как основание для игнорирование и закидывания дерьмом.
А вы не отвлекайтесь, минусуйте/плюсуйте — ведь вам же не интересно то, кто говорит что-то сознанное, а кто просто трепится и пытается свой слив замаскировать под «ты тролль, я с тобою не играю».
phponelove
12.12.2017 02:37-1Ты бы и рад соврать, да никак не получается, да? Обидно, небось)))
Обрадовался и решил всё остальное проигнорировать, впрочем, как и всегда.
godbolt.org/g/GxbPZa
Давай я чутка тебя расстрою. Ну ты это, давай, побольше скобочек.humbug
12.12.2017 02:54+2Warning: 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
код, если у тебя внутри порядок изменился?phponelove
12.12.2017 03:25-1Т.е. о чем я и говорил.
Ни о чём ты не говорил. Ты как всегда сел в лужу, но пытаешься как-то из неё всплыть.
Любая оптимизация, которая затрагивает бинарную совместимость — это проблема. Точно так же, твой раст до это оптимизации и после — не соберётся. Тебе же это никак не помешало. Вот и не помешает сейчас.
Объясни мне, в чем толк этой оптимизации C++, от которой код падает?
Никакой код не падает. Иди передай мне во внешний код, собранный растом до оптимизации.
Зачем ты пишешь эти глупые мазы? Твоему русту это не мешает только потому, что на нём ничего нет и он никому не нужен. Никакая бинарная совместимость ни с чем не нужно — причина проста — ничего нет.
Как ты можешь передать указатель на структуру во внешний C код, если у тебя внутри порядок изменился?
Ну передай мне структуру из раста во внешний код. Переведи из внешнего кода в раст. Переведи между старым растом и новым. Зачем ты придумываешь глупые оправдания?
По поводу твоих супер-раст оптимизаций. Мне лень гуглить, но ir умеет структуры и скорее всего раст их форвардит в ir, а ir — это llvm.org/doxygen/group__LLVMCCoreTypeStruct.html Т.е. судя по всему шланг так же форвардит упаковку на llvm.
И очень высока вероятность того, что великие оптимизаторы раста просто включили флажок в llvm и раструбили это среди адептов как «супер-оптимизацию». Это не ново.
0xd34df00d
12.12.2017 04:31+1Он это делает за тебя. C++ так не может и не сможет, потому что считается, что каждая структура в C++ обязана маппиться на C.
Технически, наверное, имеет право для то ли не POD-, то ли не trivially layout-типов (там определения с каждым стандартом меняются, не знаю точно).
C++ предпочитает мутабельное инстанцирование иммутабельному.
Какой-то странный пример, эквивалентный двум перегрузкам с
const T&
иT&
. Чего там удивительного?humbug
12.12.2017 10:43+1Технически, наверное, имеет право для то ли не POD-, то ли не trivially layout-типов (там определения с каждым стандартом меняются, не знаю точно).
Звучит логично. Только в примере от phponelove
MyStruct
— POD, и представлление этой структуры было оптимизировано.
godbolt.org/g/GxbPZa
mayorovp
12.12.2017 10:25+1Прости, был не прав. C++ предпочитает мутабельное инстанцирование иммутабельному. Спонсор западни — Константин Крамлин из Яндекса. Как много опытных плюсоидов завалилось на этом примере, просто жуть.
Интересно, а почему компилятор должен предпочитать обратное когда параметр — мутабельная переменная?
humbug
12.12.2017 10:48+2https://youtu.be/oXw2vXOUr1g?t=29m23s
Понимаешь, в чем дело, на зал из 100 человек правильно ответили 5. Это спустя 3 года работы с
C++14
тебе эти вещи кажутся очевидными, но они контринтуитивны.mayorovp
12.12.2017 10:55+2На самом деле контринтуитивна тут вовсе не константность, а универсальная ссылка. Такое ощущение, что универсальные ссылки появились в языке совершенно случайно — но появившись всем сразу так понравились что их оставили.
По хорошему, для них нужно было придумывать какой-нибудь отдельный синтаксис и не перегружать &&.
Я вот все еще никак не могу привыкнуть что Bar && — это ссылка на временный объект (простите, rvalue reference), а ID && — универсальная ссылка.0xd34df00d
12.12.2017 19:25+1Причём работает всё равно через пень-колоду. Синтаксиса для того, чтобы сделать универсальную ссылку на «немножко специализированный» тип, вроде, не знаю,
template<typename T> void doSmth(std::variant<T>&&); // хочу универсальную ссылку тут
нет.
potan
12.12.2017 19:53+2Я еще не видел людей, которые бы хаяли изменяемость данных
Ну я, например. Я чситаю важным достижением Rust, что он поощеряет иммутабельность, а мутабельность заставляет указывать явно.
0xd34df00d
11.12.2017 20:16+1Попробуйте написать итератор над любой нетривиальной структурй данных. С поддержкой всех нужных категорий.
Бустовскими адаптерами/фасадами для итераторов можно пользоваться?TargetSan
11.12.2017 21:06+2Да я как бы про них в курсе. Меня смущает ситуация, когда стандартная библиотека построена таким образом, что для решения созданных ею проблем требуется или писать кучу бойлерплейта, или втаскивать стороннюю библиотеку в несколько раз большего объёма. Что не всегда возможно. И никаких изменений уже лет 20 в этом направлении. ranges-v3 может и поменяют ситуацию, но последний раз когда я в них лазил, я не нашёл адапторов, аналогичных бустовским.
0xd34df00d
11.12.2017 21:30+1Я бы сказал, что не в библиотеке дело (если вы достаточно часто пишете итераторы, то я вам искренне завидую — интересные задачи решаете, небось), а в том, что трейты (ну или каноничные тайпклассы из более упоротых языков) являются более общим и более удобным средством.
lieff
09.12.2017 14:23У вас в С++ даже ассерты не ушли, надо -DNDEBUG добавить чтобы они исчезли. Код ужасно оптимизирован. Кстати в clang, rust использует то же AST дерево, так что добиться на С++ той же оптимизации обычно можно, я другого не встречал. А вот в обратную, как я ни старался, у меня не получалось в низкоуровневом коде. Там есть проблемы, например пока отсутствует alloca.
dmitryikh Автор
09.12.2017 14:45Про -DNDEBUG спасибо! Я обязательно исправлю этот недочет. Возможно, цифры изменятся.
Код ужасно оптимизирован
Этот комментарий нам никак не поможет, напишите конкретное место и как его улучшить.lieff
10.12.2017 16:17Ну тут довольно много писать просто придется. Попробую которко. Во первых указателями на ноды оперировать слишком жирно, особенно в 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.
phponelove
12.12.2017 14:36+1Этот комментарий нам никак не поможет
Ну что там далеко ходить — я взял и решил добить 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; };
Ничего особо не проверял — если работает, замените им своё.
SBKarr
09.12.2017 12:07Для наглядности было бы неплохо добавить голый C. И сравнение с -O2 и -O3. И clang для C/C++. А то в данном случае не совсем понятно, что с чем сравнивается.
broken
09.12.2017 12:56+1Есть некоторые недочеты в статье, один из них — Option — это не «типа исключение», а вполне себе отдельный тип для своих нужд. В качестве типа для содержаний ошибки он никогда не используется, так как для этого есть тип `Result`. Далее, дурить borrow checker не нужно, он в 95% случаев все правильно говорит, и городить приходится не «хитрый» код, а именно *правильный* код с точки зрения всех возможных проблем.
DarkEld3r
10.12.2017 17:48+3и городить приходится не «хитрый» код, а именно правильный код с точки зрения всех возможных проблем.
Всё-таки над non-lexical lifetimes не зря работают. Иногда borrow checker действительно заставляет делать "дополнительные приседания", хотя, как по мне, это не особо страшно.
Nirvano
09.12.2017 12:56Тут можно видеть странный синтаксис наследования #[derive(Debug,Eq)]
Не знаю, знаете вы или нет, по этому предложения для меня непонятно. Но это не синтаксис наследования, это автоматическая реализация трейтов Debug и Eq.
TargetSan
09.12.2017 13:00+2Побуду занудой
fn _merge(left_slice: &mut [u32], right_slice: &mut [u32]) -> u64
https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut
И никаких мучений с
fn _merge(vec: &mut [u32], left: usize, mid: usize, right: usize) -> u64
humbug
09.12.2017 13:54Идиоматически правильно использовать
Option
, чтобы показать, нашли мы что-то или нет. Поэтому результат возврата binary_search будет Option, а не i32. И пропадут все явные касты, которые совершенно не нужны. Советую посмотреть реализацию в libcore.
Удачи в изучении
Rust
и спасибо за статью!
Googolplex
09.12.2017 14:21Спасибо за статью. Есть несколько замечаний по коду, но в целом у вас вполне себе идиоматичный Rust получился.
Бинарный поиск:
- Передавать
&Vec<u32>
нет никакого смысла — в таких случаях нужно передвать срез&[u32]
, это делает сигнатуру немного чище и обобщённее. - Для индексов стоит использовать
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 сделать две мутабельные ссылки в один объект, даже если они не пересекаются, не получится.
Кодирование Хаффмана:
- Передавать
String
как аргумент, как правило, не нужно — но в вашей ситуации это оправдано, потому что вы этот объект немедленно добавляете в HashMap. Во второмmatch
, однако, вызыватьclone()
на ней не нужно, потому что дальше по коду строка нигде не используется и её можно переместить в оператор конкатенации и дальше в HashMap. - Конструкцию типа
match &self.left { &Some(ref leaf) => ... }
вполне можно заменить наmatch self.left { Some(ref leaf) => ... }
, и символов станет немного поменьше.
Riateche
09.12.2017 15:43> Есть несколько замечаний по коду, но в целом у вас вполне себе идиоматичный Rust получился.
Не соглашусь. Использование -1 для индикации отсутствия результата вместо Option<usize> — грубая ошибка. В Rust столько усилий положили на то, чтобы было легко выражать и обрабатывать случаи отсутствия значений, а автор всё это игнорирует. Ценность этого примера для людей, которые хотят изучить Rust — даже не нулевая, а отрицательная. И это не единственная проблема такого рода в статье.Googolplex
09.12.2017 21:05+1> Использование -1 для индикации отсутствия результата вместо Option — грубая ошибка.
Собственно, это было как раз первым пунктом моих замечаний.
dmitryikh Автор
09.12.2017 18:45Спасибо большое за замечания! Я исправлю код в репозитории и в статье.
Насчет возврата -1 изbinary_search
, как так получилось: изначально в задаче в онлайн курсе было такое требование (возвращать -1, индексация с 1), изначально я решал данную задачу наC++
и, недостаточно подумав, переписал так же наRust
. Действительно, лучше возвращатьOption
, а при записи результата делатьunwrap_or(-1)
- Передавать
bearad
09.12.2017 14:43Psychopompe
09.12.2017 16:03Слышал, что это не самый эффективный способ сравнивать производительность.
bearad
10.12.2017 05:28А как ещё оценить «производительную стоимость» абстракции, создающиеся любым языком высокого уровня?
Берем один и тот же алгоритм и реализуем его на разных инструментах, замеряя время выполнения каждого, какие ещё тут варианты есть?
П.С. аргумент «я слышал» — это же не серьёзный дискус.Psychopompe
10.12.2017 15:59Да, я знаю, что апелляция к личному опыту — плохая привычка.
Просто нужно понимать что конкретно тестит каждый бенч, и из этого оценивать.
Из-за того, что это самый популярный сборник бенчей, то всё упирается в странные правила и в желание людей с ними разбираться. То есть самая быстрая прога та, которую лучше всего реализовали. Сам язык не при чём.
Вот, что я смог найти из старых дискуссий.bearad
11.12.2017 08:44+2То есть самая быстрая прога та, которую лучше всего реализовали. Сам язык не при чём.
Снова предположение без фактов и доказательств.
Реализации и тесты алгоритмов открыты, вы можете сами убедиться в честности и способностях участников.
SharplEr
09.12.2017 14:45+1Два замечания:
1. Использование gcc для компиляции C++ в данном случае не правильно. Дело в том, что Rust использует LLVM в качестве бекенда, а gcc нет. Надо было использовать Clang, иначе твой бенчмарк сравнивает не столько Rust vs C++ сколько LLVM vs gcc.
2. В статье об этом не сказано, но в коде я кажется вижу, что вы замеряете время вызова функций руками (вижу в этом файле common/measure/src/main.rs ). В Rust в ночной сборке есть модуль для бенчмарков и надо использовать его, что бы получить более надежные результаты. Я не C++ программист, но уверен у них тоже есть своя либа для бенчмарков. Руками такое делать не стоит т.к. есть миллион граблей на которые можно наступить и получить неправильный результат.khim
09.12.2017 17:07-1Я не C++ программист,
Заметно.но уверен у них тоже есть своя либа для бенчмарков.
На чём, извините, эта уверенность построена?
Да, в C++ есть такая либа. И не одна. Десятки их, если не сотни. Если мы сейчас начнём между ними выбирать, то «утонем».
SPECи, coremarkи и прочие замеряют время работы программы. Тупо. По секундомеру. 5-7 запусков, в зачёт идёт лучший результат (идея в том, что OS может замедлить программу 100500 разными способами, а вот ускорить — так это вряд ли). Разумеется программы подобраны так, чтобы работать несколько минут — что позволяет пренебречь временем загрузки программ и временем ввода-вывода.
Руками такое делать не стоит т.к. есть миллион граблей на которые можно наступить и получить неправильный результат.
Если вы сравниваете отдельные процедуры с целью микрооптимизаций — то да, не стоит. Если же вы хотите сравнить два языка, то лучше всего — программаtime(1)
, так как она точно работает одинаково для разных программ.
dmitryikh Автор
09.12.2017 18:391. Согласен, как только доберусь до машинки, на которой проводил замеры, соберу данные по clang. Постараюсь взять clang такой версии, чтобы версия LLVM совпадала с Rust.
2. Я измеряю не время вызова функций — это действительно сложная история и есть много готовых решений. Я измеряю полное время выполнения программы. Я отказался от программы time, т.к. она под Mac os x не выдавала результат с нужной мне точностью. Программа measure, на которую вы указали, — это лишь попытка обойти это ограничение и иметь возможность проводить замеры на Linux & Mac os x.SharplEr
11.12.2017 20:44+1Полное время выполнения программы это вообще нестабильная история. Для близких пар языков вроде C++ и Rust это может быть не особо существенно, но конечно Java и Rust так уже сравнивать совсем нельзя. Но и в данном случае я бы перестраховался — вроде Rust генерирует бинарники по больше чем C++ так как больше тянет из стандартной библиотеки и это может влиять на время запуска.
Так же не очень стабильно ведет себя единичный запуск. Даже для языков, где нет сложных адаптивных JIT-ов и GC, всё равно второй запуск выполнится быстрее потому, что CPU имеет некоторый элемент адаптивности внутри (предсказатель переходов и кэш). Поэтому по хорошему надо гонять бенчмарк несколько раз пока время не перестанет изменяться, и взять уже только стабильные замеры времени для ответа.
И ещё я забыл сказать: у вас в замерах не хватает погрешности. Всё таки время выполнения это случайная величина. Насколько я помню JMH для Java при подсчете погрешности считает, что время распределено нормально. Думаю это приемлемо, хотя конечно я бы взял какие-нибудь квантили — чисто на всякий случай.
Тут ещё такой момент, о которым вы тоже пишете, от которого я сам не знаю как избавиться: у языков всё таки разные стандартные библиотеки и результаты больше говорят о разницы в реализации структур данных в библиотеке, чем о чем-то другом. По хорошему надо отдельный бенчмарк сделать для этих структур в обоих языках, а потом оценить их вклад в общее время выполнения теста и как-то его вычесть наверное.khim
11.12.2017 21:46+1Для близких пар языков вроде C++ и Rust это может быть не особо существенно, но конечно Java и Rust так уже сравнивать совсем нельзя.
А почему нельзя-то? Что вы такое собрались делать, что время запуска для вас неважно, а скорость работы — важна?
Что-то долгоживущее — это, обычно, что-то в чём вы язык выбирать по скорости не будете. Писать плагин для emacs'а вы будете на Lisp'а, а для CLion'а — на Java или Kotlin'е. Rust ни там, ни там не подходит. И не из-за того, что он медленный.
А если вы делаете отдельную утилитку — то скорость её работы будет определяться, в том числе, временем запуска. Писать её на языках, в которых рантайм запускается настолько долго, что на этом фоне незаметно, сколько времени сортируется массив на несколько миллионов элементов — просто глупо. И да, если в результате этих замеров «неожиданно» окажется, что python или perl лучше и быстрее java… то это потому, что так оно и есть — для подобных задач, разумеется…
Boris_B
10.12.2017 13:16Rust на данный момент подразумевает использование LLVM, а в C++ можно использовать как gcc так и clang. Поэтому если задачей является сравнение производительности, то сравнение c++ скомпилированого g++ и rust скомпилированого llvm вполне корректно. Если бы вопрос был в том какой из языков llvm лучше оптимизирует, тогда Ваше замечание было бы справедливо.
glebpom
09.12.2017 15:06Попробуйте заменить стандартный HashMap на FnvHashMap из crates.io/crates/fnv
behindyou
09.12.2017 16:03Время работы программы является случайной величиной, и сравнивать между собой единичные испытания некорректно.
dmitryikh Автор
09.12.2017 16:05Процитирую статью:
Делалось 10 прогонов каждой задачи на каждом наборе данных, далее результаты усреднялись
Можно было брать больше прогонов, но дисперсия времени выполнении была невелика.khim
09.12.2017 17:11Лучше не усреднять, а брать минимум. Обычно первые один-два прогона чуть медленнее.
Но самое главное — выставить правильный governor, иначе у вас и после 20 запусков программа будет всё ускоряться и ускоряться…0xd34df00d
11.12.2017 21:40+1Но самое главное — выставить правильный governor, иначе у вас и после 20 запусков программа будет всё ускоряться и ускоряться…
Может и наоборот быть, если intel pstates включился. Первые запуски будут под турбобустом, а потом ой.
Я для бенчмарков на ноуте специально перезагружался с выключенными pstates.khim
11.12.2017 21:49+1Есть и ещё более патологические случаи. Мы как-то пытались померить скорость работы на ARM'е. Оказалось, что ни на одном доступном нам телефоне ничего толком померить нельзя с точностью до 1% — ибо перегреваются они, собаки.
Спас ChromeBook (уже не помню какой модели): он достаточно велик для того, чтобы на однопоточных тестах, по крайней мере, охлаждения хватало и тормозов бы не было.
Jef239
09.12.2017 16:36Будет ли программа считать 10 или 5 секунд — обычно не так важно. А вот будем ли мы её писать и отлаживать 3 дня или 10 дней это важно.
Что у rust со скоростью написания и отладки?devalone
09.12.2017 18:39Будет ли программа считать 10 или 5 секунд — обычно не так важно
В системных языках, каким себя позиционирует Rust, важно.Jef239
09.12.2017 19:24-2Если тиражи штучные — неважно. Мне намного важнее лишние 2-3 тысячи долларов за написание кода, чем 5 долларов за более мощный проц.
P.S. не говоря уже о том, что если скорости не хватает — то изменением алгоритма получается выигрыш в разы, а не на проценты.
P.P.S Скорость — это всего лишь фетиш с++ников.JekaMas
09.12.2017 21:21+5От задач зависит. Не бывает серебряных пуль или «no free lunch».
Алгоритмы менять до бесконечности невозможно — найдется предел около текущего state of the art.
5 долларов на лишний проц — это прекрасно, пока речь не идет о системах с сотнями, тысячами и более серверов.
Думаю, любой инженер должен понимать, что «от задачи зависит».Jef239
10.12.2017 07:56Алгоритмы менять до бесконечности невозможно — найдется предел около текущего state of the art.
Поскольку у нас один и тот же LLVM и для С++ и для Rust, то не так сложно поменять код для достижения ровно того же результата. Лет 50 назад это было любимой темой — как поменять код, чтобы компилятор его оптимальней скомпилировал. :-) Так что какие-нибудь обработчики прерываний в итоге будут иметь одинаковый бинарный код и для Rust и для С++.
5 долларов на лишний проц — это прекрасно, пока речь не идет о системах с сотнями, тысячами и более серверов.
Но это уже не штучные тиражи. И потом — ну сэкономили вы 5 тысяч долларов на тысяче серверов. Сколько недель работы команды вы этим деньгами оплатите?
Если я не ошибся в подсчетах — то всего лишь неделю для команды в 5-10 человек.
Думаю, любой инженер должен понимать, что «от задачи зависит».
Угу. Только приведите мне пример задачи, где реально выгоднее замедлить на 50% разработку ради получения на 50% более быстрого кода? Мне как-то кажется, что это или экзотика или opensource, где разработчикам не платят.VEG
10.12.2017 09:56Но это уже не штучные тиражи. И потом — ну сэкономили вы 5 тысяч долларов на тысяче серверов. Сколько недель работы команды вы этим деньгами оплатите?
Давайте пересчитаем.
Угу. Только приведите мне пример задачи, где реально выгоднее замедлить на 50% разработку ради получения на 50% более быстрого кода?
На 50% более быстрый код, говорите? Ну так значит вместо тысячи серверов у нас будет уже 500. Предположим, сервера у нас — середнячки, по 2500$ каждый. Экономия — 1250000$.Jef239
10.12.2017 11:07на 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%.
Так что пример не катит…
VEG
10.12.2017 11:54+3Если важно именно как можно скорее выкатить продукт — то можно оптимизацию отложить на потом. Если же продукт уже на рынке, и выполняет свои задачи, то можно потратить время и на оптимизацию. Facebook писался сразу на обычном PHP, а потом они начали вкладывать деньги штуки типа HHVM для оптимизации. Последние версии PHP также гораздо быстрее предыдущих — разработчики потратили немало времени на оптимизацию. Правда, конечные веб-разработчики часто съедают возросшую проворность PHP использованием тяжёлых фреймворков.
Хороший антипример: Skype на мобильных платформах. Он похож на неповоротливого монстра. В итоге этим IM я практически не пользуюсь на телефоне. Только при крайней необходимости запускаю, и каждый раз это боль. Кажется, я не один такой: на мобильных платформах популярны совершенно другие IM, которые гораздо более шустры. И все они появились гораздо позднее Skype.Jef239
10.12.2017 13:05-1Напомню:
приведите мне пример задачи, где реально выгоднее замедлить на 50% разработку ради получения на 50% более быстрого кода?
Вот и получается, что таких примеров мало. Я ведь не про оптимизацию «вообще», а про конкретный баланс.
Много примеров, где стоит потратить 5% времени ради оптимизации на 200-300%. А вот 50% времени разработки на 50% скорости — редкость. Тому же скайпу 50% не помогут — он все равно будет тормозить.
Котенок в свое время ускорил PHP в 10 раз. Это дало общий прирост скорости… всего в два раза. Трудоемкость оценить сложнее, но видимо где-то не большее 10-15 процентов.
Вот такую оптимизацию — я понимаю. А 50% ускорения в обмен на 50% трудоемкости — не понимаю.VEG
10.12.2017 13:14А вот 50% времени разработки на 50% скорости — редкость. Тому же скайпу 50% не помогут — он все равно будет тормозить.
Если прирост производительности будет 50% за год разработки даже с 75% замедлением появления новых фишечек из-за этого, то глядишь за столько лет сколько он тормозит — им стало бы приятно пользоваться. И кто-то даже может быть начал бы рассматривать его как адекватный мобильный IM. А тормозит он с самого появления в 2010 (говорю про версию под Android).Jef239
10.12.2017 13:59Увы, 50% — это разовый прирост. И то, поскольку и С++ и Rust используют один и тот же LLVM, то достичь того же прироста можно без смены языка — просто написав более удобно для оптимизатора.
devalone
09.12.2017 21:41+3Бред полный, есть задачи, где скорость важна, есть задачи, где нет и вы тут со своими 5 долларами вообще ни при чём.
Jef239
10.12.2017 08:01Ну так приведите пример, где реально выгоднее замедлить на 50% разработку ради получения на 50% более быстрого кода.
JekaMas
10.12.2017 10:16+2Примеры такие редки, не спорю. И в подавляющем большинстве случаев стоит отдать предпочтение языку и технологиям, на которых разработка быстрее и стабильнее.
Но вы просили пример: допустим у меня нет возможности выбрать железо — мобильная разработка, в ней преимущество в скорости может быть оправдано, поскольку увеличит круг пользователей. Пример: есть ли разница во времени прокладки маршрута по карте между 5 секундами и почти 8ю, когда у твоих конкурентов — 2секунды?
Или обработка данных на локальной машине, чтобы попробовать модели разные: разница между 10часами и 5ю будет значительной.
Стоит войти в условия ограниченности ресурса по вычислениям, например, и задача выжать еще 50% даже ценой смены языка перестает выглядеть фантастично.Jef239
10.12.2017 12:10-1А что лучше: запустить приложение сейчас или через год? А для ускорения — может стоит пожертвовать памятью и хранить там предвычисленные значения? А локальную машину я бы сменил на быстрый сервер.
То есть очень редкая должна быть задача, чтобы менять язык ради копеечного ускорения выполнения. Вот ради скорости разработки (и, главное, отладки) — да, сменить стоит.
Всё дело в том, что оба языка компилируются одним и тем же LLVM. Поэтому выгода нестабильная и сильно зависит от исходного кода. Переписать пример чуть иначе — и выгодней станет другой язык.JekaMas
10.12.2017 12:35На это ответит только бизнес и конкретные обстоятельства. Всякое бывает. У мобильных устройств не всегда есть связь или мы делаем систему с нулевым доверием.
Или у нас уже есть тройка спецов на чудо быстром языке, которых схантили с успешного проекта конкурента, и мы можем использовать их опыт.
Задачи и условия бывают разные. Чаще всего выигрыш по скорости в 2-3 раза относительно C или C++ не стоит значительно большей разработки и увеличения рисков на поддержке (мало спецов). Но от этого ваше изначальное утверждение верным не становится.Jef239
10.12.2017 13:13-1Повторю свое изначальное утверждение:
Будет ли программа считать 10 или 5 секунд — обычно не так важно. А вот будем ли мы её писать и отлаживать 3 дня или 10 дней это важно.
Не вижу в вашем ответе опровержения этого тезиса. Более того, с первой половиной тезиса вы уже согласись, сказавПримеры такие редки, не спорю.
Вы хотите опровергнуть вторую половину тезиса?
VEG
10.12.2017 10:20+1Сама 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% ускорение для такой штуки — это реально очень много и ощутимо. Выигрыш от такой оптимизации сложно оценить в цифрах, потому что от неё по сути выиграли бы вообще все. Проспонсировать такую оптимизацию мог бы кто-нибудь, кто сам очень активно использует эту БД, и хотел бы в первую очередь сократить свои расходы на сервера, а польза для остального мира — это уже как бонус.Jef239
10.12.2017 12:24+1Rust ощутимо выгодней во времени отладки. Он страхует от кучи ошибок. И это даст возможность Мозилле не бежать вдогонку за хромом, а обогнать его.
А задач, где есть смыcл оптимизировать именно сменой языка — безумно мало. Напоминаю, что речь идет об одном и том же LLVM. То есть выигрыш есть лишь на одном из вариантов написания кода. Чуть подпилить код напильником — и мы получим одинаковый бинарный код и для C++ и для Rust.
Tippy-Tip
10.12.2017 12:27Вот с Mozilla крайне неудачный пример. 57 проворнее 52 из-за того, что в 57 не работают хоть и устаревшие, но все же популярные расширения, что вызывает негативную реакцию пользователей (поиск альтернатив, уход на форки или радикальная смена браузера). И это не проблема того или иного языка, а проблема с менеджементом проекта.
VEG
10.12.2017 12:30Я лично сравнивал Firefox 52, PaleMoon и Firefox 57. Всё без расширений. Firefox 57 был ощутимо проворнее. Обратное, думаю, можно будет увидеть на слишком старых машинах — требования к RAM, конечно же, несколько подросли. Накладные расходы на поддержку нескольких процессов.
Jef239
10.12.2017 14:01Они отключили устаревшие возможности и на этом получили выигрыш по скорости.
VEG
10.12.2017 14:14+1Вы не следите за развитием 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 не останется внутри кода интерфейса браузера — тогда их поддержку можно будет убрать из кода движка. Но это случится не так скоро.phponelove
11.12.2017 02:31+1Вы не следите за развитием Firefox, поэтому делаете такие неверные выводы. Они переделывают движок.
Всё это — никакого отношения к firefox не имеет. Обе ссылки про серво, которое до сих пор не умеет к css.
В firefox добавили ксс-парсер, при этом никаких объективных свидетельств за то, что он быстрее — нет. Да и парсинг ксс — это копейки от времени рендеринга страницы. На этом нововведения(глобальные) заканчиваются. По крайней мере ни одна из ваших ссылок к ним отношения не имеет.
VEG
11.12.2017 08:05+1Обе ссылки про Firefox. Mozilla переносит некоторые наработки из Servo в Gecko, и обозвали этот проект Quantum. Если бы вы прочитали хоть немного то что по ссылке, то поняли бы, что это это не только про парсинг. Это ещё и про быстрый ответ на вопрос «нужно ли применять это правило для этого вот элемента?», которое браузер задаёт этому движку CSS каждый раз при каждом рендеринге любого элемента на странице. Там же объясняется почему этот процесс не так тривиален, как кому-то могло бы показаться.
phponelove
11.12.2017 20:23-2Обе ссылки про Firefox.
Нет, обе ссылки маркетинговый булшит.
Mozilla переносит некоторые наработки из Servo в Gecko, и обозвали этот проект Quantum.
Я уже сказал, что серво не умеет в css — это объективная реальность, но адепты, как всегда, её игнорируют.
Если бы вы прочитали хоть немного то что по ссылке, то поняли бы, что это это не только про парсинг.
Во-первых, по ссылки ничего не написано про firefox — и это ясно и понятно, ведь вы ничего мне не показали, и не покажете. Во-вторых, что за привычка такая — болтать и не показывать?
которое браузер задаёт этому движку CSS каждый раз при каждом рендеринге любого элемента на странице.
Никакого, хоть как-то работающего, движка css в серво не существует, именно поэтому его не может существовать в firefox. Никакой движок ни в какой firefox никто не добавлял.VEG
12.12.2017 00:00+4Во-первых, по ссылки ничего не написано про 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.
Более того, в описании новшеств Firefox 57 также чётко упоминается, что Quantum CSS уже в 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'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 (которая действительно очень сыра) не может отрендерить какое-то правило правильно — это не вина этого компонента. Он не отвечает за это.phponelove
12.12.2017 00:44-3Похоже, что вы всё же не читали что там написано. А там чётко сказано:
Нет, нет. Это так не работает. Недостаточно что-то кинуть и сказать, что «я прав» — надо вывести это «прав» из ссылки. Вы утверждали то, что там что-то есть, кидая ссылки. В которых ничего про «есть» не сказано, а то, что описывается — никакого отношения к 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, который «уже готов к внедрению».
И опять же, враньё. Вы всё перепутали — демка не не может отрендерить какую-то часть правил — она не может отрендерить НИЧЕГО. А даже те пару правил, которые работают, которые и показали в демки — на самом деле не работают, они рисуются с артефактами и дерьмо анлиалиасингом. А видео, на котором запечатлена работа демки — каким-то странным образом дерьмово пожато, что никаких артефактов и качество отрисовки — на нём не видно. Вот ведь какое совпадение, да?
Ну и да, она побеждает хром только во влажных мечтах. В конечном итоге — что мы имеем? Враньё про то, что побеждает хром. Враньё про то, что она что-то там умеет. Враньё про то, что она где-то там быстрее. Враньё про то, что она где-то там( в параллельной вселенной, наверное) готова ко внедрению.
Но вы продолжайте, нет причин не верить мозилле. Блаженны верующие — верьте дальше, ведь объективная реальность — это не для вас.VEG
12.12.2017 01:09+2Вы опять мимо. 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.
Вам стоило бы немного немного разобраться в тех вещах, о которых вы пытаетесь говорить. На этом я завершаю разговор с вами, ибо уже очевидно, что продолжать его нет смысла.phponelove
12.12.2017 02:20+1Вы опять мимо.
Опять ответов нет, а трёп есть.
Quantum Render — это не про поддержку каких-то свойств в CSS.
Никто не говорил про поддержку, о поддержке говорилось в контексте демки и servo. Враньё номер раз.
Вам стоило бы немного немного разобраться в тех вещах, о которых вы пытаетесь говорить. На этом я завершаю разговор с вами, ибо уже очевидно, что продолжать его нет смысла.
И на основании вранья делаются какие-то выводы, а вернее делается слив.
Артефакты — это свойство именно композитора. Это первое. Второе — нерабочий ксс не обязательно, а вернее совсем не обязательно следует из нерабочего уровня растеризатора. Поэтому мы видим артефакты композитора, видим другие артфекты и мы как минимум не знаем — на каком уровне они взялись.
Что мы имеем в конечном итоге? Рассуждения о движках, которых нет. Спустя десять лет впилили ксс-парсер на расте. Никаких бенчмарков нет, никаких объективных данных нет. Ничего нет, а трёп о том, что «супер-быстрый» есть.
Далее мы пытаемся впиливать ещё один хелворд, который как-бы супер-быстр и работает, а самом деле он сливает хрому и не работает. Никаких объективных свидетельств за работает — нет, а за не работает — есть.
Ну и самое главное — произошел слив с основной темы, а именно интеграции какого-то раста в люсу и влияние его на её производительность. Никакой интеграции нет, кроме ксс-парсера. ксс-парсер — это капля в море. это 2% от браузера.
Никаких объектых свидетельств за то, что он быстрее старого( хотя из этого ничего не следует, ведь нет смысла сравнивать с аутсайдерами, когда надо сравнивать с хромом). Ничего нет.
В конечном итоге, мы облегчили интерфейс, впилили по треду на вкладку, добавили хелворд на расте и трубим везде о том, что «быстрее» он стал из-за раста. Хотя никто никакой связи не показал и не доказал.
Ну и как всегда, мы сравниваем раст и кресты, но при этом сравниваем ни хром и firefox, а firefox с firefox — т.е. калеку с новыми изваяниями. Почему-то хрому для того, чтобы уделывать firefox никакого раста не нужно. Почему мы сравниваем ни с хромом? А причина проста — вранья, манипуляции и маркетинг. Что он делает на техническом ресурсе? Непонятно.
Tippy-Tip
11.12.2017 09:59-1Ну вот Вы сами ответили:
Поэтому сейчас, во время больших внутренних изменений в браузере, они отключили поддержку старых расширений. На самом деле она ещё имеется в браузере, и в ночнушках (и Developer Edition) можно активировать её обратно.
Если в коде была скомпилирована такая конструкция (далее идет условный код):
if UseOldAPI { //Тут работаем со старым API};
то менеджер памяти, увидев, что UseOldAPI == false, не будет переводить блок, который выполняется при UseOldAPI == true, из долговременной памяти в оперативную, поскольку исходит из парадигмы «Оперативная память в многозадачных ОС – очень ценный ресурс, его нужно расходовать экономно». И менеджеру памяти ОС все равно компилятором с какого языка была собрана программа, с Rust/C++/Pascal.VEG
11.12.2017 10:20Это всё равно не скажется на производительности.
Во-первых, менеджер памяти может выгружать из RAM неиспользуюмую память только страницами, по 4 килобайта. То есть если по коду разбросано много вот таких вот блоков «if», и все они не слишком велики — то большая часть этого кода всё равно будет оставаться в памяти.
Во-вторых, кода, который занимался загрузкой старых расширений, там не так много. Представьте себе HTML-документ, в который вы динамически подключаете дополнительные скрипты, которые меняют поведение этого HTML-документа как им вздумается. Вот примерно так и работали старые расширения. Код, который загружал их — крошечный, по сравнению с остальным кодом браузера. Погоды он не делает. Расширения сами по себе могли сильно замедлять работу браузера (особенно самые старые, которые работали с интерфейсом браузера через синхронные вызовы), но не сам код их загрузки.
К слову, этот код загрузки старых расширений на самом деле даже не отключён. Это я дезинформировал вас. Он работает. Ему просто запретили загружать пользовательские расширения. А вот расширения, подписанные самой Mozilla (не addons.mozilla.org!) — работают, и Mozilla этим пользуется для расширений, которые нельзя сделать средствами WebExtensions. Подробности вот тут, смотрите «legacy extension», как видите даже в обычных релизах они доступны, но только для расширений от самой Mozilla.
VEG
11.12.2017 10:26Думаю, если задаться целью, то можно станцевать как-нибудь так, и таки получить возможность устанавливать старые расширения в обычных релизных версиях браузера.
DancingOnWater
13.12.2017 09:21Все системы реального времени
Jef239
13.12.2017 10:23+1Это вы сильно погорячились. Ну как пример: FreeRTOS написана на Си, причем портируется куда угодно. И так и поставляется — в исходных кодах на Си. Хотя, если её скомпилировать на ассемблер, а потом подчистить получившийся код руками — можно будет получить 50% ускорение ценой даже не 50, а 5% времени разработки. И что? Разве такое часто делают?
Процессорная мощность, которая тратится на RTOS — это обычно сильно меньше 1% времени задачи. Так что нету смысла её переписывать на ассемблер.
DancingOnWater
13.12.2017 10:40+1Во-первых, не надо все системы системы реального времени сводить только к операционным системам.
Во-вторых, не раз и не два видел, как ради скорости, народ плюет на готовые решения и пишет небольшие ОС, выполняющие только те задачи, которые нужно.DrLivesey
13.12.2017 11:11+2Во-вторых, не раз и не два видел, как ради скорости, народ плюет на готовые решения и пишет небольшие ОС, выполняющие только те задачи, которые нужно.
Чем более ограниченой является задача, тем проще заниматься оптимизацией кода под нее.
Jef239
13.12.2017 11:12+1А если это не ОС, то нам нужны резервы производительности:
- на пиковую нагрузку
- на дальнейшее развитие алгоритмов
- на долговечность процессора (при использовании 100% CPU обычные процессоры живут годы, а не десятилетия)
В итоге у меня запас по производительности от 5 до 80 раз. Только когда у нас миллионные тиражи ширпотреба, себестоимость изделия становится важнее резервов.
Лично я ускорял ассемблерный код всего дважды. Один раз по делу — 300 строк для OMRON CV1000. Это была добавка к уже работающим программам контролера, обеспечивающая запись переключений в кольцевой буфер. Ну и там действительно брались таблицы выполнения инструкций и оптимизировалось. В остальной части системы (на PC) резерв по производительности был 10 тысяч раз и достигнут он был изменением алгоритмов, а не оптимизацией кода.
Второй раз — это было в молодости. Чисто спортивная развлекуха. Я сократил Flist (однооконный файловый менеджер) на 40% бинарного кода вместе с исправлением ошибок и добавлением функций. Но он изначально был на ассемблере.DancingOnWater
13.12.2017 11:37+2Только когда у нас миллионные тиражи ширпотреба, себестоимость изделия становится важнее резервов.
Значит мы встречались с разными задачами. То, что я видел — камень используется почти под 100%. а более мощный нельзя поставить по целой куче причин.Jef239
13.12.2017 11:52+1А на пиковой нагрузке реалтайму кранты? Или это такой реалтайм, что на пике можно сильно медленней работать?
DancingOnWater
13.12.2017 12:03+1А третий вариант того, что временные диаграммы хорошо просчитаны и пик немногим отличается от нормы исключаем?
Jef239
13.12.2017 12:31+1Ну или пик — или «немногим отличается от нормы». Как бы московские холмы — это все-таки не горные пики. :-) Ну да, иногда бывают системы без пиков.
0xd34df00d
11.12.2017 21:49+1P.S. не говоря уже о том, что если скорости не хватает — то изменением алгоритма получается выигрыш в разы, а не на проценты.
Мой любимый (пусть и очень редкий в моей практике) пример: реализация random forest на плюсах и на хаскеле. На плюсах тот же алгоритм получился на пять порядков быстрее.
Правда, на плюсах я его и писал и отлаживал пару недель, и там ну очень много ансейф-хаков.Jef239
12.12.2017 09:52+1Не противоречит, вы бы ещё FOCAL привели как пример. А в данном случае мы имеем два фронтенда к одному и тому же LLVM с одинаковой моделью памяти. Если у нас есть исходный код низкого качества — то мы получим отличия в бинарном коде. Но если мы начнем оптимизировать код — бинарный код для обоих языков будет практически одинаков.
При этом выигрыш одного или другого языка больше зависит от версии LLVM, чем от самого языка. А то, что мы видим в статье — это преимущества LLVM перед GCC,
Если не согласны — объясните, в чем принципиальные отличия бинарного кода, сделанного одним и тем же компилятором?0xd34df00d
12.12.2017 19:26+1Давайте вместо объяснения принципиальных отличий я просто скажу, что пользовался llvm'ом как бекендом и в случае хаскеля? ghc умеет.
Одинаковости бекенда недостаточно.Jef239
12.12.2017 19:52+1у хаскеля — другая модель памяти. Там ведь сборка мусора, вроде?
Мы вообще что сравниваем? качество библиотек, стиль написания, качество компиляции или язык?
И на Си и на Rust можно написать как на фортране — со статическим распределением памяти. И это будет наиболее быстрый вариант, ибо куча — вещь не очень быстрая. И в этом случае скорость для Си и Rust будет примерно одинаковая и зависеть больше от версии LLVM.
Другой момент, если вы сравниваете типичные стили. Но кто вам мешает писать на С++ в стиле Rust?
Мораль в том, что скорость исполнения — второстепенны параметр. А вот скорость написания и отладки — важна. И если Rust обгоняет С++ по скорости отладки раза в 2 — значит есть смысл на него переходить.0xd34df00d
12.12.2017 20:24+2у хаскеля — другая модель памяти. Там ведь сборка мусора, вроде?
Там сборка мусора — деталь реализации, с которой компилятор может обращаться, вообще говоря, очень вольно. И достаточно умному компилятору никто не мешает свести нагрузку на GC к минимуму.
Собственно, примеры, когда хаскель отстаёт от плюсов не более чем в где-то в полтора раза на весьма вычислительных задачах, у меня тоже есть, и это очень неплохой результат, учитывая специфику.
И это будет наиболее быстрый вариант, ибо куча — вещь не очень быстрая.
Это смотря как с ней работать. В ghc'шном есть nursing area — это почти как стек, выделение памяти сводится практически к передвиганию указателя. Если вы укладываетесь в nursing area, то вам очень хорошо, у вас быстрые аллокации, быстрые деаллокации и вообще пони с радугами. Если вы из nursing area выходите, у вас долгоживущие данные, которые кладутся в более старшие поколения — всё становится сильно печальнее. Правда, тоже не всегда: если вы эти долгоживущие данные генерируете один раз и потом не меняете, то можете считать, что их и нет, на GC они влияния оказывать не будут при вашей очень маленькой подсказке.
Другой момент, если вы сравниваете типичные стили. Но кто вам мешает писать на С++ в стиле Rust?
Отсутствие механизма трейтов, вывода типов по Хиндли-Милнеру и так далее? Правда, это не в каждом проекте нужно.
Мораль в том, что скорость исполнения — второстепенны параметр. А вот скорость написания и отладки — важна.
Скорость написания и отладки конечно важна. Но не всегда скорость исполнения — второстепенный параметр, иногда нельзя докупить ещё процессоров, ещё памяти, ещё чего-нибудь.Jef239
12.12.2017 20:45-2иногда нельзя докупить ещё процессоров, ещё памяти, ещё чего-нибудь.
Вот именно, что иногда. Да, иногда бывает, что никакого запаса по мощности на развитие нет, иногда бывает, что процессоры используется со 100% загрузкой, то есть в очень тяжелом режиме по долговечности. Но и то и то — это просчет менеджмента или нехватка денег.
mayorovp
12.12.2017 10:05+2Э… мне кажется, нельзя в принципе говорить "один и тот же алгоритм" применительно к программам на плюсах и Хаскеле.
0xd34df00d
12.12.2017 19:27+1А чего там, просчитали энтропию (делается одинаково) для каждого значения разделения дерева (for в плюсах, map в хаскеле), выбрали значение с максимальным значением энтропии (maximumBy в хаскеле, std::max_element в плюсах), рекурсивно запустились на поддеревьях.
devalone
09.12.2017 18:31Измерялось полное время работы программы, в которое включено чтение данных, полезные действия и вывод в /dev/null
А причёт тут тогда заголовок, который гласит «Rust vs. C++ на алгоритмических задачах», когда ТС сравнивает Rust vs C++ на операциях ввода-вывода, которые обычно занимают больше всего времени.behindyou
09.12.2017 18:38Есть ещё время загрузки программы, которым можно либо пренебречь, только если время выполнения на порядок превосходит первое. Напрашивается использование Elapsed Timer'а.
Tippy-Tip
09.12.2017 18:31Мне кажется странным: записывать в плюсы языка жёсткое ограничение строкового типа. По-моему это надо относить к нейтральному замечанию, как и в этом случае:
+-В Rust отсутствуют исключения, обработку ошибок необходимо делать после вызова каждой функции. Причем Rust не позволит получить возвращаемое значение, если вы не обработаете случай неуспешного завершения функции. Есть макросы и встроенные конструкции языка, которые позволяют упростить эту обработку и не сильно раздувать код проверками.
dmitryikh Автор
09.12.2017 18:33Вы про UTF-8 строки? Если вы используете только ASCII символы, то вы не заметите разницы, и кол-во байт в строке будет равно кол-ву символов в строке.
khim
09.12.2017 18:37Мне кажется странным: записывать в плюсы языка жёсткое ограничение строкового типа.
Потому что совместимость. В языках, в которых есть больше одного строкового типа преобразование между ними — это страшная головная боль.
Вариант, принятый в rust — близок к оптимальному: есть один строковый тип, который используется везде, где только можно — и есть дополнительные, которые используются там, где нужно. Про WTF-8 кодировку слышали? Вот она из rust'а…SinsI
10.12.2017 06:42Это не только не плюс и не нейтральное замечание, а очень крупный недостаток, так как из-за (потенциально) переменных размеров элементов все операции доступа к определённому элементу в строках выполняются за O(n), вместо O(1), что напрочь убивает всю производительность.
TargetSan
10.12.2017 12:28Не доступ, а поиск позиции. Доступ по известной позиции тот же O(1)
По той банальной причине, что во всех вменяемых системах юникод.
И UTF-8, и UTF-16 активно используют суррогатные пары.
UTF-32? Окей, 2Х-4Х оверхед по памяти. Но можно.
Вспоминаете про std::string? Там ровно те же проблемы, если вы положили внутрь многобайтную кодировку. Только при этом начисто отсутствуют средства доступа к символам.SinsI
10.12.2017 14:37В C++ полная свобода — всегда можно выбрать (или написать самому) реализацию, максимально соответствующую задаче.
А в Rust — как решить проблему вроде разбивания текста на строки по 80 символов?TargetSan
10.12.2017 14:47В Rust точно так же есть свобода написать свою реализацию.
Не совсем понимаю вас, в чём проблема либо держать счётчик знаков и добавлять конец строки на каждом 80-м, либо просто искать позицию 80-го символа.khim
10.12.2017 17:22+6Не совсем понимаю вас, в чём проблема либо держать счётчик знаков и добавлять конец строки на каждом 80-м, либо просто искать позицию 80-го символа.
Это типичный разговор с «апологетами UTF-16/UTF-32».
У них есть фича, с которой они носятся, как с «писаной торбой»: возможность обращаться к code pointам за O(1) (с ограничениями в UTF-16). Когда задаётся разумный вопрос: «ну и зачем оно вам?» — в ответо обычно предьявляется несколько «естественных» задач где это, якобы, помогает.
«Разбор полётов» неизменно показывает, что «помощь» оказывается ложной и, в результате, задача решается неправильно — но даже если после долгих поисков кому-то и удастся предьявить задачу, которая на UTF-16/UTF-32 решается действительно удобнее — это не изменит того факта, что UTF-8 (или WTF-8) строки Rust'а лучше. Именно потому, что не провоцирует делать быстро, но неправильно.
khim
10.12.2017 16:47+1В 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.SinsI
10.12.2017 20:06+1Контрольный пример: строка содержащая сначала XYZT 10 раз, потом XYZT 20 раз. Разбить её хорошо бы посередине, правильно?
Неправильно.
Требуется банальный text wrap для Lister'а. Поэтому режется ровно после 80 символа.
Однако для этого вам вообще никакой Unicode не нужен — достаточно windows-1251 или даже KOI8-R.
И это замечательно, что в C++ можно перейти к ним и не мучаться с потенциальной переменной шириной символов.
В Rust'е же её нужно учитывать всегда, даже если у вас текст из A-Z.
Как показывает печальный пример SQL Injection'ов, чем меньше свободы в таких вещах, тем безопаснее и надёжнее результат.TargetSan
10.12.2017 22:09+1И это замечательно, что в 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
Посмотрите, есть ли там то, что вам нужно.
khim
11.12.2017 01:33+1Требуется банальный text wrap для Lister'а.
Ok.
Поэтому режется ровно после 80 символа.
Прекрасно. А что вы будете делать со строкой, которая для своего показа требует от 80 до 160 колонок? Или вы про символы двойной ширины уже забыли?
Так почти вся иероглифика так устроена! Японцы с китайцами вас за такое не по головке точно не погладят!
Как показывает печальный пример SQL Injection'ов, чем меньше свободы в таких вещах, тем безопаснее и надёжнее результат.
Как показывает пример SQL Injection'ов подход «тяп-ляп и в продакшн», «у меня на компьютере работает — а дальше хоть трава не расти» порождает их в таких количествах, что проще код переписать, чем исправлять.
Использование же готовых библиотек типа ICU и сохранённых процедур — позволят сохранить ваши волосы мягкими и шелковистыми… и даёт возможность спать по ночам, а не ловить 100500 ошибок в коде, который вроде как, «давно отлажен».
И это замечательно, что в C++ можно перейти к ним и не мучаться с потенциальной переменной шириной символов.
Это замечательно ровно до тех пор пока ваш закачик/покупатель ваше поделие не попытается использовать — и откажется от этой затеи, потому что не сможет в него ввести собственное имя.SinsI
11.12.2017 17:51+1Там не только двойная ширина, но и куча других особенностей, которые требуются для правильного отображения, от написания в обратном порядке или символов -модификаторов до банального отсутствия нужных шрифтов — и от того, что в программе используется UTF-8 эти особенности не станут автоматически учитываться.
И если заказчик именно за это не платил, то лучше в принципе запретить такие вещи чем получать баг-репорты «у нас был ученик со странным именем и вся база данных накрылась» или думать «почему страница текста отрисовывается полсекунды?».khim
11.12.2017 18:17+3Вопрос не в баг-репортах, а в алгоритмах.
лучше в принципе запретить такие вещи чем получать баг-репорты «у нас был ученик со странным именем и вся база данных накрылась»
Фильтрацией такие вещи не делаются, увы. Вернее делаются — но очень быстро ломаются при добавлении функционала.
или думать «почему страница текста отрисовывается полсекунды».
А вот здесь, как раз, UTF-8 спасает. Ибо у вас нет соблазна делать 100500 раз вещи, которые, вообще говоря, при полной поддержке юникода требуют O(N), но конкретно у вас, кокретно у вас, конкретно в первой версии программы — укадываются в O(1).
Ибо изначально используя индексы, которые не связаны с позицией символа на экране, при работе со строками вы почти всегда можете сделать так, чтобы странице текста не отражалась посекунды. А вот если у вас уже есть куча кода, которая оперериует везде и всюду этими позициями — то переписать её бывает весьма проблематично.
То, что код на UTF-8 «ломается» в случае использования не только русского языка, но и даже каких-нибудь банальных o или u, если пользоваться индексом в строке как позицией на экране… это же прекрасно! Ошибки обнаружатся быстрее и быстрее же будут исправлены.
Та же идея, что и со статической типизацией, современным C++ и rust'ом вообще: чем раньше будет обнаружена ошибка — тем дешевле её исправить!
В UTF-16 же только эмодзи и спасают.
0xd34df00d
11.12.2017 22:01+2Хрен бы с ним с разбиванием.
Представьте себе, что у вас есть функция
startsWith(s1, s2)
, проверяющая, начинается ли строкаs1
со строкиs2
. Пусть она возвращаетtrue
для данныхs1
иs2
. Даже при этом вполне может оказаться, что если вы отs2
возьмёте префикс длиныs1
, то вы получится неs1
, а её префикс или чуть более длинную строку. Потому что декомпозиция.
Уникод и человекочитаемый текст вообще — это сложно. К счастью, почти всегда вам на самом деле это не нужно.
TargetSan
09.12.2017 22:43+4Если вам нужен прозрачный интероп с ОС, всегда можно
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 — так вздрогну.Tippy-Tip
11.12.2017 09:42Увы и ах std::string/std::wstring не удовлетворяют условиям задачи.
принудительная юникодная локаль для всех стандартных строк это хорошо. Как вспомню танцы с ними на Windows — так вздрогну.
А что не так с Юникодом ыв Windows?TargetSan
11.12.2017 17:24+2Тут я пожалуй слукавил, что проблема только в std::basic_string
Там несколько проблем, связанных конкретно с С++:
- std::string — в произвольной кодировке, зависящей от системы. При этом почти весь код принимает именно её.
- Ад
<locale>
и всего, что связано с ними - UTF-8 как локаль для консоли не работает по-моему до сих пор. Но утверждать не буду.
VEG
12.12.2017 00:17+2UTF-8 как локаль для консоли не работает по-моему до сих пор. Но утверждать не буду.
Кстати, в Microsoft рассматривают вариант обучения рантайма работать в UTF-8. И вы можете поставить свой голос там (и тут тоже). Идеальным был бы вариант, если бы ещё и все *A функции из Win32 API научились бы понимать UTF-8 как одну из локалей (которую программа выбирает сама себе при загрузке, например). К сожалению, сильно надеяться на такой вариант расширения Win32 API не приходится.
FoxCanFly
09.12.2017 19:33У вас кажется неэквивалентный способ чтения из stdin int-ов (в binary_search смотрел). В rust вы читаете сразу все в большой буффер, потом парсите его. В C++ вы читаете по одному, используя способности форматированного ввода std::istream. Это заведомо медленнее
dev96
10.12.2017 13:34Согласен. Когда-то экспериментировал с чтением файла 8мб размером:
- чтение по 2 байта ~ 400 мсек
- чтение по 2кб ~ 9 мсек
Laney1
09.12.2017 21:29+1мне не нравится функция read_line в коде на rust. В однопоточном коде правильнее один раз вызвать
stdin().lock()
, успокоить чекер, и передавать получившийсяBufRead
всем желающим.
chars
иsplit_whitespace
довольно медленные, естественно из-за юникода. Если юникод не нужен, лучше их не использовать.
SinsI
09.12.2017 21:34+1Просто убили тесты длительностью в сотые доли секунды.
При таких мизерных временах работы может столько всего мешающего вылезать, что ни о каком реальном сравнении и речи быть не может.
3dcryx
10.12.2017 01:13+3Глянул код c++. Это совершенно волшебные тесты. Мерить время ввода/вывода! + время сортировки!!! + собственно время бин поиска и называть это сравнением времени поиска!!!
Автор в курсе, что бинарный поиск работает "слегка" побыстрее первых двух операций?dmitryikh Автор
10.12.2017 11:15Наверное, вы не так поняли. Задача называется binary_search и состоит из ввода/вывода + сортировку массива A + поиск значений из массива B в массиве A. В таблице честно указана асимптотика задачи
O(N logN)
.
Хотя нехорошо, что сортировка, занимающаяO(N logN)
, может занимать больше времение, чем основная деятельность программы, описанная в ее названии (тут мы имеемO(M logN)
, где M — кол-во элементов в массивеB
). Возможно, в следующей ревизии статьи и кода, я буду подавать на вход программы уже отсортированные данные.
kuftachev
10.12.2017 03:30Автору не помешает послушать выступление Алексея Шипилева, осознать всю бренность бытия, а заодно и то, что большинство людей, которые пытаются сравнивать производительность, на самом деле сравнивают что-то типа глубины с температурой… И в общем больше не заниматься глупостями.
Там есть специальная профессия мерять производительность, называется Performance Engineer.BD9
10.12.2017 05:33Вы так говорите, будто эти ваши «Performance Engineer» появляются из Великого Ничто по указанию обученного волшебника, делают свою работу, а потом туда же исчезают.
ИТ — это про науку, а не волшебство. Человек что-то сделал — ну и хорошо. Желаете улучшить — код открыт. Чего же боле?
vmchaz
10.12.2017 05:55Мне интересно, зачем в памяти держать именно в виде UTF8?
WideChar оптимален для этих целей, а конвертить в UTF8 можно при необходимости, вроде записи в файл.TargetSan
10.12.2017 12:37+1Несколько возможных причин, а может и все вместе
- Компактней чем UTF-16. Где-то натыкался, что одна и та же статья на японской вики в UTF-8 занимает 83Кб, а в UTF-16 144Кб. А составные символы что там, что там будут.
- Совместима со стандартным ASCII по представлению. И там, и там байты.
- Совместима со стандартным ASCII по кодированию. Нижняя половина таблицы ASCII в UTF-8 выглядит точно так же.
- Не зависит от порядка байт. Может быть проблемой при передаче по сети.
VEG
10.12.2017 12:59Не вижу причин, почему UTF-16 мог бы быть оптимальнее. С первыми версиями стандарта Unicode казалось, что UTF-16 удобнее, потому что один кодпоинт гарантированно занимал 2 байта. Можно было легко получить длину строки из количества занимаемых ей байт, нельзя было случайно обрезать строку посреди символа. Но так как теперь один кодпоинт в UTF-16 может занимать и 4 байта, то эти преимущества исчезают. Зато появляется зависимость от порядка байт и повышенный расход памяти.
vmchaz
10.12.2017 16:41+1Кстати, а почему максимально возможный кодпоинт возрос до 4 байтов? Новых символов добавили или в чём-то другом причина?
VEG
10.12.2017 16:46+1Ну да, 65535 кодпоинов оказалось маловато. В последнем стандарте уже 136755 символов. Текущая схема кодирования UTF-16 позволяет использовать до 1112064 кодпоинтов.
vmchaz
10.12.2017 23:24+1ru.wikipedia.org/wiki/Юникод
41 эмодзи, а также пять символов изменения цвета кожи для эмотиконов
По-моему, юникод забрёл куда-то не туда.khim
11.12.2017 01:41+1По-моему, юникод забрёл куда-то не туда.
Туда-туда. Эмодзи эти использовались почти во всех популярных мессенджерах ещё до появления в Юникоде — и отказ от этих костылей в пользу стандарта можно только приветствовать…
P.S. На самом деле там одних иероглифов столько, что в два байта всё не влазит. Эмодзи же оказались первыми действительно распространёнными символами, которые потребовали исправить-таки программы, которые трактуют UTF-16 как UCS-2. А символы для изменения цвета кожи заставили-таки отказаться от иллюзии, что одна буква — это один code point. Спасиба им хотя бы за это…
Boris_B
10.12.2017 12:37Rust поддерживает итераторы (как генераторы в Python).
Не как генераторы, а как итераторы в Python. Генератор помнит контекст выполнения, а итератор нет. Генераторов в Rust вроде бы нет.
Вместо исключений функции возвращают объект Option, который содержит None или результат корректного завершения функции.
Возможно имелись какие-то конкретные функции, которые возвращают Option, но вообще заменой исключениям в большинстве случаев являются https://doc.rust-lang.org/std/result/ .
humbug
10.12.2017 15:07Boris_B
10.12.2017 22:01Я если что даже вторую версию книги по Rust не дочитал до конца. Для тех кому лень смотреть по ссылке в Rust есть экспериментальные API для создания генераторов. Лень разбираться, но похоже удобства как в питоне, где есть специальный синтаксис (yield) не предвидится. Если ошибаюсь, поправьте.
TargetSan
10.12.2017 22:15Выдержка по ссылке:
let mut generator = || { yield 1; return "foo" };
Два момента — двойственный тип результата и нестыкованность с итераторами. Но принципиально решаемо и, думаю, будет решаться.
DrLivesey
10.12.2017 15:15Возможно имелись какие-то конкретные функции, которые возвращают Option, но вообще заменой исключениям в большинстве случаев являются https://doc.rust-lang.org/std/result/ .
Тоже бросилось в глаза, но вы уже написали про это.
Kyushu
11.12.2017 12:38-1На сколько я понял Rust для вычислительных задач не планируется и на замену тому же фортрану не претендует? Поэтому и вопрос о совместимости библиотек не стоит?
Спасибо за информацию о Rust.
В любом случае, освоение нового это задача молодежи, так как использование привычных средств лучший способ создавать надежный софт.mayorovp
11.12.2017 13:06+2Откуда такие сведения? Rust, как и любой системный язык, хорошо подходит для вычислительных задач в плане быстродействия. Совместимость библиотек также не хуже чем в других языках.
Правда, выбранные решения заставляют рассматривать Rust скорее не как язык на котором будут использоваться библиотеки на Фортране, а как язык на которым было бы неплохо писать новые библиотеки. Тут у него больше преимуществ.
splav_asv
11.12.2017 21:37+3Лично я очень жду github.com/rust-lang/rfcs/pull/2000. Довольно сильно не хватает на настоящий момент.
potan
12.12.2017 12:39+2А можно в сравнение добавить clang? Интересно, в среднем Rust проигрывает из-за более высокого уровня языка или из-за более слабой оптимизации llvm. По идее, генерируемый Rust код должен лучше поддаваться оптимизации.
chabapok
13.12.2017 00:39+2Время — штука сложная, с ней надо осторожно работать. Сразу скажу, что в код я не вникал, а зачем я это все спрашиваю — написано вконце.
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. Можно ее погонять на разных тестах и сравнить показания счетчиков. Если счетчики будут сильно разными — это подозрение, что реализации алгоритмов не идентичны. Скорей всего, если приложить усилия, более медленный код можно как-то переписать, чтобы он был равен быстрому. Но это нужно «закапываться» в вопрос.
phponelove
13.12.2017 01:461. Можно ли узнать, каким именно образом измерялось время выполнения функций? Какая гранулярность вашего таймера?
Не имеет значения. Даже если разрешение таймера на два порядка больше секундомера — этого достаточно. Хотя такого, естественно, что нету.
Не было ли миграции потока на другое ядро во время выполнения функции, делалось ли thread affinity?
Зачем? Сколько бы там их ни было — это копейки на фоне десятых долей секунды.
Если это tsc, то поддерживает ли проц функции:
Там написано какой проц.
И правильно ли снимались показания счетчика времени (наличие rdtcsp в системе команд еще не означает, что мы этой командой пользуемся. rdtsc выполняется на общем конвеере команд, в то время как rdtcsp обеспечивает hb)?
Абсолютно не имеет значение. Длинна конвейера ну десятки тысяч тактов, а измеряем мы миллиарды тактов.
работа с данными — штука, которая нагружает определенным образом подсистему памяти.
Из этого, ровным счётом, ничего не следует.
Если меряешь время алгоритмов, которые перелопачивают массивы, то нужно мерять разные длины массивов.
Это итак делается.
Потому, что из за эффектов с кэшом, решающую роль могут оказывать совсем не те эффекты, которые были при тестах с другим набором или размером данных.
Какие такие эффекты с кешом? Каким образом увеличение датасета повлияет на кеш? Если у нас есть решение А и решение Б, которое обладают определённым паттерном обращения к памяти, то увеличение датасета паттерн не поменяет. Они могут в разное время, с увеличением датасета, вылезти за какой-то левел кеша/памяти, но из этого ровным счётом ничего не следует — всё это сгладиться.
Сложность так же вызывает то, что эффекты с кэшом сильно зависят от того, как именно в памяти лежат данные.
Абсолютно неважно то, как они там лежат. Важно то, как к ним обращаются, а это уже следствие того, как они лежат. Да и из этого так же ничего не следует — если решение А лежит криво, то на каком угодно датасете оно будет криво.
Так, известно, что перемножение матриц 512х512 работает медленней, чем 513х513.
Какое гениальное открытие, оказывается, что в рамках системы, которая завязана на степени — работать со структура степени двойки проще и быстрее.
Можно так же нарваться на коллизии адресов TLB страниц — и этим просадить производительность.
Показать сможете?
Это все что я насобирал, просто чтобы понимать, что задача не такая простая, как кажется на первый взгляд.
Всё это — никакого отношения к задаче не имеет. Во-первых, кто ставил условие о том, что оно должно быть оптимальней за рамками указанного датасета? Во-вторых, кто и где мерил тут «микро-эффекты»? Третье, всё это можно свести к одно — измеряй на разных датасетах, но это итак сделано и это известная все элементарщина.
Сложность задачи не в том, что-бы измерить, а в том, чтобы понять — что мы намерили. И ни одного слова про это от вас не поступало.
Особенно, если алгоритм выполнялся меньше нескольких секунд (и несколько прогонов алгоритма не всегда могут быть решением проблемы). Какие-то более сложные вещи, которые выполняются больше 1..2сек уже можно более-менее корректно сравнивать без учета большого кол-ва тонкостей и хитрых эффектов.
Никакие секунды тут не причём. Секунды относятся только к точности измерения и не более, а они будут точными уже на сотых долях секунды — никаких микро-эффекты тут уже ни на что не влияют.
Вы рассуждали о каких-то там кешах, но сейчас показали даже не 5%, а 0. Если у нас датасет один и тот же — абсолютно неважно сколько секунд мы его долбим. Хоть час — это ничего не изменит. Время работы может быть следствием увеличением датасета, но не всегда и тут вам просто повезло.
Но конкретно в нашем случае, скорей всего, если дать больше данных, то основное время выполнения съест подситема подтягивания данных из памяти в кэш. Она не зависит от языка — и времена сравняются.
Никакая подсистема памяти никакое время не ест. Никакие подтягиваний данных в кеш ни на что не влияют — я даже не знают что надо сделать, чтобы это повлияло на измерения ТС — построить цепочку из RA в один байт длинною в l3.
Но всё это, конечно же, к теме отношения не имеет. Если у нас одной реализации надо что-то там подтягивать, то и другой так же. Если нам нужны эти данные — мы в любом случае обязаны их подтягивать. И каким образом нам это помешает что-то измерить — неясно.
Утверждения о том, что язык ни на что не влияет — не состоятельны. Язык — это возможности, а так же те, кто на нём пишет. Если в каком-то языке нету возможностей, либо тех, кто сможет их применить — это проблема языка.
Существует множество ручек, которые можно крутить. Начиная от организации структур данных, свободную организацию которых — позволяют единицы языков. Заканчивая всяким nt. И то, это только относительно летенси-цепочек.
Ручек же для трупута ещё больше — там уже есть параллельность. Это не говоря о том, что данные надо не только читать.
Скорей всего, если приложить усилия, более медленный код можно как-то переписать, чтобы он был равен быстрому. Но это нужно «закапываться» в вопрос.
Это ни из чего не следует. Есть ли тот, кто сможет его переписать? А есть ли в языке те возможности, которые необходимы? В этом основная проблема.
splav_asv
13.12.2017 07:56+3Зачем? Сколько бы там их ни было — это копейки на фоне десятых долей секунды.
Не всегда — кэш сбрасывается.
Какое гениальное открытие, оказывается, что в рамках системы, которая завязана на степени — работать со структура степени двойки проще и быстрее.
В тот то и дело, что наоборот.
Иллюстрации про кэш: igoro.com/archive/gallery-of-processor-cache-effects
phponelove
13.12.2017 09:35+1Не всегда — кэш сбрасывается.
И? Уже после выявляются новые обстоятельства? Пусть он хоть 10раз сбрасывается — это ничего не изменит.
В тот то и дело, что наоборот.
Никакого дела нет, есть увиденная где-то статья и ретранслирование из неё, с умным видом, цифр в интернете. При этом не просто ретранслирование, а натягивание совершенно на другие кейсы.
А по поводу матриц, кто-то спастив число из статьи очень сильно себя подставил, ведь он не дочитал до:Why do the vertical lines stop at 4MB array length?
И вот если бы он дочитал, а потом посчитал, что 512х512 даже из даблов — это в 2раза менее 4мб, то он бы понял, что просто так пастить цифры их статьи глупо — можно попасть в лужу. Он в неё и попал.
Ну ладно, 513 для строк ещё можно объяснить тем, что имелись ввиду квадратные матрицы. Но тут уже подозрительно.
Да и к тому же, есть транспонирование.
Halt
За статью, безусловно, спасибо. Однако, подача материала, а главное, цель статьи меня смущают.
Мне кажется, что плюсер, прочитавший статью и не знакомый с Rust, в лучшем случае «пожмет плечами» или пробурчит «как на Фортране можно писать на любом языке программирования».
Сила Rust не только и не столько в итераторах, ссылках и семантике перемещения, сколько в иной философии и подходу к проектированию программ. Но из статьи этого понять не выйдет.
У меня по крайней мере, сложилось бы впечатление, вроде: «Мда… ну и зачем весь этот бред? Учитесь писать на плюсах, а не выдумывайте очередного его “убийцу”».
erlioniel
А можно как проходящему мимо человеку запросить более обстоятельный комментарий (а возможно даже и статью) про разницу в философии проектирования программ?
Halt
Rust — уникальный проект. И как язык и как экосистема.
Как язык он уникален тем, что ядро команды составляют великолепные специалисты по теории типов, которые, тем не менее, ориентируются на практичность, а не на академическую чистоту/новизну языка. В итоге получился язык с весьма мощной системой типов, который однако, можно успешно применять в повседневной разработке.
Философия языка — это сплав современных представлений computer science и
хиропрактик системного программирования, повернутых, однако, лицом к программисту-прикладнику.Ключевая, на мой взгляд, идея, идущая красной нитью через весь язык, — программист, как и любой другой человек не всесилен и склонен совершать ошибки. Соответственно, язык, который сваливает вину за промахи на программиста — плохой язык.
Да, тут я говорю и про C++. Программирование на C++ — это сделка с дьяволом. Ты получаешь большие возможности в обмен на свою душу, когда самолет под управлением твоей программы грохнется в океан. C++ пестрит ситуациями, приводящими к неопределенному поведению, о которых программист обязан помнить.
В технике давно пришли к выводу, что если в системе есть опасные агрегаты, то нужно исключить физическую возможность попадания человека в механизм, а не обучать обслуживающий персонал кунг-фу и умению танцевать с бритвами на скользком полу в трансформаторной будке.
Rust пытается сделать то же самое на уровне системного программирования. Есть фундаментальная разница между утверждениями «корректность системы проверена тестами» и «корректность системы доказана математически». В первом случае, даже после десяти лет эксплуатации, мы все равно не можем быть уверены, что система надежна.
Ключ к решению — формализация тех отношений в коде, которые в традиционных языках остаются на совести программиста. Например отношения владения памятью и поддержания ссылочной целостности на уровне указателей. Rust физически не допускает ситуаций, могущих привести к неопределенному поведению, нарушению алиасинга, состоянию гонок или обращению к уже освобожденной или еще не выделенной памяти.
Все вместе позволяет без опаски реализовывать (а главное рефакторить!), сложные многопоточные программы. Здесь очень кстати будет классический уже пост Fearless Concurrency от одного из авторов языка, плюс великолепная серия статей Lin Clark про внутреннее устройство Firefox Quantum, который стал возможен исключительно благодаря возможностям Rust.
Как экосистема Rust уникален тем, что ядру команды удалось сформировать удивительное сообщество, готовое к диалогу и дружелюбное к новичкам. Буквально каждый момент взаимодействия с этим сообществом вызывает чувство глубокой благодарности. Тем удивительнее это видеть в среде, близкой к системному программированию.