8 ms·
Why Rust fails hard at scientific computing
- estebank 9y ago> You might say, 30 lines, easy no? Well except that Go can be played on 9x9, 11x11, 13x13, 15x15 and 17x17 too, any oddxodd size, no way I'm implementing those 30 lines x 6 times, this is not fun I'm curious if the author has tried using macros, which would require him writing the code only once.
- aldanor 9y agoRe: (2), this RFC was merged: https://github.com/rust-lang/rfcs/pull/2133 https://github.com/rust-lang/rfcs/pull/2133
- aldanor 9y agoRe: (3), this one: https://github.com/rust-lang/rfcs/pull/2000 https://github.com/rust-lang/rfcs/pull/2000
- mastax 9y agoAnd implemented: https://github.com/rust-lang/rust/pull/43690 https://github.com/rust-lang/rust/pull/43690
- weinzierl 9y agoTo add a little context here: > 2. Arrays in Rust are a second-class citizens, actually I think they don’t even have their visas. I hear them laughing at me when I try to use them. You can’t even clone them: RFC 2133[1] adds Clone to Arrays (and Tuples), it has been merged and implemented[2]. > 3. Rust is still “discussing” integer as generic type parameter (since 2015), meaning a matrix type Matrix[M, N, float] will not exist before a long long time. The following github discussions are quite the read: It links to a bunch of stuff, but fails to recognize that this also has a merged RFC [3] and implementation is actively worked on[2]. [1] https://github.com/rust-lang/rfcs/pull/2133 https://github.com/rust-lang/rfcs/pull/2133 [2] https://github.com/rust-lang/rust/pull/43690 https://github.com/rust-lang/rust/pull/43690 [3] https://github.com/rust-lang/rfcs/pull/2000 https://github.com/rust-lang/rfcs/pull/2000 [4] https://github.com/rust-lang/rust/issues/44580 https://github.com/rust-lang/rust/issues/44580
- tom_mellior 9y ago> fails to recognize The article is dated April 1st, your links on const generics are from later.
- weinzierl 9y agoAh, sorry, I missed that.
- microcolonel 9y ago> I’m not even talking about Rc, RefCell and Box which seems like security through obscurity. What do you mean?
- posterboy 9y agofor one point, an efficient programm probably wouldn't need to pack memory intensive arrays on the stack all at once.
- wcrichton 9y agoExtensive discussion on /r/rust: https://www.reddit.com/r/rust/comments/76olo3/why_rust_fails_hard_at_scientific_computing/ https://www.reddit.com/r/rust/comments/76olo3/why_rust_fails...
- fny 9y agoAside from all the discussion around these low-level complaints, the Rust community is well aware of the lack of higher-level scientific computing libraries, which IMO is the more relevant reason not to use Rust today: http://www.arewelearningyet.com/scientific-computing/ http://www.arewelearningyet.com/scientific-computing/ Hell, Rust doesn't even have stable bindings around BLAS/LAPACK/LINPACK and friends which are the lifeblood of every language capable of scientific computing. Give it time. Scientific computing was never the primary thrust for Rust, and other more pertinent domains have yet to reach complete maturity.[0] [0]: http://www.arewewebyet.org/ http://www.arewewebyet.org/
- dnautics 9y agoIt's also totally fine for rust to be not as good for scientific computing. If you try to be everything for everyone you'll fail to be good at any one thing. It's hard to imagine rust will ever be competitive at scientific programming, say, with Julia. You might get say a 20% boost in number crunching performance at the expense of developer time. Conversely, you'd be a damn fool to try and write Firefox in Julia.
- fermienrico 9y agoWhat happened to the readability of code? This looks dreadful. I am sure there is a way to come up with a better syntax without sacrificing the type safety aspects. fn main() { let my_str: Rc<RefCell<Box<MyStruct1>>> = Rc::new(RefCell::new(Box::new(MyStruct1))); my_trait_fn(Rc::new(RefCell::new(Box::new((**my_str.borrow()).clone())))); my_str.borrow().my_fn(); } I come from a Python/Java world and I remember when I first delved into embedded MCU programming in C/C++, it was a cognitive overload and a half. I think we need to take a step back and while I understand the differences between high level languages (Java/Python) and C/C++ having the burden to allow for low-level programming; readability aspects of code often gets the lowest attention. If we, humans, spend so much time writing/parsing code through our vision system, programming languages need to take this aspect seriously and optimize the syntax over readability than expressivity. I recall Kernighan's "The Elements of Programming Style" where he talks about making the code more understandable than "clever". The syntax of a programming language directly facilitates (or impedes) this idea. Edit - Everyone seems to keep pointing out it is a contrived example. Yes, I agree but the syntax has nothing to do with making the contrived example even more difficult to read?
- sgift 9y agoWhile certainly not the most readable example I'd like to know how much you've used rust? If you don't know the syntax of a language it often looks dreadful.
- fermienrico 9y agoYea, I admit, I haven't used Rust much except for following the MCU programming guide: https://japaric.github.io/copper/ https://japaric.github.io/copper/ Don't you think regardless of familiarity, Rust doesn't quite have a beautiful syntax? I am following this guide and it's like the visual equivalent of nails on a chalkboard. Perhaps it's subjective, what do you think?
- steveklabnik 9y agoIt's more complex than it needs to be; there's no reason to put a Box there, for example. (Even though the blog post is from April, it links to a SO question from 2015, where that Box was necessary, but as of Rust 1.4, a few months later, it is not. This info is in the comments of that answer.) It's also very unclear why they created an entire second Rc rather than just cloning the first one, as that's the point of Rc. Compare: fn my_trait_fn(t: &MyTrait) { t.trait_func(); } fn main() { let my_str = Rc::new(RefCell::new(MyStruct1)); my_trait_fn(&*my_str.borrow()); my_str.borrow().my_fn(); }
- spaceseaman 9y ago> then your matrix is allocated on the heap not the stack, meaning slower operations This doesn't seem right at all... Does anyone know more? Maybe they meant slow to allocate / deallocate? The author's problem appears to require you to generate a lot of temporary data and then throw it away. They might benefit from just writing their own pool allocator, so they don't have to wait for heap allocates (assuming that's their problem).
- cfallin 9y agoYeah, the only downside of heap storage is the alloc/dealloc cost -- once you have the memory, it's all just bytes in RAM, whether heap or stack. It's not even a clear tradeoff for large arrays, because using value types on the stack implies lots of copies -- when you have a large block of data you want to refer to it by reference. Also, stack size is finite and relatively small -- 2MB-ish default per thread on Linux, or is it 8MB? -- so placing large matrices there will work until it doesn't. Heap allocation has all of virtual memory at its disposal.
- Jare 9y agoI imagine the following problems affecting performance outside of the alloc/dealloc themselves: - Heap memory may not be reused as cleanly as stack memory, which means lower cache efficiency. Your suggestion of a pool allocator should take care of this. - Runtime code for bounds checking needs to take the array size from the object even if it's constant. I recall there were XXX_unsafe() versions of accessors, which should not do any bounds checking, but the code is going to look ugly. - Having to go through an indirection from the object to the memory buffer. Here you are at the optimizer's mercy. So to avoid overly complex / ugly / brittle code where you need high performance, you are pretty much forced to write your own matrix types from the ground up, which is one of the concerns of the OP. I don't believe this is a terribly bad thing, and if the existing crates for this are not great yet, it would seem to me that it's just not been a focus for the Rust community, not an insurmountable problem.
- mintplant 9y ago> Consequences ? You can’t use Rust arrays to represent a matrix bigger than 4×8, how useful is that? I went and Google'd "rust linear algebra library" and immediately found https://nalgebra.org https://nalgebra.org which can represent such matrices just fine, and even integrates with LAPACK. Did the author do this most cursory of research before springing to write their rant? This is like saying "Python fails hard at scientific computing" because the default list type isn't an efficient basis for representing matrices, while entirely ignoring NumPy. Also, those are memory slices, not arrays: they aren't pointers to heap-allocated data. So [[f32; 9]; 5] corresponds to a contiguous block of memory representing a 5x9 matrix of 32-bit floats. It has the same memory structure as [f32; 45], but you can index it as eg. my_matrix[3][2] to access the element in the 2nd column of the 3rd row. Again, I guess it's much easier to bash something than try to understand it.
- coldtea 9y ago>I went and Google'd "rust linear algebra library" and immediately found https://nalgebra.org https://nalgebra.org which can represent such matrices just fine, and even integrates with LAPACK. Did the author do this most cursory of research before springing to write their rant? This is like saying "Python fails hard at scientific computing" because the default list type isn't an efficient basis for representing matrices, while entirely ignoring NumPy. The author didn't want to use/leverage/whatever some ready made package. He wanted to write his own. Python does suck for scientific computing at that level as well -- Numpy and Scipy are huge wrappers for C/Fortran etc code.
- mintplant 9y agoThe authors of nalgebra seem to have succeeded where the author failed, then. Perhaps because they took the time to understand what slices are.
- coldtea 9y agoThe author didn't say it was impossible altogether -- just that he would want actual malleable first-class multi-dimensional array types.
- blinkingled 9y agoI am going through the rust by example thing every weekend and also trying to write some Rust code on my own and I found the 'too much symbols'/unreadable code and just the general complexity of the language very off putting. If I have to write low level code, I might just stick with C or C++ - the devils I know rather than trying to tame a new incarnation of the devil that is Rust. The complexity and learning curve costs of Rust far outweigh the benefits at this point. Edit: If I sounded like I am giving up on Rust above, that's not yet the case. I am currently enjoying challenging myself to learn something complex and to that effect, Rust is holding my interest.
- steveklabnik 9y agoDid you try the book? Just curious.
- blinkingled 9y agoI started there, 1st edition IIRC - went up to refs and borrowing and then thought maybe the by example approach is something I would like better - and I do find the example approach keeps my interest better. Although I suppose I should go look at the 2nd ed draft when I need to look into more theory - reading the refs and borrowing section from 2nd ed right now :)
- steveklabnik 9y agoAh yeah, the second edition is just straight-up better, and does contain examples. For example, ownership, references, and borrowing are theory-based in 1st edition, but are with String/&str in the second. That said, both are great resources. For some people, the book doesn't work for them but RBE does, and for others, RBE doesn't but the book does.
- pjmlp 9y agoI went through the books, yet it was a big challenge porting an old Gtkmm toy application to Gtk-rs, because of lifetimes on callbacks. Initially I was referring to the fields on the struct, but ended up passing them around as parameters. You have a referrence to this kind of problem on the NLL proposal. But unless I missed it, the books don't talk much about this.
- evmar 9y agoI wish the HN community would collectively decide to downvote posts like these, which reward people who use clickbaity titles to get attention at the cost of hiding more careful analyses. The post can be summarized as a critique of a programming language that did not meet the author's desires, but sections of the critique is (1) dislike of the syntax, (2) a specific missing feature, and (3) a very specific feature which doesn't exist in any of the popular scientific computing languages. It's fine to talk about not liking a tool, but it doesn't merit "fails hard".
- dnautics 9y ago3) very much exists in julia
- haeffin 9y agoWhy do you say (3) does not exist? C++ has it (and it's one of the main reason why, at the moment, I wouldn't use Rust to do the things I do).
- pjmlp 9y ago(3) Exists in Fortran, C# and F# [1] Looking outside popular scientific computing languages, many Algol derived languages support it. [1] .NET languages are used in scientific computing with packages like Alea GPU and IMSL.
- bj1291 9y agoDoes someone have a summary. This page is intolerable.
- Animats 9y agoLack of multidimensional arrays as a language primitive is a problem. It was a problem in C, C++, and Go, too. There, you either have arrays of arrays, or some macro/template that does a multiply on the subscript. The reply in one of the Rust forums is that multidimensional arrays are only found in "domain-specific languages", like FORTRAN. When someone brings this up the usual excuses are trotted out: - "It can be done with templates". - "You can have an array of arrays". Then there's bikeshedding. Some people want to be able to extract an arbitrary subarray slice from an N-dimensional array and treat that like a regular array. This is possible but complicated, and if implemented slows down the simple operations. Then there are people who want N-dimensional arrays where N is determined at run time. (This now includes the machine learning people.) Endless arguments between these groups resulted in nothing being done in Go. I haven't followed that discussion for Rust. The big advantage of doing this in the language is that there's a lot of hardware support for doing number-crunching fast on dense arrays, and you want the compiler to be using it effectively. Matrix multiply on arrays of arrays is much slower than on a dense array where the compiler knows the spacing between the rows at compile time. At least have arrays as good as FORTRAN. This is why some heavy number-crunching work is still done in FORTRAN.
- danieldk 9y agoLack of multidimensional arrays as a language primitive is a problem. [...] "It can be done with templates" I am wondering what your point is. There are excellent and performant C++ template-based libraries for tensors (Eigen, Armadillo, etc.). In Rust I have used ndarray a bit for linear algebra and it is quite good [1]. If you have parametric polymorphism and operator overloading, whet is the problem of doing it in a library? The big advantage of doing this in the language is that there's a lot of hardware support for doing number-crunching fast on dense arrays, and you want the compiler to be using it effectively. Last time I checked Intel MKL (and wrappers like Armadillo) was still the fastest for doing number crunching of dense arrays on CPUs. [1] The primary problem I ran into was that some common expressions were not backed by BLAS yet.
- camus2 9y ago> Endless arguments between these groups resulted in nothing being done in Go Do you have some links to these discussions?
- gravypod 9y agoI don't know if Nim would be my first choice for a future scientific computing language. I think I'd be more focused on Julia or, recently, Crystal. Julia has some extreme magic that it can do to make your life easy. Crystal will attract the Ruby-like crowd of people who will create very neat and effective libraries.
- dom96 9y agoI disagree (but then again I am biased). I think Nim has a chance of attracting the Python-like crowd of people (plus Python has some very good scientific libraries like pandas) and I would be willing to bet that Nim's ability to perform magic surpasses that of Julia (or at least is equivalent). Edit: Shameless plug. My book, Nim in Action, is 50% off today with code HalloweenPB50 (From Manning). Perfect opportunity to grab a copy and learn Nim :) https://book.picheta.me/ https://book.picheta.me/
- dnautics 9y agoDoes Nim have first class distributed computing primitives? For embarrassingly parallel operations, I can deploy on a slurm provisioned cluster on a supercomputer in about 5 lines of native Julia. There's a lot of really nice "magic" in Julia. I implemented an experimental numerical system (a binary representation of real numbers) and after generating the +-x/ operations, I had complex numbers, Fourier transforms, and lu decomposition for free. With the macro system I then wrote a library that let's me generate verilog code and built a hardware implementation of the number system and deployed comprehensive tests cross correlating the results of all operations across native Julia, hardware simulation in Julia,and verilog version transpiled through verilator. I wanted to play around with Mary Wootters' discoveries about Reed Solomon encoding, so getting working Reed Solomon code over arbitrary galois fields was as simple as building the type rules and basic +-x/ and then again using matrix solving to solve erasures (in this case you can tell the type system there is no order to the type so it doesn't do partial pivoting). Then I needed to do some brute force searching of a large parameter space. The core program for this search is about 25 lines of code in the main loop, which reads like simple math statements out of a well written text, and the backing galois field and Reed Solomon libraries are about 100 and 50 lines, respectively.
- baldfat 9y agoScientific Computing doesn't really happen in low level language spaces today. FORTRAN, C and C++ are what powers the Higher Level languages. Most is done in R, Matlab, Python, and Julia and for good reason. Most people doing scientific computing are not coders that can do the work at a higher level at a productive level.
- amelius 9y agoI suspect Rust also fails hard at interactive code (UIs). Why? Because Rust's memory management doesn't work well with cyclic structures, and closures are such structures. Closures have shown to be very useful for interactive code (see Javascript). Someone please prove me wrong. (I'm wondering how Firefox deals with this).
- steveklabnik 9y agoRust uses closures quite heavily. The biggest problem with UIs is that they tend to be based on inheritance, which Rust doesn't have. It's not insurmountable; there are people who have written macros to make this kind of code more concise, or alternative UI libraries that don't rely on inheritance.
- amelius 9y ago> Rust uses closures quite heavily. I suppose they only support read-only free variables then?
- steveklabnik 9y agoNot exactly, no. There's a bit more ceremony since there isn't a GC, but that's a systems language for you. Closures are basically syntax sugar for a struct that represents the environment, and a function pointer which takes that struct as one of its parameters. So you can do anything that you can do with that setup, aka, anything :) https://doc.rust-lang.org/book/second-edition/ch13-01-closures.html https://doc.rust-lang.org/book/second-edition/ch13-01-closur...
- dbaupp 9y agoRust's borrow checker makes writing to captured variables okay and safe, e.g. this is a bit unconventional, but it works: let mut sum = 0; (0..10).for_each(|i| sum += i); println!("{}", sum); // 45 Having cyclic values and/or mutation of shared values requires things like reference counting and cells, but this is what designed-for-UI languages like Swift are doing, just with more syntactic sugar (and, of course, the lack of it in Rust is still annoying for this sort of thing, but it isn't a fundamental blocker, and is less problematic than lack of inheritance, as steveklabnik says).