4 ms·
> Which is easily shown to be false given that hardware designers have had bend backwards and into contorted shapes to emulate the hardware environment that C
by AshamedCaptain 4y ago
> Which is easily shown to be false given that hardware designers have had bend backwards and into contorted shapes to emulate the hardware environment that C was originally created to work against.
It may be a valid point, but value-less, since this basically implies that most hardware you are going to be able to buy today (ARM, x86, whatever) is going to be a fast PDP-11 (or at least it's going to present itself as one).
- tialaramex 4y agoNot quite. The modern hardware isn't actually a fast PDP-11 it's just capable of emulating one more efficiently than you would if people didn't insist on writing C. So it may be that your "clever" C algorithm which in your head translates into just six CPU operations, unfortunately on a real modern CPU is six hefty macro-ops that will take dozens of cycles to execute and repeatedly go to sleep waiting for main memory, whereas the algorithm in a modern language that looked ludicrous to your C programmer eyes compiles to sixteen tiny ops the CPU can consume two at a time with no waits and it's done in eight cycles while the C code was still waiting for a main memory read.
- AshamedCaptain 4y ago> The modern hardware isn't actually a fast PDP-11 it's just capable of emulating one more efficiently than you would if people didn't insist on writing C. I'm not disagreeing with that. It's a valid remark. What I'm arguing is that it's a useless remark in practice. > So it may be that your "clever" C algorithm which in your head translates into just six CPU operations, unfortunately on a real modern CPU is six hefty macro-ops that will take dozens of cycles to execute Note that this was already true even for the original PDP-11, and more so for the original 8086, both being ridiculously CISC and microprogrammed. C was born in this context. > whereas the algorithm in a modern language that looked ludicrous to your C programmer eyes compiles to sixteen tiny ops the CPU Even if true, it's irrelevant, since even a "modern language" will be forced to target that machine with its PDP-11 -emulating ISA, and is therefore subject to the same limitations than "modern C" is. The very point the article is making leads to the conclusion that most machines today are actually presenting themselves as a PDP-11-like, and therefore any language which does not target a PDP-11-like is doomed to a niche.
- tialaramex 4y agoThe C programmers do distort what makes sense, but they can't overcome practical realities. For example have you noticed your CPU is multi-core? The PDP-11 wasn't multi-core, and C doesn't really do well for writing concurrent software. Practically though, despite Amdahl's law it makes sense to provide a multi-core CPU. Most C software won't be able to take advantage, but some of your software is written by people who either will put the extra effort in despite C or use a different language where it wasn't so hard. Take another example, the 0-terminated string. This terrible C design is more or less omnipresent. And sure enough CPUs have features to enable this terrible data structure, because it's so omnipresent. But those features don't make a better string worse they just pull the C string closer to parity than it might be otherwise. Rust's str::len() is still much faster than C's strlen() even if your CPU vendor focuses entirely on the C market.
- astrange 4y agoNULL-terminated strings aren't bad because strlen() is slow. The performance tradeoffs are fine enough. They're bad because they have bad security properties.
- AshamedCaptain 4y agoThe one example I like is shared memory multi-processing (what you mean when you say "multi-core", because multi-core systems are older than C), because I do see it as a hack done to preserve compatibility with a more traditional programming style (or dare I say, with a more traditional "programmer"). As I said, I agree with this, but the argument is still value-less. If you don't do multiprocessing this way you'll find yourself handicapped because _all_ current machines are heavily optimized for it. There's very little point to a language that doesn't do it this way, because it would not be able to run as well on current machines! The other two examples are highly questionable. 0-terminated strings are not an artifact of the computer architecture, but a conscious trade-off, and in fact other languages already used Pascal strings (even on the PDP-11). Same as if you want your lists with a constant time size function or not. Sure your size() is now "much faster", but something else now becomes slower. In fact, you could argue x86 bends to supports _both_ string implementations, since the rep prefix does support (and update) a count. And the final example, "isdigit" is arguably not even a language issue, and definitely not a legacy of the PDP-11 nor x86 architecture. I'm rather sure no one in his right mind would even consider wasting 0.25KiB just for such function, not until well into the 90s at least. These types of code-bloat optimizations are more of the 2000s and even 2010s "performance" craze. You yourself even say on the other thread that glibc is doing this to facilitate locales, and not for performance reasons.
- Gordonjcp 4y ago> So it may be that your "clever" C algorithm which in your head translates into just six CPU operations, unfortunately on a real modern CPU is six hefty macro-ops that will take dozens of cycles to execute and repeatedly go to sleep waiting for main memory, whereas the algorithm in a modern language that looked ludicrous to your C programmer eyes compiles to sixteen tiny ops the CPU can consume two at a time with no waits and it's done in eight cycles while the C code was still waiting for a main memory read. Can you give an example of that?
- tialaramex 4y agoI can give you a smaller example if that helps. Rust's u8::is_ascii_hexdigit is a predicate which decides whether the 8-bit unsigned integer is the ASCII code for a hexadecimal digit, that is 0 through 9, A through F or a through f. Inside the implementation is exactly what a modern programmer would expect, a pattern match, it'll do a whole bunch of arithmetic operations on the 8-bit unsigned integer and get a true or false result. Your modern CPU can do that real fast, because it's all just arithmetic on registers. If you go look at a C library isxdigit() function, sometimes (not so often these days) you will find it has a mask and a LUT, it's assuming that "just" looking up the answer in memory will be faster. Your modern CPU can't do that quickly. It must calculate the index into the LUT, and then read the appropriate memory, hopefully that's in L1 cache or something, if it needs to go to main memory that takes an eternity, and either way it means something else can't be in the cache because this LUT is taking up space.
- Gordonjcp 4y agoI can't say I've ever seen a lookup table method for that, going right back to implementing that kind of function on 8-bit CPUs. Are you sure you're not just used to shitty code?
- tialaramex 4y agoGNU libc is an example of a relatively well known isxdigit() which uses a LUT even today. Its excuse is that this simplifies locale handling, you know for if your locale has hexadecimal digits in a different place from everybody else, presumably because you're from some alternate universe where Z is a hex digit.
- bluecalm 4y agoIt might be but it isn't. The promise of clever modern language beating C in performance is almost as old as C and we are yet to see one.
- tialaramex 4y agoYou didn't need to wait, Fortran has better performance than C from the outset.