14 ms·
I found out I already know Rust: unsafe { let x_tmp0 = _mm_clmulepi64_si128(xmm_crc0, crc_fold, 0x10); xmm_crc0 = _mm_clmulepi6
by YZF 2y ago
I found out I already know Rust:
unsafe {
let x_tmp0 = _mm_clmulepi64_si128(xmm_crc0, crc_fold, 0x10);
xmm_crc0 = _mm_clmulepi64_si128(xmm_crc0, crc_fold, 0x01);
xmm_crc1 = _mm_xor_si128(xmm_crc1, x_tmp0);
xmm_crc1 = _mm_xor_si128(xmm_crc1, xmm_crc0);
Kidding aside, I thought the purpose of Rust was for safety but the keyword unsafe is sprinkled liberally throughout this library. At what point does it really stop mattering if this is C or Rust?
Presumably with inline assembly both languages can emit what is effectively the same machine code. Is the Rust compiler a better optimizing compiler than C compilers?
- Filligree 2y agoThe usual answer is: You only need to verify the unsafe blocks, not every block. Though 'unsafe' in Rust is actually even less safe than regular C, if a bit more predictable, so there's a crossover point where you really shouldn't have bothered. The Rust compiler is indeed better than the C one, largely because of having more information and doing full-program optimisation. A `vec_foo = vec_foo.into_iter().map(...).collect::Vec<foo>`, for example, isn't going to do any bounds checks or allocate.
- johnisgood 2y agoI have been told that "unsafe" affects code outside of that block, but hopefully steveklabnik may explain it better (again). > isn't going to do any bounds checks or allocate. You need to add explicit bounds check or explicitly allocate in C though. It is not there if you do not add it yourself.
- LegionMammal978 2y ago> I have been told that "unsafe" affects code outside of that block, but hopefully stevelabnik may explain it better (again). Poorly-written unsafe code can have effects extending out into safe code. But correctly-written unsafe code does not have any effects on safe code w.r.t. memory safety. So to ensure memory safety, you just have to verify the correctness of the unsafe code (and any helper functions, etc., it depends on), rather than the entire codebase. Also, some forms of unsafe code are far less dangeous than others in practice. E.g., most of the SIMD functions are practically safe to call in every situation, but they all have 'unsafe' slapped on them due to being intrinsics. > You need to add explicit bounds check or explicitly allocate in C though. It is not there if you do not add it yourself. Unfortunately, you do need to allocate a new buffer in C if you change the type of the elements. The annoying side of strict aliasing is that every buffer has a single type that's set in stone for all time. (Unless you preemptively use unions for everything.)
- uecker 2y agoC has type-changing stores. If you store to a buffer with a new type, it has the new type. Clang does not implement this correctly though, but GCC does.
- pornel 2y agoBuggy unsafe blocks can affect code anywhere (through Undefined Behavior, or breaking the API contract). However, if you verify that the unsafe blocks are correct, and the safe API wrapping them rejects invalid inputs, then they won't be able to cause unsafety anywhere. This does reduce how much code you need to review for memory safety issues. Once it's encapsulated in a safe API, the compiler ensures it can't be broken. This encapsulation also prevents combinatorial explosion of complexity when multiple (unsafe) libraries interact. I can take zlib-rs, and some multi-threaded job executor (also unsafe internally), but I don't need to specifically check how these two interact. zlib-rs needs to ensure they use slices and lifetimes correctly, the threading library needs to ensure it uses correct lifetimes and type bounds, and then the compiler will check all interactions between these two libraries for me. That's like (M+N) complexity to deal with instead of (M*N).
- steveklabnik 2y ago> I have been told that "unsafe" affects code outside of that block, but hopefully stevelabnik may explain it better (again). It's due to a couple of different things interacting with each other: unsafe relies on invariants that safe code must also uphold, and that the privacy boundary in Rust is the module. Before we get into the unsafe stuff, I want you to consider an example. Is this Rust code okay? struct Foo { bar: usize, } impl Foo { fn set_bar(&mut self, bar: usize) { self.bar = bar; } } No unsafe shenanigans here. This code is perfectly safe, if a bit useless. Let's talk about unsafe. The canonical example of unsafe code being affected outside of unsafe itself is the implementation of Vec<T>. Vecs look something like this (the real code is different for reasons that don't really matter in this context): struct Vec<T> { ptr: *mut T, len: usize, cap: usize, } The pointer is to a bunch of Ts in a row, the length is the current number of Ts that are valid, and the capacity is the total number of Ts. The length and the capacity are different so that memory allocation is amortized; the capacity is always greater than or equal to the length. That property is very important! If the length is greater than the capacity, when we try and index into the Vec, we'd be accessing random memory. So now, this function, which is the same as Foo::set_bar, is no longer okay: impl<T> Vec<T> { fn set_len(&mut self, len: usize) { self.len = len; } } This is because the unsafe code inside of other methods of Vec<T> need to be able to rely on the fact that len <= capacity. And so you'll find that Vec<T>::set_len in Rust is marked as unsafe, even though it doesn't contain unsafe code. It still requires judicious use of to not introduce memory unsafety. And this is why the module being the privacy boundary matters: the only way to set len directly in safe Rust code is code within the same privacy boundary as the Vec<T> itself. And so, that's the same module, or its children.
- Filligree 2y ago> You need to add explicit bounds check or explicitly allocate in C though. It is not there if you do not add it yourself. Yes — in C you can skip the bounds-checks and allocation, because you can convince yourself they aren't needed; the problem is you may be wrong, either immediately or after later refactoring. In other memory-safe languages you don't risk the buffer overrun, but it's likely you'll get the bounds checks and allocation, and you have the overhead of GC. Rust is close to alone in doing both.
- johnisgood 2y agoRust is not the only one, there is Ada as well. Ada without SPARK adds bounds checks (which can be disabled through a compiler option), but with SPARK, it does not have to be done at runtime, among many other things (contract-based programming (without SPARK), formal verification (with SPARK) where you need it, and so forth), everything as a breeze. https://docs.adacore.com/spark2014-docs/html/ug/en/usage_scenarios.html https://docs.adacore.com/spark2014-docs/html/ug/en/usage_sce... Look at the table after this paragraph: > SPARK builds on the strengths of Ada to provide even more guarantees statically rather than dynamically. As summarized in the following table, Ada provides strict syntax and strong typing at compile time plus dynamic checking of run-time errors and program contracts. SPARK allows such checking to be performed statically. In addition, it enforces the use of a safer language subset and detects data flow errors statically. This is the documentation (namely SPARK User's Guide). As for what SPARK is: https://learn.adacore.com/courses/intro-to-spark/chapters/01_Overview.html#what-is-it https://learn.adacore.com/courses/intro-to-spark/chapters/01..., so you will be able to see (if you read further), that Ada alone may suffice for the majority of the cases, as for many things you do not even need SPARK to begin with. Many courses for both Ada and SPARK are available here: https://learn.adacore.com/index.html https://learn.adacore.com/index.html There are very good reasons for why Ada is used in critical systems, especially, but not limited to avionics and railway systems, see more at https://www.adacore.com/industries https://www.adacore.com/industries.
- mwkaufma 2y agoWon't the final result allocate?
- steveklabnik 2y agoIt won't allocate in this case because it's still a vec of foo at the end, so we know it has enough space. If it were a different type, it may or may not allocate, depending on if it had enough capacity.
- oneshtein 2y ago> I thought the purpose of Rust was for safety but the keyword unsafe is sprinkled liberally throughout this library. What wrong with that?
- dietr1ch 2y ago> I thought the purpose of Rust was for safety but the keyword unsafe is sprinkled liberally throughout this library. Which is exactly the point, other languages have unsafe implicitly sprinkled in every single line. Rust tries to bound and explicitly delimit where unsafe code is to makes review and verification efforts precise.
- datadeft 2y agoI thought that the point of Rust is to have safe {} blocks (implicit) as a default and unsafe {} when you need the absolute maximum performance available. You can audit those few lines of unsafe code very easily. With C everything is unsafe and you can just forget to call free() or call it twice and you are done.
- steveklabnik 2y ago> unsafe {} when you need the absolute maximum performance available. Unsafe code is not inherently faster than safe code, though sometimes, it is. Unsafe is for when you want to do something that is legal, but the compiler cannot understand that it is legal.
- datadeft 2y agoTrue, however I only saw this happens to achieve max perf. I have very limited experience so this is confirmation bias from my end.
- steveklabnik 2y agoAn example of unsafe not for performance is when interacting with hardware directly.
- deleted 2y ago[deleted]
- ycombinatrix 2y agoor the operating system! opening stdout with file handle 0 is not guaranteed safe by the compiler. there's an "unsafe" somewhere in there.
- WD-42 2y agoIt’s not about performance, it’s about undefined behavior.
- akx 2y agoTo quote the Rust book (https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html): In addition, unsafe does not mean the code inside the block is necessarily dangerous or that it will definitely have memory safety problems: the intent is that as the programmer, you’ll ensure the code inside an unsafe block will access memory in a valid way. Since you say you already know that much Rust, you can be that programmer!
- silisili 2y agoI feel like C programmers had the same idea, and well, we see how that works out in practice.
- dijit 2y agothe problem in those cases is that C can’t help but be unsafe always. People can write memory safe code, just not 100% of the time.
- sunshowers 2y agoNo, C lacks encapsulation of unsafe code. This is very important. Encapsulation is the only way to scale local reasoning into global correctness.
- chillingeffect 2y agoEh. Good C programmers know what's safe and what's not. Often comments call out sketchy stuff. Just because it's not a language keyword, doesnt mean it's not called out. Bad C programmers though? Their stuff is more dangerous and they don't know when and don't call it out and should probably stick to Rust.
- sunshowers 2y agoNo, it's been proven over and over that simply knowing invariants is not enough, in long-term projects built by large teams where team members change over time. Even the most experienced C developers are going to fail every so often. You need tooling that automates those invariants, and you need that tooling to fail closed. I take a hard line on this stuff because we can either keep repeating the fundamental mistake of believing things like "willpower" to write correct code are real, or we can move on and adopt better tooling.
- Aurornis 2y agoUsing unsafe blocks in Rust is confusing when you first see it. The idea is that you have to opt-out of compiler safety guarantees for specific sections of code, but they’re clearly marked by the unsafe block. In good practice it’s used judiciously in a codebase where it makes sense. Those sections receive extra attention and analysis by the developers. Of course you can find sloppy codebases where people reach for unsafe as a way to get around Rust instead of writing code the Rust way, but that’s not the intent. You can also find die-hard Rust users who think unsafe should never be used and make a point to avoid libraries that use it, but that’s excessive.
- timschmidt 2y agoUnsafe is a very distinct code smell. Like the hydrogen sulfide added to natural gas to allow folks to smell a gas leak. If you smell it when you're not working on the gas lines, that's a signal.
- cmrdporcupine 2y agoLook, no. Just go read the unsafe block in question. It's just SIMD intrinsics. No memory access. No pointers. It's unsafe in name only. No need to get all moral about it.
- kccqzy 2y agoBy your line of reasoning, SIMD intrinsics functions should not be marked as unsafe in the first place. Then why are they marked as unsafe?
- cmrdporcupine 2y agoThere's no standardization of simd in Rust yet, they've been sitting in nightly unstable for years: https://doc.rust-lang.org/std/intrinsics/simd/index.html https://doc.rust-lang.org/std/intrinsics/simd/index.html So I suspect it's a matter of two things: 1. You're calling out to what's basically assembly, so buyer beware. This is basically FFI into C/asm. 2. There's no guarantee on what comes out of those 128-bit vectors after to follow any sanity or expectations, so... buyer beware. Same reason std::mem::transmute is marked unsafe. It's really the weakest form of unsafe. Still entirely within the bounds of a sane person to reason about.
- pcwalton 2y ago> Presumably with inline assembly both languages can emit what is effectively the same machine code. Is the Rust compiler a better optimizing compiler than C compilers? rustc uses LLVM just as clang does, so to a first approximation they're the same. For any given LLVM IR you can mostly write equivalent Rust and C++ that causes the respective compiler to emit it (the switch fallthrough thing mentioned in the article is interesting though!) So if you're talking about what's possible (as opposed to what's idiomatic), the question of "which language is faster" isn't very interesting.
- deleted 2y ago[deleted]
- AlotOfReading 2y agoThe key difference is that there are invariants you can rely on as a user of the library, and they'll be enforced by the compiler outside the unsafe blocks. The corresponding C invariants mostly aren't enforced by the compiler. Worse, many C programmers will actively argue that some amount of undefined behavior is "fine".
- jdefr89 2y agoNot to mention they link to libc.. All rust code does last I checked…
- techjamie 2y agoThere is an option to not link to it for instances like OS writing and embedded. Writing everything in pure Rust without libc is entirely possible, even if an effort in losing sanity when you're reimplementing every syscall you need from scratch. But even then, your code is calling out to kernel functions which are probably written in C or assembly, and therefore "dangerous." Rust code safety is overhyped frequently, but reducing an attack surface is still an improvement over not doing so.
- jdefr89 2y agoI agree and binary exploitation/Vulnerability Research is my area of expertise.. The whole "Lets port everything to Rust" is so misguided. Binary exploitation has already gotten 20x harder than say ten years ago.. Even so.. Most big breaches happen because people reuse their password or just give it out... Nation States are pretty much the only parties capable of delivering full kill chains that exploit, say chrome... That is why I moved to the embedded space.. Still so insecure...
- pdimitar 2y ago> The whole "Lets port everything to Rust" is so misguided. Well, good thing that nobody sane is saying that then.
- iknowstuff 2y agohttps://github.com/embassy-rs/embassy https://github.com/embassy-rs/embassy
- PhilipRoman 2y agoIronically using C without libc turns out to be easier (except for portability of course). The kernel ABI is much more sane than <stdio.h>. The only useful parts of libc are DNS resolution and text formatting, both of which it does rather poorly.
- einpoklum 2y ago> At what point does it really stop mattering if this is C or Rust? That depends. If, for you, safety is something relative and imperfect rather than absolute, guaranteed and reliable, then - the answer is that once you have the first non-trivial unsafe block that has not gotten standard-library-level of scrutiny. But if that's your view, you should not be all that starry-eyed about how "Rust is a safe language!" to begin with. On the other hand, if you really do want to rely on Rust's strong safety guarantees, then the answer is: From the moment you use any library with unsafe code. My 2 cents, anyway.
- koito17 2y agoThe purpose of `unsafe` is for the compiler to assume a block of code is correct. SIMD intrinsics are marked as unsafe because they take raw pointers as arguments. In safe Rust (the default), memory access is validated by the borrow checker and type system. Rust’s goal of soundness means safe Rust should never cause out-of-bounds access, use-after-free, etc; if it does, then there's a bug in the Rust compiler.
- no_wizard 2y agoHow do we know if Rust is safe unless Rust is written purely in safe Rust? Is that not true? Even validators have bugs or miss things no?
- steveklabnik 2y ago> Even validators have bugs Yep! For example, https://github.com/Speykious/cve-rs https://github.com/Speykious/cve-rs is an example of a bug in the Rust compiler, which allows something that it shouldn't. It's on its way to being fixed. > or miss things no? This is the trickier part! Yes, even proofs have axioms, that is, things that are accepted without proof, that the rest of the proof is built on top of. If an axiom is incorrect, so is the proof, even though we've proven it.
- TheDong 2y agoAnd while we're in the hypothetical extreme world somewhat separated from reality, a series of solar flares could flip a memory bit and all the error-correction bits in my ECC ram at once to change a pointer in memory, causing my safe rust to do an out of bounds write. Until we design perfectly correct computer hardware, processors, and a sun which doesn't produce solar radiation, we can't rely on totally uniform correct execution of our code, so we should give up. The reality is that while we can't prove the rust compiler is safe, we can keep using it and diligently fix any counter-examples, and that's good enough in practice. Over in the real world, where we can acknowledge "yes, it is impossible to prove the absence of all bugs" and simultaneously say "but things sure seem to be working great, so we can get on with life and fix em if/when they pop up".
- sesm 2y agoRust code emitter is Clang, the same one that Apple uses for C on their platforms. I wouldn't expect any miracles there, as Rust authors have zero influence over it. If any compiler is using any secret Clang magic, that would be Swift or Objective-C, since they are developed by Apple.
- xxs 2y agooddly enough that's not the most optimal version of crc32, e.g. it's not an avx512 variant.
- Shorel 2y agoAwesome find. This really means: Assembly language faster than C. And faster than Rust. Assembly can be very fast.
- bitwize 2y agoYou can use 'unsafe' blocks to delineate places on the hot path where you need to take the limiters off, then trust that the rest of the code will be safe. In C, all your code is unsafe. We will see more and more Rust libraries trounce their C counterparts in speed, because Rust is more fun to work in because of the above. Rust has democratized high-speed and concurrent systems programming. Projects in it will attract a larger, more diverse developer base -- developers who would be loath to touch a C code base for (very justified) fear of breaking something.
- dzaima 2y agoLooks like as of 2 weeks ago the unsafe block should no longer be required: https://github.com/rust-lang/stdarch/pull/1714 https://github.com/rust-lang/stdarch/pull/1714 ..at least outside of loads/stores. From a bit of looking at the code though it seems like a good amount of those should be doable in a safe way with some abstractions.
- gf000 2y agoRust's borrow checker still checks within unsafe blocks, so unless you are only operating with raw pointers (and not accessing certain references as raw pointers in some small, well-defined blocks) across the whole program it will be significantly more safe than C. Especially given all the other language benefits, like a proper type system that can encode a bunch of invariants, no footguns at every line/initialization/cast, etc.
- acdha 2y agoYes. I think it’s easy to underestimate how much the richer language and library ecosystem chip away at the attack surface area. So many past vulnerabilities have been in code which isn’t dealing with low-level interfaces or weird performance optimizations and wouldn’t need to use unsafe. There’ve been so many vulnerabilities in crypto code which weren’t the encryption or hashing algorithms but things like x509/ASN parsing, logging, or the kind of option/error handling logic a Rust programmer would use the type system to validate.
- asveikau 2y ago> At what point does it really stop mattering if this is C or Rust? If I read TFA correctly, they came up with a library that is API compatible with the C one, but they've measured to be faster. At that point I think in addition to safety benefits in other parts of the library (apart from unsafe micro optimizations as quoted), what they're leveraging is better compiler technology. Intuitively, I start to assume that the rust compiler can perhaps get away with more optimizations that might not be safe to assume in C.
- deleted 2y ago[deleted]
- cbarrick 2y agoOthers have already addressed the "unsafe" smell. I think the bigger point here is that doing SIMD in Rust is still painful. There are efforts like portable-simd [1] to make this better, but in practice, many people are dropping down to low-level SIMD intrinsics and/or inline assembly, which are no better than their C equivalents. [1]: https://github.com/rust-lang/portable-simd https://github.com/rust-lang/portable-simd
- throwaway2037 2y ago> Is the Rust compiler a better optimizing compiler than C compilers? First, I assume that the main Rust compiler uses LLVM. I also assume (big leap here!) that the LLVM optimization process is language agnostic (ChatGPT agrees, whatever that is worth). As long as the language frontend can compiler to LLVM language-independent intermediate representation (IR), then all languages can equally benefit from the optimizer.
- pjmlp 2y agoIt goes both ways, many C folks call files full of inline Assembly and compiler specific extensions, C.
- School-Cotton 2y ago> I thought the purpose of Rust was for safety but the keyword unsafe is sprinkled liberally throughout this library. This is such a widespread misunderstanding… one of the points of rust (there are many other advantages that have nothing to do with safety, but let’s ignore those for now) is that you can build safe interfaces, possibly on top of unsafe code. It’s not that all code is magically safe all the time.
- atoav 2y agoThere are certain optimizations you can only make with unsafe, because the borrow checker is smart, but not all-knowing. There have been countless discussions how unsafe isn't the ideal name. It should be more like in the meaning of trust the programmer that they checked this manually. That being said, most rust programs don't ever need to use unsafe directly. If you go very low level or tune for prrformance it might become useful however. Or if you're lazy and just want to stop the borrow checker from saving your ass.
- sidkshatriya 2y agoYou can choose unsafe rust which has many more optimizations and is much faster than safe rust. Both are legitimate dialects of the language. Should you not feel confident with a library that is too “unsafe” you can use another crate. The rust ecosystem is quite big by now. Personally I would still use unsafe safe rust than raw C which has more edge cases. Also when I’m not on the critical path I can always use safe rust.
- fxtentacle 2y agoYeah, this article about a rust "win" perfectly illustrates why I distrust all good news about it. Rust zlib is faster than zlib-ng, but the latter isn't a particularly fast C contender. Chrome ships a faster C zlib library which Rust could not beat. Rust beat C by using pre-optimized code paths and then C function pointers inside unsafe. Plus C SIMD inside unsafe. I'd summarize the article as: generous chunks of C embedded into unsafe blocks help Rust to be almost as fast as Chrome's C Zlib. Yay! Rust sure showed it's superiority here!!!!1!1111
- FreshOldMage 2y agoDid you even read the article? They compare specifically against the Chrome zlib library and beat it at 10 out of 13 chunk sizes considered.
- torginus 2y agoI wonder why writing SIMD in high-level languages hasn't been figured out yet for CPUs (it has been the norm for GPUs for since forever). Auto-vectorization universally sucks, so do OpenMP directives. There was Ispc, which was a separate C-like programming language just for SIMD, but I don't understand why can't regular compilers generated high-quality vectorized code.
- YoshiRulz 2y ago.NET (C#) is getting there with Vector<T>.
- torginus 2y agoThat's just syntactic sugar (and a bit of architecture independence) over intrinsics. You can get the same in C++ just with wrapping intrinsics in classes, and a few ifdefs.
- queuebert 2y agoWhy do you say that? I would say SIMD is pretty well figured out in well-written code, e.g. small, tight loops over vectors. Unrolling and vectorizing a loop is not that hard and happens constantly on all our phones for signal processing, for example.
- Sharlin 2y agoTo be fair, there's a safe portable SIMD abstraction brewing in `std::simd` but it's not stable yet. SIMD is just a terrible mess of platform differences in general and making a SIMD-using program safe means ensuring the availability of every single intrinsic used, lest the program is unsound. Of course that's not what C or C++ programs typically do, but in that world unsoundness is the norm anyway.
- keybored 2y ago> Kidding aside, I thought the purpose of Rust was for safety but the keyword unsafe is sprinkled liberally throughout this library. At what point does it really stop mattering if this is C or Rust? Kidding aside the 150-comment Unsafe Rust subthread was inevitable.