15 ms·
Why you might want async in your project
- PaulHoule 3y agoI find async is so much fun in Python and meshes with the other things you can do with generators but that is because I have the reference collector cleaning up behind me. Looking back with like 30 years of hindsight it seems to me that Java’s greatest contribution to software reuse was efficient garbage collection; memory allocation is a global property of an application that can’t efficiently be localized as you might want a library to use a buffer it got from the client or vice versa and fighting with the borrow checker all the time to do that is just saying “i choose to not be able to develop applications above a certain level of complexity.”
- airstrike 3y agoAnd Java's worst contribution to software was how painfully slow and resource hungry most of the software written with it tends to be... Your argument is looking at the advantages Java brought to development speed and entirely disregarding runtime speed
- fnord77 3y agojava benchmarks are close to C's benchmarks; thousands of times faster than python
- cies 3y agonot in terms of memory usage and startup time. otherwise it's quite fast.
- jshen 3y agoI don’t like Java, but you are completely wrong.
- notamy 3y agoAny tool can be misused - the same comment could be made about Javascript, PHP, Perl, C, C++, Python, really any language.
- charrondev 3y agoThe only Java thing I work with is ElasticSearch (and in the past other lucene based search tools like Solr). These can be resource hungry depending on what your indexing but they are also faster and more scalable than other tools I’d used before.
- jjav 3y ago> And Java's worst contribution to software was how painfully slow and resource hungry most of the software written with it tends to be... It's hip to hate on java, but at least do it from an informed position. Java is extremely fast, which is why it's so popular for server code where performance matters.
- fnord77 3y agothe JVM is an underappreciated engineering marvel
- cies 3y ago> underappreciated widely used though. not sure if that count for appreciation, but i think it's one of the highest forms. it's not bad, not not great either. i miss proper sum types, and it really lament the fact that static things are nearly impossible to be mocked which prompts everyone to use DI for everything instead of static.
- za3faran 3y agoJava has sum types now with sealed interfaces and pattern matching. Records have detructoring out of the box, and I believe supporting it for general classes is in the works.
- cies 3y agoI think sealed interfaces it not quite the same as "tagged unions"-style enum's with payloads. Also, it does not matter much anymore, the whole std-lib is full of exceptions-to-implement-multiple-return-values.
- za3faran 3y agosealed interface Shape {} record Square(int x) implements Shape {} record Rectangle(int l, int w) implements Shape {} record Circle(int r) implements Shape {} double getArea(Shape s) { // Exhaustively checks for all alternatives. return switch (s) { case Square(var x) -> x * x; case Rectangle(var l, var w) -> l * w; case Circle(var r) -> Math.PI * r * r; } } This is a good article: https://mccue.dev/pages/11-1-21-smuggling-checked-exceptions https://mccue.dev/pages/11-1-21-smuggling-checked-exceptions
- cies 3y ago
- codeflo 3y agoI agree that memory management can't be solved locally. The situation in C++, where every library or API you use has a different cleanup convention, that you need to carefully read about in the documentation to even properly review a pull request, is proof of that. I disagree that this criticism applies to Rust. For 99% of the cases, the idiomatic combination of borrow checking, Box and Arc gets back to a unified, global, compiler-enforced convention. I agree that there's a non-trivial initial skill hurdle, one that I also struggled with, but you only have to climb that once. I don't see that there's a limit to program complexity with these mechanisms.
- meindnoch 3y ago>The situation in C++, where every library or API you use has a different cleanup convention, that you need to carefully read about in the documentation to even properly review a pull request, is proof of that. Lol wut. The C++ resource management paradigm is RAII. If you write a library that doesn't use RAII, it's a bad library. Not a fault of the language.
- codeflo 3y agoYou have two choices. Either you write code with good performance, which means that functions do take references and pointers sometimes, in which case you do have all of the usual lifetime issues. This is the proper way to use C++, and it's perfectly workable, but it's by no means automatic. That's the reality that my comment was referencing. Or you live in a fantasy land where RAII solves everything, which leads to code where everything is copied all the time. I've lived in a codebase like this. It's the mindset that famously caused Chrome to allocate 25K individual strings for every key press: https://groups.google.com/a/chromium.org/g/chromium-dev/c/EUqoIz2iFU4/m/kPZ5ZK0K3gEJ?pli=1 https://groups.google.com/a/chromium.org/g/chromium-dev/c/EU...
- cma 3y ago2014, isn't that pre-C++11 in Chromium?
- 3y ago
- Nullabillity 3y agoThe problem with garbage collection is that it doesn't work for other kinds of resources than memory, so basically every garbage collected runtime ends up with an awkward and kinda-broken version of RAII anyway (Closeable, defer, using/try-with-resources, context managers, etc). Static lifetimes are also a large part of the rest of Rust's safety features (like statically enforced thread-safety). A usable Rust-without-lifetimes would end up looking a lot more like Haskell than Go.
- pphysch 3y ago> The problem with garbage collection is that it doesn't work for other kinds of resources than memory Why is that a "problem with GC"? Abstracting away >90% of resource management (i.e. local memory) is a significant benefit. It's like saying the "problem with timesharing OS" is that it doesn't address 100% of concurrency/parallelism needs.
- insanitybit 3y agoI think a charitable reading would be that by "problem" they meant "limitation".
- pbourke 3y agoI quite like context managers and try-with-resources style constructs. They make lifetimes explicit in the code in a fairly intuitive way. You can get yourself turned around if you deeply nest them, etc but there are usually ways to avoid those traps.
- ok123456 3y agoDon't static lifetimes just mean that leaking memory is considered 'safe' in rust?
- Nullabillity 3y agoStatic lifetimes as in "known and verified at compile-time", not "the 'static lifetime".
- chrisweekly 3y agoFYI This is an interesting response by the maintainer* of smol to this recent discussion: https://news.ycombinator.com/item?id=37435515 https://news.ycombinator.com/item?id=37435515 * EDIT: corrected, thanks
- Nullabillity 3y agoMaintainer, not creator. Smol was originally created by stjepang, who has basically disappeared these days. EDIT: I originally incorrectly claimed that stjepang also created rather than maintained crossbeam, making the same msitake as I was correcting.
- chrisweekly 3y agowhoops! thanks
- urschrei 3y agocrossbeam was created by Aaron Turon (who has – inevitably – also left the Rust project): https://aturon.github.io/blog/2015/08/27/epoch/ https://aturon.github.io/blog/2015/08/27/epoch/
- Nullabillity 3y agoOops, shame on me!
- rane 3y agoWhere should this link point to?
- charcircuit 3y agoasync is not free. It will turn your code into a big state machine and each thing you await will likely create its own thread. There is simplicity in a avoiding that and having code that gets compiled to something that is straightforward and single threaded.
- deleted 3y ago[deleted]
- maxbond 3y agoThis is true of all abstractions; if you don't need them, then they'll make your program more complex and more painful to write and maintain. Exercising judgement about when to use or shirk an abstraction is a lot of what being a software engineer is about.
- eternityforest 3y agoWhen does async/await ever make your program harder to maintain? Maybe to people who don't already know it, but almost all the big languages have async, it would be hard for a programmer to get away with not learning it, at least if they're a python of JS programmer. It adds complexity, but it's at the level where you don't have to think about it. If you're doing something advanced enough to where async is a leaky abstraction, you're probably doing something big enough to where you would want the advantages it offers. If you're doing something simple, async is just a black box primitive that is pretty easy to use.
- api 3y agoAwaits don’t create threads, at least not in any runtime I know of. There is usually a fixed number of threads at launch.
- maxbond 3y agoI think they meant it was likely to spin off additional tasks/green threads.
- api 3y agoThe author is a maintainer of smol, which I think is a far superior runtime to tokio for numerous reasons including structured concurrency, performance, size, and an ownership parameter that reduces the need for Arc<> all over the place by letting you scope on the runtime or task. The whole thing is just tighter and better thought out. Yet tokio is the de facto standard and everything links against it. It’s really annoying. Rust should have either put a runtime in the standard library or made it a lot easier to be runtime neutral.
- lawn 3y agoIs there a solid web framework tht uses smol instead of tokio?
- eternityforest 3y agoI love async in Python and JS. I used to be one of those "Threads aren't that hard, just use threads" people, but that was back when the trend was doing "async" with layers of nested callbacks like as if this was LISP or something where people just accept deep nesting. Now we have async/await and I'm always happy to see it.
- CharlieDigital 3y agoI can't quite understand all of the async hate. I use it in C# and JS with no friction or mental overhead required. In C#, I can still use channels or threads if I want to as well. But async/await is great for any I/O heavy code.
- francasso 3y agoAm I the only one that after reading opening sentences like "There is a common sentiment I’ve seen over and over in the Rust community that I think is ignorant at best and harmful at worst." just refuses to read the rest? If you are actually trying to make a point to people that think differently than you, why antagonize them by telling them they don't know what they are talking about?
- winwang 3y agoI agree with your sentiment, but want to point out the irony of choosing ignorance after being insulted as ignorant.
- francasso 3y agoYou are assuming that the article would actually increase my knowledge
- quickthrower2 3y agoHeuristics. Not going to read every article that negs me.
- a-dub 3y agodo any of the async libraries for rust have good visualization tools for inspecting the implicit state machine that is constructed via this type of concurrency primitive?
- mplanchard 3y agoNot sure if it’s exactly what you’re looking for, but tokio-console is pretty nice
- Arnavion 3y agoThe state machine transformation is not specific to any async libraries. The compiler is the one that desugars async fns / blocks to state machines. AFAIK there is nothing other than dumping the HIR / MIR from rustc to inspect it. But even without that the transformation is pretty straightforward to do mentally. The first transformation is that every async block / fn compiles to a generator where `future.await` is essentially replaced by `loop { match future.poll() { Ready(value) => break value, Pending => yield } }`. ie either polling the inner future will resolve immediately, or it will return Pending and yield the generator, and the next time the generator is resumed it will go back to the start of the loop to poll the future again. The second transformation is that every generator compiles to essentially an enum. Every variant of the enum represents one region of code between two `yield`s, and the data of that variant is all the local variables that in the scope of that region. Putting both together: async fn foo(i: i32, j: i32) { sleep(5).await; i + j } ... essentially compiles to: fn foo(i: i32, j: i32) -> FooFuture { FooFuture::Step0 { i, j } } enum FooFuture { Step0 { i: i32, j: i32 } Step1 { i: i32, j: i32, sleep: SleepFuture } Step2, } impl Future for FooFuture { fn poll(self) -> Poll<i32> { loop { match self { Self::Step0 { i, j } => { let sleep = sleep(5); self = Self::Step1 { i, j, sleep }; } Self::Step1 { i, j, sleep } => { let () = match sleep.poll() { Poll::Ready(()) => (), Poll::Pending => return Poll::Pending, }; self = Self::Step2; return Poll::Ready(i + j); } Self::Step2 => panic!("already run to completion"), } } } }
- aaomidi 3y agoI really wish we had stuff like the switch to scheduler that effectively makes asyncish behavior possible at the kernel level. I’m tired of everyone implementing async on their own.
- dpc_01234 3y agoI might need a lot of stuff in my software. Eventually. I might need distributed database, or to to scale it out to run on multiple machines, and then maybe Raft, or reactive architecture, zero-copy IO, or incremental updates or ... or ... the list goes on and on. Thinking too much and in particularly going with over complicated solutions from the very start because "might" is just bad engineering. Also, even if I do need async in a certain place, doesn't mean I need to endure the limitations and complexity of async Rust everywhere in my codebase. I can just spawn a single executor and pass around messages over channels to do what requires async in async runtime, and what doesn't in normal and simpler (and better) blocking IO Rust. You need async IO? Great. I also need it sometimes. But that doesn't explain the fact that every single thing in Rust ecosystem nowadays is async-only, or at best blocking wrapper over async-only. Because "async is web-scale, and blocking is not web-scale". Edit: Also the "just use smol" comically misses the problem. Yeah, smol might be simpler to use than tokio (it is, I like it better personally), but most stuff is based on tokio. It's an uphill battle for the same reasons using blocking IO Rust is becoming an uphill battle. Only thing better than using async when you don't want to is having to use 3 flavors (executors) of async, when you didn't want to use any in the first place. Everything would be perfect and no one would complain about async all the time if the community defaulted to blocking, interoperable Rust, and then projects would pull in async in that few places that do actually need async. But nobody wants to write a library that isn't "web-scale" anymore, so tough luck.
- the__alchemist 3y agoI also see the `Async` vs `blocking` false dichotomy in embedded rust discussions. `Async/Await` != asynchronous execution.
- vc8f6vVV 3y agoIn some sense it is. Async is a glorified future, and future is a glorified thread management, and threads are a way to facilitate asynchronous execution. You can also create a threadless runtime, but then you are relying on OS threads (e.g. I/O or XHR), otherwise you are simply combining function calls (for which we already have language syntax).
- IshKebab 3y ago> Even the simple, Unix-esque atomic programs can’t help but do two or three things at once. Okay, now you set it up so, instead of waiting on read or accept or whatnot, you register your file descriptors into poll and wait on that, then switching on the result of poll to figure out what you actually want to do. > Eventually, two or three sockets becomes a hundred, or even an unlimited amount. Guess it’s time to bring in epoll! Or, if you want to be cross-platform, it’s now time to write a wrapper around that, kqueue and, if you’re brave, IOCP. This feels like a straw man. Nobody is saying "don't use async; use epoll!". The alternative to async is traditional OS threads. This option is weirdly not mentioned in the article at all. And yes they have a reputation for being very hard - and they can be - but Rust makes traditional multithreading MUCH easier than in C++. And I would argue that Rust's async is equally hard. Rust makes traditional threading way easier than other languages, and traditional async way harder than other languages, enough that threads are arguably simpler.
- conradludgate 3y agoIt's necessary to use some kind of poll construction if you want cancellation and timeouts without shutting down the entire application
- IshKebab 3y agoTrue but you can still do that using traditional threads using cancellation tokens. In some ways it's worse because you have to explicitly add them, and I have yet to see any Rust APIs that actually use them (though there is a `cancellation` crate so at least some must be). In other ways it's better because it gives you control and explicit visibility over the cancellation points.
- conradludgate 3y agoDo you have a source on these cancellation tokens for threads? The cancellation crate hasn't been touched since 2016 and requires the running thread have a mechanism to be woken up. If you're in the middle of a read, you won't observe a wakeup unless you use an async-io function that can be timed out or interrupted. This is no better than async/await. And await is just as obvious of the cancellation points. That being said, there are also numerous crates for async rust cancellation tokens that can polled in parallel with a read such that you can observed the cancel instantly and switch to a cleanup process instead of immediately cancelling everything
- nyanpasu64 3y agoI'd argue that libraries forcing programs to include an async runtime upfront because there's a chance that it may someday grow to the extent you want a central executor (when they are absolutely ill-suited for audio programming, and probably not the case for Animats's metaverse client at https://news.ycombinator.com/item?id=37437676 https://news.ycombinator.com/item?id=37437676), imposes unnecessary dependency bloat on applications. And unless you use block_on(), async code pushes applications from a "concurrency library" model where apps block on mutexes/signals/channels as needed, to a "concurrency framework" model where code only runs under the runtime's callbacks (which is not the right choice for all apps).
- carterschonwald 3y agoIsn’t the real problem the lack of monads and monad transformers so you can’t have async machinery used for domain specific stuff?
- deleted 3y ago[deleted]
- dang 3y agoRecent and related: Maybe Rust isn’t a good tool for massively concurrent, userspace software - https://news.ycombinator.com/item?id=37435515 https://news.ycombinator.com/item?id=37435515 - Sept 2023 (567 comments)
- nevermore 3y agoLots of comments and arguments about async being big or complex, and it's really not, it's pulling in the runtimes that's big and complex, and I think Rust really failed by forcing libraries to explicitly choose a runtime. As a library developer you're then put in the position of not using async, or fragmenting yourself to just the subset of users or other libraries on your runtime.
- vc8f6vVV 3y ago> Why don’t people like async? That's pretty simple. The primary goal of every software engineer is (or at least should be) ... no, not to learn a new cool technology, but to get the shit done. There are cases where async might be beneficial, but those cases are few and far in between. In all other cases a simple thread model, or even a single thread works just fine without incurring extra mental overhead. As professionals we need to think not only if some technology is fun, but how much it actually costs to our employer and about those who are going to maintain our "cool" code when we leave for better pastures. I know, I know, I sound like a grandpa (and I actually am).
- diarrhea 3y agoBut async Python is a single threaded. I’d prefer async over multithreading in python nowadays. Otherwise code can be slow as piss, if it’s doing a lot of I/O. Then, async is almost table stakes for almost any level of reasonable performance (GIL and all).
- vc8f6vVV 3y agoNot exactly sure how async in Python works, but if its runtime is non-preemptive and single-threaded (i.e. based on yield), then congratulations, you reinvented Windows 3.1! Those who are old enough to be "lucky" to use it, remember that the damn thing could hang the whole OS if your application was careless enough to block and not yield. Also "slow" is relative, if you create a thread to do DB query, thread creation is a way faster than any DB request, so not sure why it's slow. Never had problems with Ruby threads even though Ruby doesn't have a mechanism to create a thread pool (didn't have? it's been some time since I worked with Ruby). Java & Scala, OTOH are using thread pools, even multiple variations of them, so the thread startup time doesn't matter. In any case you are talking about I/O, in which case neither thread startup nor context switching matters.
- hgomersall 3y agoYour answer boils down to: "I know this technique, I don't want to learn blub technique. My job is to get stuff done, not learn new techniques." In which case, good for you; enjoy your sync code (seriously), and please stop telling the rest of us that have learnt the new blub technique that we shouldn't use it.
- andy_xor_andrew 3y ago> I’ve written quite a few Rust projects where I expect it to only involve blocking primitives, only to find out that, actually, I’m starting to do a lot of things at once, guess I’d better use async. In my experience (which, admittedly, is far less than the author, a developer of smol!) the answer to "I'm starting to do a lot of things at once" in Rust is usually to spin up a few worker threads and send messages between them to handle jobs, a la Ripgrep's beautiful implementation. In a way, it seems like async Rust appears more often when you need to do io operations, and not so much when you just need to do work in parallel. Of course, you surely can use async rust for work in parallel. But it's often easier to keep async out of it if you just need to split up some work across threads without bringing an entire async executor runtime into the mix. I don't think async/await was poorly implemented in Rust - in fact, I think it avoids a lot of problems and pitfalls that could have happened. The complications arise because async/await is, kind of, ideologically antithetical to Rust's other goal of memory safety and single-writer. Rust really wants to have its cake (compile-time memory safety) and eat it too (async/await). And while you can criticize it, you have to admit they did a pretty good job given the circumstances.
- ojkelly 3y ago> In a way, it seems like async Rust appears more often when you need to do io operations, and not so much when you just need to do work in parallel. Yep this makes sense to me. If your workload is CPU-bound then context switching to make progress on 10 tasks concurrently, is going to be slower than doing them sequentially. But if it’s IO-bound you will spend most of your time waiting, which you could use to make progress on the other tasks.
- delusional 3y agoThe author starts by citing greenspun's tenth rule and goes on to elaborate on the argument that if you are going to have a half implementation of async anyway, why not just pull it in? Yet fails to interrogate the relationship between this argument and the cited "rule". If you should use async because you might need it in the future, shouldn't we all be writing in lisp? If we presuppose that all software eventually develops an async, and we therefore should use async. Would it not stand to reason that greenspun's rule that all software contains a lisp would imply that we must also all use lisp?
- Arnavion 3y agoThe author said what you wrote in the first sentence, ie "use async if you are going to have a half implementation of async anyway". "Use async because you might need it in the future" is something you made up, not what the author said.
- delusional 3y agogreenspun's tenth rule is about the inevitability of the half baked implementation of lisp. By evoking the sentiment of the rule the author is implicitly making the argument that all "sufficiently complicated programs" will eventually contain a half baked implementation of async. The implicit argument doesn't stand alone though. The author goes on to write: > It happens like this: programs are naturally complicated. Even the simple, Unix-esque atomic programs can’t help but do two or three things at once. Okay, now you set it up so, instead of waiting on read or accept or whatnot, you register your file descriptors into poll and wait on that, then switching on the result of poll to figure out what you actually want to do. The implication is clear. Even simple programs will eventually require async, and should therefore just use it right now. unix-esque in this paragraph is supposed to evoke ls or cat. Is your program really going to be simpler than cat? No? Then you apparently need async.
- Arnavion 3y ago>The implication is clear. Even simple programs will eventually require async, and should therefore just use it right now. There's no implication. Read what you quoted instead of digging for quick jabs. "Even the simple, Unix-esque atomic programs can’t help but do two or three things at once. Okay, now you set it up so, instead of waiting on read or accept or whatnot..." >unix-esque in this paragraph is supposed to evoke ls or cat. Is your program really going to be simpler than cat? No? Then you apparently need async. cat and ls don't do two or three things at once.
- klysm 3y agoIn C#, I put anything doing IO in an async function and make cancelation tokens required
- Arnavion 3y ago>Except, this isn’t a problem with Rust’s async, it’s a problem with tokio. tokio uses a 'static, threaded runtime that has its benefits but requires its futures to be Send and 'static. It's not a problem with tokio either. The author's point is specifically about the multi-threaded tokio runtime that allows tasks to be moved between worker threads, which is why it requires the tasks to be Send + 'static. Alternatively you can either a) create a single-threaded tokio runtime instead which will remove the need for tasks to be Send, or b) use a LocalSet within the current worker that will scope all tasks to that LocalSet's lifetime so they will not need to be Send or 'static. If you go the single-threaded tokio runtime route, that doesn't mean you're limited to one worker total. You can create your own pseudo-multi-threaded tokio runtime by creating multiple OS threads and running one single-threaded tokio runtime on each. This will be similar to the real multi-threaded tokio runtime except it doesn't support moving tasks between workers, which means it won't require the tasks to be Send. This is also what the author's smol example does. But note that allowing tasks to migrate between workers prevents hotspots, so there are pros and cons to both approaches.
- Dowwie 3y agoActix-web uses the single threaded Tokio runtime per physical core. This architecture is harder to design for than multi threaded async Tokio. Performance gains aren't worth the effort.
- Arnavion 3y agoWe use the same design for a product at $dayjob and have had no difficulty in designing for it. There is a lot of benefit from being able to use Rc and RefCell instead of having to go for Arc and Mutex. (Of course it's best if it can be written to not have any RefCell / Mutex at all.)
- adamch 3y ago> tokio uses a 'static, threaded runtime that has its benefits but requires its futures to be Send and 'static. This is only partly true -- if you want to `spawn` a task on another thread then yes it has to be Send and 'static. But if you use `spawn_local`, it spawns on the same thread, and it doesn't have to be Send (still has to be 'static).
- fnordpiglet 3y agoBiggest issue I have with async is the lack of native async traits and the lack of support for async closures. You can work around the traits issue but the closure issue you can’t. I’ve spent hours trying to work around closures that wrap async code.
- insanitybit 3y agoCan you tell me more about the async closure issue you're having?
- klabb3 3y agoI mean.. I appreciate that there are proponents and people trying to improve the state of async rust but to allude that everything is dandy is either dishonest or more likely a strong curse of knowledge bias. I’ve worked deeply in an async rust codebase at a FAANG company. The vast majority chooses a dialect of async Rust which involves arcs, mutices, boxing etc everywhere, not to mention the giant dep tree of crates to do even menial things. The ones who try to use proper lifetimes etc are haunted by the compiler and give up after enough suffering. Async was an extremely impressive demo that got partially accepted before knowing the implications. The remaining 10% turned out to be orders of magnitude more complex. (If you disagree, try to explain pin projection in simple terms.) The damage to the ecosystem from fragmentation is massive. Look, maybe it was correct to skip green threads. But the layer of abstraction for async is too invasive. It would have been better to create a “runtime backend” contract - default would be the same as sync rust today (ie syscalls, threads, atomic ops etc – I mean it’s already half way there except it’s a bunch of conditional compilation for different targets). Then, alternative runtimes could have been built independently and plugged in without changing a line of code, it’d be all behind the scenes. We could have simple single-threaded concurrent runtimes for embedded and maybe wasm. Work stealing runtimes for web servers, and so on. I’m not saying it would be easy or solve all use-cases on a short time scale with this approach. But I do believe it would have been possible, and better for both the runtime geeks and much better for the average user.
- biomcgary 3y agoYour position wrt green threads sounds like Graydon's (https://graydon2.dreamwidth.org/307291.html https://graydon2.dreamwidth.org/307291.html).
- klabb3 3y agoYeah, perhaps. But I am not part of that minuscule subset of people who have deep expertise in both compiler internals and runtime architecture to have well founded opinion on the design. There is FFI and stack issues that’d need some incredibly bright engineers to sort out. My argument is more along the lines of: modularity is the (only) way to reduce complexity. We already have modular runtimes in other languages (project loom in Java, webassembly etc). Most people should not care about runtimes much. The ecosystem cost of async ended up being high. Thus, runtimes should be an implementation detail for most users. Doesn’t mean Rome has to be rebuilt. Perhaps the async we have can be saved, but even so it involves biting the apple of actually defining precisely what a runtime is so that crate authors can think of them just like they think of allocators today (ie not at all).
- bionhoward 3y agoFor the thing I’m working on, I have an infinite number of little tasks with potentially shared smaller subtasks. How could I unleash all the processors on my computer on this workload and allow them to correctly avoid repeated calculation of results of shared subtasks? For example, I’m using an outbox: im::OrdMap<String, Array2<_>> and a situation might arise where one task could avoid repeating work on a subtask because that’s already in progress elsewhere by waiting for the key/value pair (so that process could do something else) Would it be worth going to async for that? How could a worker function know if some key in the outbox was already being calculated and it could work on something else? How would you share an outbox like that across a bunch of rayon processes communicating with async? (I’ll read smol docs and try to figure it out but this article made a lot of sense, thank you)
- biorach 3y agoPersonally I wouldn't use async but instead use a pool of worker threads passing messages to an orchestrator thread using channels: https://doc.rust-lang.org/rust-by-example/std_misc/channels.html https://doc.rust-lang.org/rust-by-example/std_misc/channels.... The orchestrator can then keep track of what's going on and avoid duplicating tasks and the workers don't need to worry about any global state.