5 ms·
Bit of a mixed bag. Slide 12: > Go etc. are garbage collected, making interacting with C either impossible or excruciatingly slow. "Impossible" is, of course
by tom_mellior 8y ago
Bit of a mixed bag.
Slide 12:
> Go etc. are garbage collected, making interacting with C either impossible or excruciatingly slow.
"Impossible" is, of course, a lie. "Excruciatingly slow" may be the case, though it depends on the kind of interaction you are looking at. Without any context, this statement is overly general.
Slide 14 can be taken out of context and misrepresented as showing that "Rust can be faster than C", although the programs being compared use different data structures, so it's inconclusive. (It's not clear to me whether the author wanted to make a broad claim like this. The title suggests so, but again, the difference in data structures suggests otherwise.)
Slide 16:
> every operating system retains some assembly for reasons of performance
Is this really the case? I would be interested in examples. My impression was that assembly is needed for hardware interaction for which there are simply no C constructs available (setting timers, interrupts, system registers, and whatnot), and possibly highly space-constrained bootloader stuff, but performance? C compilers are not stupid.
Slide 19:
> in-kernel C tends to be de facto safe
This is a bit of a stretch. I mean, yes, most lines of the Linux kernel have never been the cause of a CVE. But it's hard to tell which lines are the critical ones. Properly encapsulating safe/unsafe regions does seem like an improvement. (My understanding is that Rust's "unsafe" is not properly encapsulated: You might mess something up that will cause a segfault later on, in ostensibly "safe" code. Still, it's a step forward.)
- jcranmer 8y ago>> in-kernel C tends to be de facto safe > This is a bit of a stretch. Not "a bit;" quite a lot of a stretch. What makes C problematic is that the language is specified in terms of an abstract machine that looks absolutely nothing like real hardware, and executing instructions that make no sense in the abstract machine but are very well-defined in the real hardware is liable to cause problems for the optimizer in the compiler. The assumption that it's safe is built on the principle that a) compiler vendors aren't going to break the kernel build and b) it's possible to jam enough flags onto the compiler to make the abstract machine adhere to the real semantics. Both of these are dangerous assumptions. One of the more infamous compiler-writers-are-breaking-code-for-no-reason rants is the optimization that assumes that a dereferenced pointer cannot be null, so it deletes null checks. Of course, the actual code that caused that rant was this: static unsigned int tun_chr_poll(struct file *file, poll_table * wait) { struct tun_file *tfile = file->private_data; struct tun_struct *tun = __tun_get(tfile); struct sock *sk = tun->sk; unsigned int mask = 0; if (!tun) return POLLERR; This isn't some advanced compiler optimization breaking your code for being an idiot. That code would only work in the first place if you had compiler optimizations that pushed the dereference of tun below the check, which means there's no simple abstract machine interpretation that would make it work.
- tom_ 8y agoWhat if the implementation handles dereferences of invalid pointers by accessing the value at the corresponding address, and all possible addresses are all valid? (I have worked on a system that worked liked this. There was some kind of process information block at address 0, and NULL at runtime was all bits reset. The system compiler was not gcc.)
- protomyth 8y agoWhat makes C problematic is that the language is specified in terms of an abstract machine that looks absolutely nothing like real hardware, and executing instructions that make no sense in the abstract machine but are very well-defined in the real hardware is liable to cause problems for the optimizer in the compiler. Is there a language (other than assembly) that would have a closer model to the hardware?
- jcranmer 8y agoTo my knowledge, there isn't any language that really tries to be the kind of "portable assembler" that people try to shoehorn C into. There just really isn't the demand for it, since trying to be a portable assembler requires trading off fidelity to hardware for optimization, and most people would rather have faster code than more accurate hardware representation. The internal IRs of compilers (such as LLVM IR or GCC GIMPLE) is likely to be much more accurate to hardware (and the ones I called out are indeed so). It's debatable if you want to actually call these non-assembly languages, though.
- tom_mellior 8y agoThe claim about compiler IRs is strange. For one thing, hardware is different. Which hardware are you claiming LLVM IR is "accurate" to? More concretely, LLVM IR has integer types for any bit size, while actual hardware architectures have only a few. Many real CPUs have condition code registers set as side effects of operations, while LLVM requires explicit compares, etc.
- 8y ago
- wyldfire 8y ago> (setting timers, interrupts, system registers, and whatnot) Yes, you're right. Some of those can occasionally be accessed via memory mapped registers, but at least some inline assembly should be expected/required for most architectures. So it's not merely performance. Though it also makes sense for some critical tasks to be tuned via intrinsics or assembly.
- rwmj 8y agoWe have tons of production OCaml code interacting with C and it's not "excruciatingly slow" at all, in fact it's very fast indeed.
- taeric 8y agoThe problem with many of these "very fast indeed" claims, is that they usually compare to not interfacing with C at all. That is, how much is the overhead of the call from OCaml to C? Not just how much faster did the entire system get. My hunch is that it is still quite favorable.
- rwmj 8y agoThere are two ways to call into C code from OCaml depending on whether the C code needs to allocate on the OCaml heap or not. If not (the so-called "noalloc" method) then it's just a call instruction. Otherwise it goes through a trampoline which IIRC uses a computed branch which probably kills branch prediction. One other problem is that you cannot inline C into OCaml functions (which is generally true when mixing languages) so there's always some overhead even in the noalloc case, although it will be fairly small on modern CPUs. Edit: https://camltastic.blogspot.com/2008/08/tip-calling-c-functions-directly-with.html https://camltastic.blogspot.com/2008/08/tip-calling-c-functi...
- arcticbull 8y ago> Slide 12: IMO the big GC related issue is lack of determinism especially in the management of external resources. If you need that in a managed language you're forced into grafting reference counting on top which is particularly error-prone and unpleasant. Interacting with other languages can be slow, and bridging uncomfortable. > Slide 14: Rust can be faster than C in the general case because of the optimizations permitted by, among other things, strict aliasing rules. [1] The Rust language invariants are stronger and therefore the compiler can be more aggressive. > Slide 16: IMO you can do all that stuff in C with a combination of volatile pointer I/O and if necessary compiler intrinsics. I've done a lot of AVR programming and never dropped down to assembler because something wasn't available in C. I'd imagine the answer is really neither, just that some people are set in their ways and view ASM as 'faster' even though in reality LLVM would probably generate faster code by better taking into account instruction issue and data dependencies. > Slide 19: Unsafe Rust is unsafe because you can't express what you want in safe Rust. To your point, you can absolutely break things. The implicit contract is when writing unsafe sections that you are to maintain the invariants of the safe language at entry and exit of the block. What makes safe Rust safer than C, though, is all the flagship features. You can detect data races across threads (and interrupts) statically. You can't write to and read from data at the same time. You have fully deterministic dynamic collection of resources. etc. [1] https://robert.ocallahan.org/2017/04/rust-optimizations-that-c-cant-do_5.html https://robert.ocallahan.org/2017/04/rust-optimizations-that...
- weberc2 8y ago> MO the big GC related issue is lack of determinism especially in the management of external resources. This isn't a property of GCs per se; it's just that most GCs are optimized for throughput. Go's GC's pause times are on the order of 1ms, which might not be appropriate for every application, but it's probably fine for soft-realtime systems. There are a lot of other levers one could imagine as well, like semantics for demarcating critical sections.
- arcticbull 8y agoLack of determinism is absolutely a property of GCs, by definition. You’re never 100% sure when a resource is going to get deallocated/finalized with absolute certainty. This is a dealbreaker for kernel type applications. For instance think of a network socket wrapped in a structure, you can’t rely on a finalizer invocation to close your file descriptor, you don’t know when if ever it will happen so you have to wrap it with your own brand of ref counting. I’m not criticizing pause times, but rather that you can’t rely on code inspection to identify the point of resource deallocation, which is pretty much the flagship feature of a GC :)
- steelframe 8y agoPrimarily because it's written in C, the Linux kernel is currently in a full-blown security crisis. Nobody has credibly challenged the facts that Dmitry has laid bare, but at the same time, few people are motivated to fix the problem. https://www.youtube.com/watch?v=qrBVXxZDVQY https://www.youtube.com/watch?v=qrBVXxZDVQY