Forwarded from Kai Ren
2. Касательно божественности CSP, акторов, асинхронщины, конкурентности и многопоточности.
Я не знаю почему так происходит, но у большинства Go'шников почему-то святая уверенность в превосходстве дизайна и реализации конкурентности в Go над чем бы то ни было. Когда на самом же деле, несмотря на действительно матёрую под-капотную реализацию (снимаем шляпу перед Дмитрием Вьюковым), это очень жёсткий компромис в плане дизайна для наиболее типовых ситуаций.
Меня забавляет, что Go'шники повторяю постоянно мантру "Concurrency is not parallelism", но если попросишь Go'шника написать тебе параллельный алгоритм, он побежит городить лабуду из каналов и sync.WaitGroup, но не потому что он глупый, а потому что сам язык Go и его платформа не дают Вам возможности писать параллельный код, а только конкурентный. Вы не можете гарантировать кол-во используемых реальных потоков ОС для той или иной задачи, ибо за Вас это решает внутренний планировщик. В Rust же идеология - не ограничивать в возможностях, потому многопоточность может использоваться как для конкурентности (см. crossbeam), так и для параллелизма (см. rayon).
Аналогично и с асинхронностью. За счёт того, что в Go смешались в кучу асинхронность, многопоточность и конкурентность, у Go'шников, как правило, нет понимания в плане разделения задаче на IO-bound и CPU-bound. Для них это всё одно и то же, и этими вопросами вместо них занимается планировщик горутин, далеко не всегда оптимальным образом. В Rust же вполне себе внятное разделение: IO-bound решай асинхронщиной, хоть поверх нескольких тредов, хоть поверх одного, CPU-bound решай синхронным многопотоком и/или векторизацией. Причём Rust стремится дать Вам возможность полностью выбирать ожидаемые гарантии, и тот же runtime крейт позволяет Вам выбрать желаемую под-капотную реализацию асинхронного движка на Ваш вкус, ведь те же тредпулы бывают с разными гарантиями производительности для разных задач.
Касательно того, что CSP в Go удобный - тоже с Вами не соглашусь. Как ни смешно, но отсутствие дженериков полностью убило удобство CSP в Go, ибо почти полностью убило создание вменяемых переиспользуемых абстракций. Шаг в лево, шаг в право, от стандартного "прочитать с канала"/"записать в канал", и сразу куча граблей и головняка. Вот несколько типовых моментов:
- В Go есть buffered и unbuffered каналы, buffered каналы имеют максимальный размер, а что мне делать, если мне нужен безразмерный канал и чтобы пишущий в канал никогда не блокировался и данные никогда не выбрасывались? Только пилить свою реализацию через interface{}, либо писать каждый раз руками. Причём, как раз за счёт того, что каналы не являются библиотечным типом, а гвоздями запаяны в сам язык, возникает куча трудностей, и не просто написать незабагованную реализацию безразмерного канала не так уж и тривиально, но и даже использовать её правильно нужно постараться: https://github.com/eapache/channels/issues/27
Скажите безразмерные каналы никому не нужны, потому что именно Вы их никогда не использовали? Real-world Go приложения с Вами не согласятся: https://github.com/kubernetes/ingress-nginx/pull/2082
В Rust есть crossbeam-channel крейт. Он не вшит в язык, но при этом лучше Go'шных каналов и в плане производительности, и в плане удобства использования.
- Есть 1000 ссылок, нужно каждую обработать определенным образом, но так, чтобы в один момент времени обрабатывалось не более 5 одновременно. Что делаем в Go? Правильно: создаём канал с буфером 5 на подачу ссылок, стартуем 5 горутин на обработку, которые сосут ссылки из этого канала, обрабатывают и складывают результат в другой канал, ну и, в зависимости от других условий sync.WaitGroup для синхронизации, чтобы всё довыполнялось. Надо ли говорить, что в 90% случаев были ошибки в реализации этого дела, особенно когда код писался джунами? В Rust же просто используешь futures_unordered() и горя не знаешь.
Я не знаю почему так происходит, но у большинства Go'шников почему-то святая уверенность в превосходстве дизайна и реализации конкурентности в Go над чем бы то ни было. Когда на самом же деле, несмотря на действительно матёрую под-капотную реализацию (снимаем шляпу перед Дмитрием Вьюковым), это очень жёсткий компромис в плане дизайна для наиболее типовых ситуаций.
Меня забавляет, что Go'шники повторяю постоянно мантру "Concurrency is not parallelism", но если попросишь Go'шника написать тебе параллельный алгоритм, он побежит городить лабуду из каналов и sync.WaitGroup, но не потому что он глупый, а потому что сам язык Go и его платформа не дают Вам возможности писать параллельный код, а только конкурентный. Вы не можете гарантировать кол-во используемых реальных потоков ОС для той или иной задачи, ибо за Вас это решает внутренний планировщик. В Rust же идеология - не ограничивать в возможностях, потому многопоточность может использоваться как для конкурентности (см. crossbeam), так и для параллелизма (см. rayon).
Аналогично и с асинхронностью. За счёт того, что в Go смешались в кучу асинхронность, многопоточность и конкурентность, у Go'шников, как правило, нет понимания в плане разделения задаче на IO-bound и CPU-bound. Для них это всё одно и то же, и этими вопросами вместо них занимается планировщик горутин, далеко не всегда оптимальным образом. В Rust же вполне себе внятное разделение: IO-bound решай асинхронщиной, хоть поверх нескольких тредов, хоть поверх одного, CPU-bound решай синхронным многопотоком и/или векторизацией. Причём Rust стремится дать Вам возможность полностью выбирать ожидаемые гарантии, и тот же runtime крейт позволяет Вам выбрать желаемую под-капотную реализацию асинхронного движка на Ваш вкус, ведь те же тредпулы бывают с разными гарантиями производительности для разных задач.
Касательно того, что CSP в Go удобный - тоже с Вами не соглашусь. Как ни смешно, но отсутствие дженериков полностью убило удобство CSP в Go, ибо почти полностью убило создание вменяемых переиспользуемых абстракций. Шаг в лево, шаг в право, от стандартного "прочитать с канала"/"записать в канал", и сразу куча граблей и головняка. Вот несколько типовых моментов:
- В Go есть buffered и unbuffered каналы, buffered каналы имеют максимальный размер, а что мне делать, если мне нужен безразмерный канал и чтобы пишущий в канал никогда не блокировался и данные никогда не выбрасывались? Только пилить свою реализацию через interface{}, либо писать каждый раз руками. Причём, как раз за счёт того, что каналы не являются библиотечным типом, а гвоздями запаяны в сам язык, возникает куча трудностей, и не просто написать незабагованную реализацию безразмерного канала не так уж и тривиально, но и даже использовать её правильно нужно постараться: https://github.com/eapache/channels/issues/27
Скажите безразмерные каналы никому не нужны, потому что именно Вы их никогда не использовали? Real-world Go приложения с Вами не согласятся: https://github.com/kubernetes/ingress-nginx/pull/2082
В Rust есть crossbeam-channel крейт. Он не вшит в язык, но при этом лучше Go'шных каналов и в плане производительности, и в плане удобства использования.
- Есть 1000 ссылок, нужно каждую обработать определенным образом, но так, чтобы в один момент времени обрабатывалось не более 5 одновременно. Что делаем в Go? Правильно: создаём канал с буфером 5 на подачу ссылок, стартуем 5 горутин на обработку, которые сосут ссылки из этого канала, обрабатывают и складывают результат в другой канал, ну и, в зависимости от других условий sync.WaitGroup для синхронизации, чтобы всё довыполнялось. Надо ли говорить, что в 90% случаев были ошибки в реализации этого дела, особенно когда код писался джунами? В Rust же просто используешь futures_unordered() и горя не знаешь.
GitHub
Memory leak with InfiniteChannel · Issue #27 · eapache/channels
If I understand correctly, the goroutine ch.infiniteBuffer() persists in memory until all the values from inner buffer are drained. So, in situation where there are many writes, but only few reads,...
Forwarded from Kai Ren
Касательно акторов, то это просто чуть более высокоуровневая абстракция, которая удобна в ряде случаев, и не живёт отдельно от футур и асинков, но построена поверх них и вполне себе с ними сосуществует удобным образом. Кстати, реализации акторов есть и в Go прямо поверх CSP.
Forwarded from Kai Ren
3. Чего на Rust написано серьезного и какое у него будущее.
Rust уже активно используется большими компаниями. Firecracker от Amazon, libra от Facebook, fuchsia от Google, deno от Раяна Даля (кстати отказался от Go в пользу Rust), внутренне используется в Dropbox, CloudFlare, и, кажись, Microsoft Azure. Это только что сразу на уме всплыло. С каждым готом всё больше набирает популярность.
Нытьё про то, что язык игрушечный и никому не нужный, и ничего на нём не написано, я уже слышал и про Go ещё 3-4 годна назад, когда уже был Docker и HashiCorp'ы наваяли уже кучу всякого не нём, что использовалось в продакшене многими. Так что в будущем Rust у меня никаких сомнений нету.
Rust уже активно используется большими компаниями. Firecracker от Amazon, libra от Facebook, fuchsia от Google, deno от Раяна Даля (кстати отказался от Go в пользу Rust), внутренне используется в Dropbox, CloudFlare, и, кажись, Microsoft Azure. Это только что сразу на уме всплыло. С каждым готом всё больше набирает популярность.
Нытьё про то, что язык игрушечный и никому не нужный, и ничего на нём не написано, я уже слышал и про Go ещё 3-4 годна назад, когда уже был Docker и HashiCorp'ы наваяли уже кучу всякого не нём, что использовалось в продакшене многими. Так что в будущем Rust у меня никаких сомнений нету.
Forwarded from Kai Ren
В заключение:
Если Вам тема Rust интересна именно в плане разработки веб-бекендов, то советую либо вникать в тему глубже и разбираться почему сделано так, а не иначе, и почему ситуация такова, а не кидаться мнениями со "своего болота" в стиле "я привык в Go делать так, почему в Rust не так? Фу какой Rust нихароший...". Либо же просто вернуться к Rust через годика 2, когда async story в Rust не просто стабилизируется до конца, но заматереет, да ещё и подтянется экосистема.
Если Вам тема Rust интересна именно в плане разработки веб-бекендов, то советую либо вникать в тему глубже и разбираться почему сделано так, а не иначе, и почему ситуация такова, а не кидаться мнениями со "своего болота" в стиле "я привык в Go делать так, почему в Rust не так? Фу какой Rust нихароший...". Либо же просто вернуться к Rust через годика 2, когда async story в Rust не просто стабилизируется до конца, но заматереет, да ещё и подтянется экосистема.
Forwarded from Peter Sovietov
Краткий обзор компилятора Go в двух частях от известного специалиста Eli Bendersky https://eli.thegreenplace.net/2019/go-compiler-internals-adding-a-new-statement-to-go-part-1/
Forwarded from Alexander Chichigin
А ещё есть Futhark, написанный на Haskell: https://github.com/diku-dk/futhark
So what was your point, again? 😊
So what was your point, again? 😊
GitHub
GitHub - diku-dk/futhark: :boom::computer::boom: A data-parallel functional programming language
:boom::computer::boom: A data-parallel functional programming language - diku-dk/futhark
Forwarded from Αλεχ Zhukovsky
подробнее могу отправить за видосом к дегузу: https://www.youtube.com/watch?v=sxudIMiOo68
YouTube
Fun(c) 2018.7: John De Goes - FP to the Max
What happens when you take a procedural program riddled with partial functions and effects, and incrementally refactor it to a purely functional program—and then dial it up to eleven? In this live coding session, John hopes you'll be amused and a maybe little…
Forwarded from Jack
есть https://github.com/kbknapp/cargo-outdated , который детектит устаревшие зависимости
GitHub
GitHub - kbknapp/cargo-outdated: A cargo subcommand for displaying when Rust dependencies are out of date
A cargo subcommand for displaying when Rust dependencies are out of date - kbknapp/cargo-outdated
Forwarded from ọzkriff
есть несколько начинаний для коллективного ревью зависимостей, например reddit.com/r/rust/comments/bhjjln/cargo_crev_verify_deps_best_security_view_into но прошлые местные обсуждения сходились на том, что в таком виде оно не взлетит
reddit
r/rust - `cargo crev verify deps` - best security view into dependencies
135 votes and 18 comments so far on Reddit
Kai Ren
Почитал я тут Ваши "набросы", и со своей стороны могу сказать, что либо Вы спешите делать выводы о вещах, в которых толком не разобрались и не имеет солидной практики, либо же лукавите и банально толсто троллите. Мне пришлось пописать средне-большие проекты…
Telegram
Kai Ren in rust_offtopic
Почитал я тут Ваши "набросы", и со своей стороны могу сказать, что либо Вы спешите делать выводы о вещах, в которых толком не разобрались и не имеет солидной практики, либо же лукавите и банально толсто троллите.
Мне пришлось пописать средне-большие проекты…
Мне пришлось пописать средне-большие проекты…
Forwarded from Kai Ren
Вопрос про Go? Не считаю его простоту положительной чертой. Она позволяет быстрее написать "какой-то" код, но чтобы писать качественный и идеоматичный код, приходится вкатываться не сильно меньше Rust'а, ибо слишком много спрятанных граблей.
Что мне понравилось в Go - это скорре платформа, а не язык. В меру шустрая и не прожорливая, удобно деплоится. Ну и вкусности вроде из-коробочных тестов/доков/fmt, хотя оно и до сих пор сыровато.
Так то я совсем ушёл в Rust из Go, и Rust для меня по всем пунктам лучше, чем Go, кроме, разве что, ещё сырой экосистемы для веб-бекендов.
Что мне понравилось в Go - это скорре платформа, а не язык. В меру шустрая и не прожорливая, удобно деплоится. Ну и вкусности вроде из-коробочных тестов/доков/fmt, хотя оно и до сих пор сыровато.
Так то я совсем ушёл в Rust из Go, и Rust для меня по всем пунктам лучше, чем Go, кроме, разве что, ещё сырой экосистемы для веб-бекендов.
Forwarded from Bogdan
Продублирую сюда с мейна, вдруг кому интересно будет
https://medium.com/@chyyran/calling-c-natively-from-rust-1f92c506289d
https://medium.com/@chyyran/calling-c-natively-from-rust-1f92c506289d
Medium
Calling C# natively from Rust.
…or how CMake drove me to create an eldritch monstrosity.
#![feature(async_await, async_closure)]
// [dependencies]
// tokio = "0.1.19"
// futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] }
use futures::prelude::*;
pub type Handler<A> = dyn FnMut(Context<A>) + Send + 'static;
pub trait Address {}
impl<T> Address for T {}
pub struct Router<A> {
route: Option<Box<Handler<A>>>,
}
pub struct Context<A>(A);
pub type Caller<A> = Context<A>;
#[derive(Clone)]
pub struct Driver;
impl<A> Router<A>
where
A: Address,
{
fn set_sync_boxed<F>(&mut self, handler: Box<F>)
where
F: FnMut(Context<A>) + Send + 'static,
{
self.route = Some(handler);
}
pub fn set_boxed<F, T>(&mut self, mut handler: Box<F>)
where
F: (FnMut(Context<A>) -> T) + Send + 'static,
T: std::future::Future<Output = ()> + Send + 'static,
{
let handler = move |ctx| {
let fut = handler(ctx).unit_error().boxed().compat();
tokio::spawn(fut);
};
self.set_sync_boxed(Box::new(handler))
}
}
pub async fn info<A>(_ctx: Context<A>, _driver: Driver)
where
A: Address + 'static
{
unimplemented!()
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn it_works() {
let router: Router<u8> = Router {route: None};
let driver_inner = Driver{};
let handler = async move |ctx| {
info(ctx, driver_inner.clone()).await;
};
let handler = Box::new(handler);
router.set_boxed(handler);
assert_eq!(2 + 2, 4);
}
}
// [dependencies]
// tokio = "0.1.19"
// futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] }
use futures::prelude::*;
pub type Handler<A> = dyn FnMut(Context<A>) + Send + 'static;
pub trait Address {}
impl<T> Address for T {}
pub struct Router<A> {
route: Option<Box<Handler<A>>>,
}
pub struct Context<A>(A);
pub type Caller<A> = Context<A>;
#[derive(Clone)]
pub struct Driver;
impl<A> Router<A>
where
A: Address,
{
fn set_sync_boxed<F>(&mut self, handler: Box<F>)
where
F: FnMut(Context<A>) + Send + 'static,
{
self.route = Some(handler);
}
pub fn set_boxed<F, T>(&mut self, mut handler: Box<F>)
where
F: (FnMut(Context<A>) -> T) + Send + 'static,
T: std::future::Future<Output = ()> + Send + 'static,
{
let handler = move |ctx| {
let fut = handler(ctx).unit_error().boxed().compat();
tokio::spawn(fut);
};
self.set_sync_boxed(Box::new(handler))
}
}
pub async fn info<A>(_ctx: Context<A>, _driver: Driver)
where
A: Address + 'static
{
unimplemented!()
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn it_works() {
let router: Router<u8> = Router {route: None};
let driver_inner = Driver{};
let handler = async move |ctx| {
info(ctx, driver_inner.clone()).await;
};
let handler = Box::new(handler);
router.set_boxed(handler);
assert_eq!(2 + 2, 4);
}
}
Возвращаясь к теме, что в расте запрещена рекурсия async-функций. Написал простейший web-scrapper на космическом языке dart :)) - выкачивает ссылки до определенной глубины, отсекая дубли, и замеряя время. И рекурсия асинхронной функции тут работает влет, а ведь dart умеет компилять в машинный код, то есть такое возможно в принципе сделать - стейтмашину с динамическим стеком, жду когда растишка до такого дорастет !
https://gist.github.com/epishman/703006571d273532619096412165a7bf
https://gist.github.com/epishman/703006571d273532619096412165a7bf
Gist
Recursion of async function "do_query()"
Recursion of async function "do_query()". GitHub Gist: instantly share code, notes, and snippets.
Конвертация замыкания к FnMut
fn check(_: impl FnMut()) {}
fn main() {
let f = || {};
check(f);
}
fn check(_: impl FnMut()) {}
fn main() {
let f = || {};
check(f);
}
Forwarded from Nikita Melkozerov
В линухе уже все есть:
/usr/bin/numactl -m 0 -N 0 ./hello-world
/usr/bin/numactl -m 0 -N 0 ./hello-world