6 ms·
#2 saves a little but not much and precludes unsafe low-level code completely. #1 I think impairs many HLLs, certainly every multithreaded imperative shared mem
by _yosefk 10y ago
#2 saves a little but not much and precludes unsafe low-level code completely. #1 I think impairs many HLLs, certainly every multithreaded imperative shared memory ones (and IMO nothing is close to these in terms of efficiency on multicore); which languages can work well without cache coherence? (I worked in C++ on multicore with no hw coherence btw. Quite the cruel and unusual punishment.)
- catern 10y ago>#2 saves a little but not much and precludes unsafe low-level code completely. Sure, in the same way most people today don't run their entire application in ring 0, but instead run on top of a kernel. Privilege levels like that are another thing that high level languages don't need. A compiler or JIT can generate low-level code just fine. >certainly every multithreaded imperative shared memory ones (and IMO nothing is close to these in terms of efficiency on multicore) You're begging the question here. Why is shared memory multithreading the fastest way available? It's because cache coherence is heavily optimized in hardware. But, of course, cache coherence is built on shared-nothing message-passing under the hood. So why not just use message-passing directly? Plenty of languages where that's the native primitive.
- Rusky 10y ago> cache coherence is built on shared-nothing message-passing under the hood Only sometimes, and only for nearer caches. Data still often gets shared by both sources reading and writing a shared memory location (whether a farther-out cache or main memory or disk).
- _yosefk 10y agoIt is not obvious to me that a compiler or JITter can generate low level code that is (A) free of bugs (compilers are notoriously buggy, as in hundreds of real bugs found by a single fuzzer like CSmith) and I don't want those bugs to hose my filesystem as it could on DOS machines, and (B) always as efficient as lower-level code. MS for instance did interesting experiments with removing virtual memory and I'm not saying it's necessarily a bad idea, just that it saves you very little and the real-world risks are higher than the theoretical risks. Shared memory multithreading is fastest because explicitly communicating all of your tasks' inputs and outputs without any caching will transfer more data than using caches where several tasks can read the same thing from the same place. Cache coherence is not only built on shared nothing-message-passing under the hood, it's built on caches. If you cache your messages/accesses/whatever somewhere, you'll have coherence problems, if you don't, you'll have efficiency problems. Just because you have a language with a native primitive doesn't mean this language doesn't throw efficiency out the window by building things around this primitive. Say, plenty of languages have cons cells for all the wrong reasons (I think Clojure is the one language that escaped this in its lineage) and still cons sells are fundamentally inefficient no matter what you do in hardware, this part I elaborated on in TFA. Just like having a language with a primitive solving the halting problem doesn't solve the halting problem, you can't have a language with arbitrary primitives and a magic machine making the sw+hw system as efficient as any alternative.
- elihu 10y agoRegarding #2, eliminating context switch overhead and system call latency by running everything in one address space would be huge for some applications. Enormous effort has been spent to allow HPC applications to talk directly to the network interface hardware without going through the OS (for instance using the Infiniband verbs API), because system calls are way too slow. This puts a lot of burden on the hardware vendors, because hardware implementation bugs might expose security vulnerabilities that could be exploited by malicious software, and there's no OS layer in between to sanity-check the hardware commands. If a system call is just calling a function, then that's not an issue anymore; you can just use system calls for everything, because it's fast. The compiler might even inline them. The OS can insert any sanity checks it wants and expose a safe API to the application. Regarding unsafe code, it's true than unsafe code blocks can violate the memory safety of the whole system, but that doesn't mean you can never use them, it just means that you need to use them carefully. An unsafe code block could be treated like kernel code or a setuid root binary. In a language like Rust, ideally many of the unsafe code patterns would be migrated into the standard library with a safe interface, and the unsafe low-level hardware interaction that the OS would need to do would be another set of unsafe code blocks. Normal application could be forbidden from using their own unsafe blocks (unless the superuser says it's okay), but they can still call into the kernel and into the standard library. (The way I imagine this working is that the operating system would have a policy of not executing any code that wasn't compiled by a trusted compiler, with compiler options dictated by some security policy. Code that was compiled on an un-trusted compiler can run inside an emulator.) If having unsafe code blocks anywhere in the system seems wrong, consider that operating system kernels are essentially large unsafe code blocks. If we can abstract out the bits that truly need to be unsafe and write the rest as safe code, the end result may be far fewer corners for memory corruption bugs to lurk in, and an overall improvement in security and stability.
- mtdewcmu 10y ago>> Regarding #2, eliminating context switch overhead and system call latency by running everything in one address space would be huge for some applications. That sounds like basically running node.js in kernel space, which is entirely doable. I think people have put HLLs into kernel space before.