26 ms·
C Is Not a Low-Level Language
- burke 8y agoC is Not a True Scotsman
- mar77i 8y agoDamn Scots! They ruined Scotland!
- judge2020 8y agoArchive.is link, as the page loaded incredibly slow for me: http://archive.is/E9s70 http://archive.is/E9s70
- thinkling 8y agoTLDR: C was close-to-the-metal on the PDP-11 but since then hardware has become more complex while exposing the same abstraction to the C programmer. That means that hardware features such as speculative execution and L1/L2 caching are invisible to the programmer. This was the cause of Spectre and Meltdown and it forces a lot of complexity into the compiler. GPUs achieve high performance in part because their programming model goes beyond C. Processors would be able to evolve if they weren't hamstrung by having to support C.
- arghwhat 8y agoIt is very important to emphasize that GPU's only "achieve high performance" in workloads tailored very specifically to their extremely limited architecture. CPU's, on the other hand, are designed to be much more generic with decent performance for any task.
- derefr 8y agoI wouldn't say this is precisely true. Look at Dolphin's "ubershaders" (https://dolphin-emu.org/blog/2017/07/30/ubershaders/ https://dolphin-emu.org/blog/2017/07/30/ubershaders/): they're essentially a Turing machine, running on your GPU, used to emulate a GPU of another architecture... and yet this is still (much!) faster than doing the same on the CPU. And there's nothing special about emulating a GPU on a GPU; you could emulate a CPU architecture just as easily, at a much higher level than you get from an FPGA, and so perhaps faster than you'd be able to get from today's FPGAs. And, if you're mapping GPU shader units 1:1 to VM schedulers, you'd also get a far higher degree of core parallelism than even a Xeon Phi-like architecture would give you. (The big limitation is that you'd be very limited in I/O bandwidth out to main memory; but each shader unit would be able to reserve its own small amount of VRAM texture space—i.e. NUMA memory—to work with.) I'm still waiting for someone to port Erlang's BEAM VM to run on a GPU; it'd be a perfect fit. :)
- umanwizard 8y agoI think it's more accurate to say that CPUs are high-performance for different tasks than GPUs. Simple code, very wide data workloads are atrociously slow on CPUs, and single-threaded heavily branching workloads are atrociously slow on GPUs. That doesn't mean that one is more limited than the other.
- arghwhat 8y agoI would argue that the total set of "good performance" workloads are smaller on a GPU than they are on a CPU. However, "good performance" on a CPU is much much worse than "good performance" on a GPU. CPU's just achieve their mediocre performance on a larger set of usecases. GPU's are specialized devices that are very good at specialized activities.
- hexane360 8y agoThe whole point of the article is that this is largely a myth imposed by the memory model of C and C-like languages. Single-threaded heavily branching workloads would be atrociously slow on modern CPUs, if not for branch prediction (which caused Spectre). On modern processors, you have 180 instructions running in one thread. The processor is doing an ok job filling these instructions, but a language and compiler can do a much better job. C doesn't collect any information about data dependencies, and instead just pretends all instructions are sequential. Even code which contains tight loops of sequential commands can be optimized, because you have an entire program and operating system running around that sequential code.
- occamrazor 8y agoI don’t understand. CPUs do not support C, they support a specific instruction set. What stops them from having instructions for cache management, pipelining, speculative execution hints, etc?
- tathougies 8y agoThe Itanium processor did exactly this. Other than being a commercial flop, it was found to be quite difficult to actually get the compiler to generate good management instructions, and x86 was often able to beat out an itanium core at the same clock speed
- hexane360 8y agoIt seems like x86 exists at a local maxima, and Itanium didn't go far enough away to find a different peak.
- coliveira 8y agoThey do not support C officially, but every CPU designer knows that 99%+ of the code that matters is written in C. Therefore they design chips targeting this translation from C. What the authors want is a better lower level interface that would allow for modern processor features without the legacy of the features available to the PDP11.
- umanwizard 8y ago> 99%+ of the code that matters is written in C I think a better way of stating this is "99% of the code that matters is written in C, or in a language designed with a similar target architecture as C in mind". Certainly a lot of code that matters is written in C++, Objective-C, and Java, but the same points hold true for all of those.
- mattnewport 8y agoMany of these features are very difficult to use in a useful way in a static context (e.g. at compile time) because the performance gains mostly come from taking advantage of dynamic context. Speculative execution and out of order execution for example are mostly useful because you don't know at compile time exactly what data your code is processing or what CPU it is running on, what function / context you are being called from, what is in cache and what isn't, etc. The SPUs on the PlayStation 3 were an experiment in user managed caches and that proved to be a difficult thing to make effective use of even in games where you know more context than a lot of code can assume.
- jackhack 8y agoThanks for the TLDR. But if that's the argument, then not even assembly is sufficient, as control over speculative branching and prefetch is only accessible via microcode in the CPU. I think the argument is improperly framed. This is a discussion over public and private interface. The CPU is treated as a black box with a public interface (the x86+ instruction set). Precisely how those instructions are implemented (on chip microcode) is a private matter for the chip design team, which if correctly implemented, does not matter to the user, as the results should be correct and consistent. Obviously, a poor implementation can lead to Spectre or Meltdown. But for the most part the specific transistors & diodes used to sum a set of integers, or transfer a word from L2 to L3 cache, etc. shouldn't matter to us. If the compilers are relying on side effects to alter behavior of the internal implementation based on performance evidence, then that is a boundary violation. C is low level. It remains "universal assembly language".
- umanwizard 8y agoYou make good points - if we're just talking about semantics, then yes C is the closest portable language to x86 or arm and is low-level in that sense. But on the other hand, semantics is not always the only important thing: performance is sometimes important also, and there these low-level details matter. The architecture does its best to hide them from the user, but the abstraction is very leaky. For example, when writing high-performance CPU-bound code it's usually important to keep in mind how wide cache lines are, but C doesn't expose this to the programmer in a natural way.
- PeterisP 8y agoThe argument implied in the article is that choosing a different public interface (breaking "C compatibility" and the imposted limitations) could bring a serious performance improvement. While precisely how those instructions are implemented (on chip microcode) is a private matter for the chip design team, we do care how much resources it takes to implement these instructions, since if we can enable a more efficient implementation then we can get better price/performance.
- kartickv 8y ago
- kevstev 8y agoI was with you until the last sentence: "Processors would be able to evolve if they weren't hamstrung by having to support C." I don't think its fair or correct to say that C is the real issue. Recently there have been languages like erlang and support for more functional models that make concurrent code a lot easier to write. The first real consumer multicore processors were only released a bit over 10 years with Intel's Core 2 duo's. Of course SMP systems existed before that, Sun had them for years, but they were relatively niche. Still, Java, C++, C#, are all languages that produce much easier to maintain code if they are single threaded. Recent darlings like JS and Python are single threaded out of the box. The large majority of languages in use today are not designed to be concurrent as a first principle. True multicore systems have been around for decades, software and mindshare is now starting to catch up and use tools that make concurrency easy.
- jacquesm 8y ago> Recently there have been languages like erlang Erlang is decades old. It's 32, only 16 years younger than C.
- SomeHacker44 8y agoMy Symbolics computers (running Symbolics Ivory processors) run Lisp really well, as well as C - they have a C compiler. I have operational computers of a variety of architectures at home, including the oldest generations (6502, 680x0), Sparc, Symbolics, DEC Alpha, MIPS 32- and 64-bit, etc., and even an extremely rare (and unfortunately not-running) Multiflow, the granddaddy of VLIW. My favorite part of the original article was the final section. I wish we had a modern CPU renassiance akin to what was going on in the 80s and 90s, but the market dominance of x64 and ARM seems to be squelching things, with optimizations to those architectures rather than novel new ones (with possibly novel new compiler technologies). 64-bit ARM was a nice little improvement, though.
- ajross 8y agoYeah, but that's just reinventing the mistakes of VLIW all over again. Yes, CPUs have complicated behavior in a way that can't be captured by scalar imperative languages in a concise way. No, that doesn't mean that you can fix this with new abstractions. The reason C won wasn't that it forced CPUs to adhere to its particular execution metaphor[1], but that it happened upon a metaphor that could be easily expressed and supported by CPUs as they evolved over decades of progress. [1] Basically: byte-addressable memory in a single linear space, a high performance grows-down stack in that same memory space, two's complement arithmetic, and "unsurprising" cache coherence behavior. No, the last three aren't technically part of the language spec, but they're part of the model nonetheless and had successful architectures really diverged there I doubt C-like runtimes would have "won".
- deleted 8y ago[deleted]
- mattnewport 8y agoThis article makes some valid points but is overall rather misleading I think. Almost all of the reasons given why C is "not a low-level language" also apply to x86/x64 assembly. Register renaming, cache hierarchies, out of order and speculative execution etc are not visible at the assembly / machine code level either on Intel or other mainstream CPU architectures like ARM or Power PC. If C is not a low level language then a low level language does not exist for modern CPUs and since all other languages ultimately compile down to the same instruction sets they all suffer from some of the same limitations. It's really backwards compatibility of instruction sets / architectures that imposes most of these limitations. Processors that get around them to some degree like GPUs do so by abandoning some amount of backwards compatibility and/or general purpose functionality and that is in part why they haven't displaced general purpose CPUs for general purpose use.
- ge0rg 8y agoI also had the initial impression that the article is misleading, but later on the author made the point that the C compiler is doing significant work to reorder / parallelize / optimize the code. I agree that x86/x64 is not a low-level language either, but even if it was, with the description the author provided, I'd agree with his point of C not being low-level. Regarding cutting off backwards compatibility to improve the design, Intel's Itanium (affectionately called "Itanic") was a very progressive approach to shift the optimization work from the CPU (and the compiler) to just the compiler. I'm not sure what the reasons for its failing were, though.
- pcvarmint 8y agoItanic failed because Intel/HP initially still used a front side bus memory architecture, which could not support the bandwidth necessary for peak performance on anything but matrix multiplication and other computations where most of the work is done in-cache. Then Opteron came around, with its faster memory, and Intel was suddenly thrown back into reality. Itanic was also in-order (at least as far as dispatch), meaning anytime an instruction was stalled, so were all instructions in the same bundle or after it. One "non-low-level" idea on Itanic, which never really panned out in practice, was for the assembler to automatically insert stop bits ;; marking assembly code "sequence points", instead of the programmer having to do it manually. But in practice, everyone did it manually, because they'd rather know how well their bundles were being used, and whether they could move instructions around in order get the full 3 instructions / bundle (6 instructions / clock). And explicit stop bits did not provide any advantage to future hardware by marking explicit parallelism, because at every generation everyone was concerned about obtaining maximum performance on the current machine, which involved shuffling instructions into 6-instruction double-bundles, often at the expense of parallelism on future implementations (which never went beyond two bundles / clock).
- dahart 8y ago> A processor designed purely for speed, not for a compromise between speed and C support, would likely support large numbers of threads, have wide vector units, and have a much simpler memory model. Sounds like a GPU? > Running C code on such a system would be problematic, so, given the large amount of legacy C code in the world, it would not likely be a commercial success. It seems like ATI & NVIDIA are doing okay, even with C & C++ kernels. GLSL and HLSL are both C-like. What is problematic?
- tsomctl 8y agoC-like code that runs on GPUs is not even close to normal C, even though the syntax is similar. The way you layout your memory, schedule your threads, and add memory barriers is completely different. You are never going to take a piece of large C code written for a CPU and just run it directly on a GPU.
- dahart 8y agoHuh, that’s weird, I run a C++ compiler directly on my GPU code. The only difference between CPU and GPU code at the function level is whether I tag it with a __global__ macro or not, and lots of functions compile and run for both CPU and GPU. Memory layout, thread scheduling, and barriers are not features of the C language and have nothing to do with whether your C is “normal”. Those are part of the programming model of the device you’re using, and apply to all languages on that device. Normal C on an Arduino looks different than normal C on an Intel CPU which looks different than normal C on an NVIDIA GeForce.
- tsomctl 8y agoOK, I guess it comes down to what you call "normal" C. I was defining it as what would run on x86 Windows or Linux.
- my123 8y agoYou can look at C++ AMP too, it runs with all GPUs that support DX11 on Windows, and is a part of the Windows SDK. It's implemented by AMD ROCm on Linux, which also implements HIP/CUDA. Normal C/C++ can run fine on modern GPU architectures.
- Rebelgecko 8y agoGoing by their definition, I don't think there are any low level languages, at least on modern architectures. Even x86 assembly abstracts out a lot of what is going on within the CPU.
- umanwizard 8y agoThat doesn't mean the definition is useless -- rather than "C isn't a low-level language, as opposed to something else which is", the point might be "there exist no low-level languages according to most people's understanding of that term". Which is still an interesting and useful fact.
- rbanffy 8y agoIt also hides the fact C is just a couple notches above the absolute minimum most people would even consider - writing assembly code by hand - and is, effectively, the lowest most programmers will ever venture.
- fixermark 8y agoTrue, but one of the points the article makes is that in practice, there's a vast gulf of distance (person-years of C compiler development) between the C code one writes and the resulting assembly code output (and this is ignoring the fact that x86 assembly is, itself a co-evolved abstraction with C-like languages that is basically emulated on modern massively-parallel CPU architectures). In that regard, a case can be made that when you're writing in C, you're writing exactly as close to the bare metal as if you're writing in, say, Go or Haskell.
- simen 8y ago> In that regard, a case can be made that when you're writing in C, you're writing exactly as close to the bare metal as if you're writing in, say, Go or Haskell. No, you really can't. This is childish black and white thinking. The computational model of C is built on an interface exposed by the hardware. Go and Haskell build many additional abstractions on top of that same model. This article could have had a fruitful discussion about what the author is trying to say, but by choosing such a clickbait title, he managed to turn it into a discussion on semantics that wants to deny useful distinctions, because in some context (not the context in which it's actually used), it doesn't fit. This kind of linguistic wankery really pisses me off, because it's useless and rests on a misunderstanding of how people actually use language (which is to say, in context and often in relative terms).
- retrogradeorbit 8y agoIt's all relative. Lower level than what? Higher level than what? C is lower level than a huge number of other languages so I would feel comfortable calling it 'low level'.
- Avshalom 8y agoRelative to dozens of years of "portable assembly" and "C makes you understand how a computer works" and "C is efficient because it maps to almost 1:1 with CPU operations" and a jillion of related claims.
- jacquesm 8y agoDepending on your hardware that is still the case. There are plenty of embedded systems where these claims still hold. It's not really C that has changed (though the language has evolved a little bit), it's the hardware that changed and the implementation of the language.
- cestith 8y agoThat's the main thrust I got from the article. It does depend on the hardware, and for mainstream desktop and server hardware it no longer maps well to what the machine is doing.
- waynecochran 8y agoOk, w/o dipping into machine code, show me a low level language. Any snippet of C-code is transparent in that you know roughly how it is going to be translated into machine code.
- anonlastname 8y agoVHDL and other hardware description languages
- arghwhat 8y agoVHDL is as low-level for an FPGA as C is for a CPU. VHDL is almost low-level for an ASIC, where you can implement logic more directly. But even then, VHDL is an abstraction.
- anonlastname 8y agoHardware description languages like VHDL, maybe gpu shader languages like CUDA/HLSL
- United857 8y agoHLSL isn't a low-level language, indeed HL stands for high level. It's not much different than CPU. The runtime compiles down to a standard bytecode, and the driver translates to the GPU's proprietary native code.
- Someone 8y ago”Any snippet of C-code is transparent in that you know roughly how it is going to be translated into machine code.” …for a definition of ‘roughly’ that has become significantly less precise over the past decades. For example, there was a time where you could be reasonably sure every multiplication in your source code mapped to a multiplication instruction, but that time has long been gone. Constant folding, replacement of multiplications by shifts and loop hoisting aren’t exactly novel techniques.
- umanwizard 8y ago
- deleted 8y ago[deleted]
- lowken10 8y agoIf the general public & tech community refers to C as a low level language then it is a low level language.
- pjmlp 8y agoWhen a lie gets repeated enough times, eventually it becomes a fact.
- sametmax 8y agoIt's just practical. Otherwise how do you call java ? Or python ? And what would be the benefit of changing those particular sementics ? In french we define such article as "fucking a fly".
- mr_toad 8y agoJava and Python are much closer to C than C is to assembly. Managing memory and raw pointers are nothing. Try implementing a recursive function call in x86 assembler to get a real idea of low level.
- vardump 8y ago> Try implementing a recursive function call in x86 assembler to get a real idea of low level. Isn't that pretty easy? Recursion is just a function calling itself, directly or indirectly. Here's the simplest possible recursive function in x86 assembler. It simply causes a stack overflow. recursion: call recursion That's it. Tail recursive version would be simply a jump: recursion: jmp recursion Here's the simplest terminating version I could think of: recursion: dec eax jz rec_exit call recursion rec_exit: ret
- mr_toad 8y agoA call is not a function; functions have parameters and return values, and you’ll need at least one local variable. And you need to save the values of all the registers if you don’t want the called function overwriting them. You’ll need to manually push that all onto the stack with each call. There’s no compiler to do all that work for you.
- ovao 8y agoTo me the argument's akin to suggesting that Robert Wadlow wasn't tall, because giraffes are taller than Robert Wadlow. When the spectrum of the context is unambiguous, that's not an argument for finding a way to make it ambiguous.
- Sean1708 8y agoI think that would be a fair point if the article was about whether or not we should call C a low-level language, but the article is actually about whether C maps cleanly onto what the machine actually does and what a machine might look like if we didn't have that expectation.
- umanwizard 8y agoThe points made in the article are certainly valid, but C is low-level in an abstract sense: it is approximately the intersection of all mainstream languages. I.e. if a feature exists in C, it probably exists in every language most programmers are familiar with. (I worded this statement carefully to exclude exotic languages like Haskell or Erlang). Thus C, while not low-level relative to actual hardware, is low-level relative to programmers' mental model of programming. If this is what we mean, it's still true and useful to think of C as a low-level language. That said, it's important to keep the distinction in mind -- statements like "C maps to machine operations in a straightforward way" have been categorically wrong for decades.
- saagarjha 8y agoOne feature that many languages don't provide is the ability to have direct control over how aggregates are organized in memory.
- typomatic 8y ago> I worded this statement carefully to exclude exotic languages like Haskell or Erlang I suspect that your definition of "exotic" is exactly "not like C".
- munificent 8y ago> if a feature exists in C, it probably exists in every language most programmers are familiar with. I don't think that's true. Off the top of my head, C has: array point decay, padding, bit fields, static types, stack allocated arrays, integers of various sizes, untagged enums, goto, labels, pointer arithmetic, setjmp/longjmp, static variables, void pointers, the C preprocessor. Those features are all absent in many other languages and are totally foreign to users that only know those languages. A large part of C is exposing a model that memory is a freely-interpretable giant array of bytes. Most other languages today are memory safe and go out of their way to not expose that model.
- United857 8y agoIt's worth noting that chips that were designed for high-performance computing (e.g. the Cell) from the outset generally don't have silicon devoted to things like out of order execution, register renaming, etc. In this case, the bulk of the optimization logic does shift to the programmer (aided by the compiler). The reason is that in these domains (e.g. game consoles, supercomputing), you know ahead of time the precise hardware characteristics of your target, you can assume it won't change, and can thus optimize specifically for that ahead of time. This isn't true for "mass-market" software that needs to run across multiple devices, with many variants of a given architecture.
- obl 8y agoThe point of dynamic optimizations (such as ooo) is not only to hide implementation details (such as register file size) but very much to take advantage of dynamic opportunities that simply cannot be known statically. The optimal schedule can be very different depending on whether some load hit L1 vs L2 or even was forwarded from the store buffer. There are some classes of very regular algorithm where you could probably predict everything (and handle the memory hierarchy) statically, such as GEMM, but it's not very common.
- mattnewport 8y agoYeah, this is a very important point that many people seem to be missing, including the authors of the original article it seems to me. It was certainly a big problem for performance of games on Cell in my experience.
- mattnewport 8y ago> The reason is that in these domains (e.g. game consoles, supercomputing), you know ahead of time the precise hardware characteristics of your target, you can assume it won't change, and can thus optimize specifically for that ahead of time. Cell was a failure in large part because this proved to be less true / less relevant than its designers thought. Source: many late nights / weekends trying to get PS3 launch titles performing well enough to ship.
- arghwhat 8y agoIt is correct that C is not really a low level language, but the points about how C limits the processor doesn't make much sense. It uses UltraSPARC T1 and above processors as an example for a "better" processor "not made for C", but this argument makes no sense at all. The "unique" approach in the UltraSPARC T1 was to aim for many simple cores rather than few large cores. This is simply about prioritizing silicon. Huge cores, many cores, small/cheap/simple/efficient die. Pick two. I'm sure Sun would have loved to cram huge caches in there, as it would benefit everything, but budgets, deadlines and target prices must be met. Furthermore, the UltraSPARC T1 was designed to support existing C and Java applications (this was Sun, remember?), despite the claim that this was a processor "not designed for traditional C". There are very few hardware features that one can add to a conventional CPU (which even includes things like the Mill architecture) that would not benefit C as well, and I cannot possibly imagine a feature that would benefit other languages that would be harmful to C. The example of loop count inference for use of ARM SVE being hard in C is particularly bad It is certainly no harder in the common use of a for loop than it is to deduce the length of an array on which a map function is applied. I cannot imagine a single compromise done on a CPU as a result of conventional programming/C. That is, short of replacing the CPU with an entirely different device type, such as a GPU or FPGA.
- dgreensp 8y agoThe point is specifically about parallel vs sequential programs. Legacy C code is sequential, and the C model makes parallel programming very difficult. I met a guy back in college, a PhD who went to work at Intel, who told me the same thing. In theory, the future of general purpose computing was tons of small cores. In practice, Intel's customers just wanted existing C code to keep running exponentially faster.
- arghwhat 8y ago> Legacy C code is sequential, and the C model makes parallel programming very difficult. Neither of these statements are true, unless "Legacy" refers to the early days of UNIX. Tasks that parallelize poorly do not benefit of many small cores. This is usually a result of either dealing with a problem that does not parallelize, or just an implementation that does not parallelize (because of a poor design). Neither of these attributes are related to language choice. An example of something that does not parallelize at all would be an AES256-CBC implementation. It doesn't matter what your tool is: Erlang, Haskell, Go, Rust, even VHDL. It cannot be parallelized or pipelined. INFLATE has a similar issue. For such algorithms, the only way to increase throughput is to increase single-threaded performance. Increasing cores increase total capacity, but cannot increase throughput. For other tasks, synchronization costs of parallelization is too high. I work for a high performance network equipment manufacturer (100Gb/s+), and we are certainly limited by sequential performance. We have custom hardware in order to load balance data to different CPU sockets, as software based load distribution would be several orders of magnitude too slow. The CPU's just can't access memory fast enough, and many slower cores wouldn't help as they'd both be slower, and incur overheads. Go and Erlang of course provide built-in language support for easy parallelism, while in C you need to pull in pthreads or a CSP library yourself, but the C model doesn't make parallel programming "very difficult", nor is C any more sequential by nature than Rust. It is also incorrect to assume that you can parallelize your way to performance. In reality, the "tons of small cores" is mostly just good at increasing total capacity, not throughput.
- compiler-guy 8y agoThere is an entire junkyard full of processors designed to run other languages well. LISP machines in the 60s, Java machines in the 90s, many others. For whatever reason, successful general purpose silicon has almost always followed a C-ish model. It's also worth noting that Fortran runs quite well on C-ish style processors.
- gpderetta 8y agoExactly. While CPU designers c will certainly make sure they can run C code fast, it turns out that, for the last 40 years at least, the C model (sequential, procedural, mostly flat address space) is the most efficient to implement in hardware.
- davidw 8y ago"C combines the power and performance of assembly language with the flexibility and ease-of-use of assembly language."
- _pmf_ 8y agoIt's low level, but the level is not identical to the machine level.
- rhacker 8y agoThe article itself has 4 definitions or "attributes" for low-level languages that can be considered contradictory: * "A programming language is low level when its programs require attention to the irrelevant." * Low-level languages are "close to the metal," whereas high-level languages are closer to how humans think. * One of the common attributes ascribed to low-level languages is that they're fast. * One of the key attributes of a low-level language is that programmers can easily understand how the language's abstract machine maps to the underlying physical machine. So basically the entire article's premise (the title) hinges on the last bullet- which can be contested. All the other mentioned attributes can be applied to Java, C, C#, C++. So failing the last bullet point doesn't apply to just C.
- dgreensp 8y agoI think the author's point is that despite being perceived as low-level, C doesn't really differ from, say, Java on the last bullet. In other words, a programmer who sits down and uses C and not Java might think, "I am being forced to pay attention to irrelevant things and think in unnatural ways, but that's because I am writing fast code using operations that map to operations done by the physical machine. In a higher-level language like Java, more of these details are out of my control because they are abstracted away by the language and handled by the compiler." I think the article does a great job dismantling this point of view, and telling the story that C is not so different from Java, aside from being unsafe and ill-specified.
- zkomp 8y agoMaybe true but I think the Java example is not that good. Java is still not that different from C. Java is more like a decendant to C and C++ - and to be honest both languages force you to pay attention to lots of irrelevant "low-level" detail, fictionally low-level since its not actually the machine but language itself (that is stuck in the PDP11 mental mode...) Compared to something different like Erlang, Haskell, Lisp
- dgreensp 8y agoHigh-level and low-level are relative, to be sure, but Java is definitely considered higher-level than C -- it was designed to target a virtual machine, for example, while C was designed to target real machines -- so I think it illustrates the article's point perfectly.
- agumonkey 8y agoThe thing is, most of the time you're reflecting at some logical level that will not be the "reality". The problem is that C programmer think that C === reality === performance. C has better (lower) constant factors but by no means better all the time.
- emilfihlman 8y agoThis article is completely clickbait. C is low level. For example, with AVRs everything you do maps very clearly to what happens as opcodes. It's like the author wants to blame C for whatever reason and conveniently forgets that C is also portable.
- cestith 8y agoThe author isn't blaming C. C has stayed largely the same. The author is saying that Intel and AMD have - unlike PIC, AVR, and such - hidden the machine from C so thoroughly that it's no longer a low-level language for that platform.
- munificent 8y agoI really really liked this article, and reading the comments here is blowing my mind. Did we read the same thing? I think it's a strong insight that insight that chip designers and compiler vendors have spent person-millenia maintaining the illusion that we are targeting a PDP-11-like platform even while the platform has grown less and less like that. And, it turns out, with things like Spectre and the performance cost of cache misses, that abstraction layer is quite leaky in potentially disastrous ways. But, at the same time, they have done such a good job of maintaining that illusion that we forget it isn't actually reality. I like the title of the article because many programmers today do still think C is a close mapping to how chips work. If you happen to be one of the enlightening minority who know that hasn't been true for a while, that's great, but I don't think it's good to criticize the title based on that.
- zengid 8y agoI agree that this article hits pretty hard against a lot of assumptions about how our machines are working. I also feel like the author is trying to say something about how imperative scalar (meaning 'operates on one datum at a time') languages are causing more trouble than they're worth. Sophie Wilson said something similar in her talk about the future of microprocessors [1]. This implies that declarative and functional semantics would be more amenable to parallelization, as the author mentions in the article, as well as allowing the compiler more freedom to deduce a suitable 'reordering' of operations that would better fit the memory access heuristics the machine is using. [1] https://youtu.be/_9mzmvhwMqw?t=26m30s https://youtu.be/_9mzmvhwMqw?t=26m30s
- jstimpfle 8y ago> cost of cache misses How is the C memory model a leaky abstraction here? What better way do you suggest? Are we not fine coding sequential (in memory) datastructures in C?
- munificent 8y agoC leads you to believe that memory access has uniform cost regardless of address. What is the perf cost of: *foo Depending on what foo points to, and which memory you have previously read, the cost can vary by close to two orders of magnitude on many chips. C does give you the ability to control those costs, but controlling how you lay out your data in memory and controlling imperatively in which order you access it. But the language doesn't show you those costs in any way.
- laythea 8y agoThe title is not a well formed statement. It all depends on what you are used to. IE. If I write Java, C is low level. If I write assembler, C is high level.
- wglb 8y agoThe article does not properly distinguish between C as a language and what the C compiler does with the C program. The logic of the article references what the compiler does. The reasonable way to measure languages is to look at the abstractions present in the language. C has fewer abstractions than the other languages that we are familiar with. That is the reasonable definition of the level of a language.
- favorited 8y agoThat's exactly the author's point. The C that programmers write is remarkably far from what the compiler generates for modern hardware. How do you propose measuring the number of abstractions? JavaScript has remarkably few built-in abstractions, but it's in no way "low-level" from a hardware perspective.
- julienfr112 8y agoOk. But what are the alternatives that are not decade away ?
- scott_s 8y agoThe author, David Chisnall, is a co-author on a related paper from PLDI 2016: "Into the Depths of C: Elaborating the De Facto Standards", https://news.ycombinator.com/item?id=11805377 https://news.ycombinator.com/item?id=11805377
- favorited 8y agoHe was also one of the earliest non-Apple contributors to Clang, was on the FreeBSD core team, and wrote the modern GNU Objective-C runtime implementation. His work on Objective-C in particular is prolific.
- sizeofchar 8y agoAlso, his book on Objective-C is the best one I read.
- Shikadi 8y agoLanguage evolves. C is certainly lower level than C# or JavaScript, so even if it no longer fits the definition created decades ago, I don't see a problem with the term evolving to match modern times. People say assembly language when they mean assembly language, (which others have argued isn't low level any more anyway) so using low level to describe a language closer to the hardware seems valid to me. It's interesting that the author argues C could be considered low level on the PDP-11, because by the old definition used back it definitely wouldn't be. That tells me the author's definition of low level is already an evolution of the original definition, so there's no reason the term can't evolve some more. Wiki definition: "A low-level programming language is a programming language that provides little or no abstraction from a computer's instruction set architecture—commands or functions in the language map closely to processor instructions. Generally this refers to either machine code or assembly language."
- lmm 8y agoThe whole point of the article is that by a definition like the one you quoted, modern C is not low-level, though PDP-11 era C was.
- Shikadi 8y agoExcept what I'm saying is that PDP-11 era C wasn't low level. It incedentally took advantage of some low level features, but that wasn't by design, and wouldn't have changed its classification as a high level language at the time anyway
- richardwhiuk 8y agoThat's not true - PDP-11 era C isn't either - if you run it on a modern processor. And it's doubtful it even was then.
- lmm 8y ago> PDP-11 era C isn't either - if you run it on a modern processor. And it's doubtful it even was then. With a modern compiler C isn't low-level. But under PDP-11 era C compilers, C really did map closely to processor instructions.
- justicezyx 8y agoStatement of using adjective almost always is about defining the context.
- skylyrac 8y agoThis doesn't make any sense. This would mean that my C code compiled for a Cortex-M0 is low level, but for my x86 laptop is not. Or even more stupid, that the same assembly code running in an old 386 is low level, but for an i7 isn't. Low level is about how close to talking to the CPU you are, not about how close to the silicon you are. The CPU is a black box and the programmer communicates with it. What that box does inside doesn't matter.
- sigjuice 8y agoAlso, various C interpreters exist where there is no explicit C —> assembly translation.
- plpot 8y agoI find this article insightful, but missing the points it tries to deliver. What the article is very good at delivering is that current CPU's ISAs exports a model that doesn't exist in reality. Yes, we might call it PDP-11, although I miss that architecture dearly. C was never meant to be a low level language. It was a way to map loosely to assembler and provide some higher level abstraction (functions, structures, unions) to write code that was more readable, and structured, than assembler. And yes, it is far from perfect. And yes, today is called a low level language with good reasons. But this article is all about exposing the insanity that modern CPU have become, insanity that is the sacrifice to the altar of backward compatibility -- all CPU architecture that tried the path of not being compatible with older CPUs have died. I am pretty sure that once we'll have an assembler that map closely to the microcode, or to the actual architecture of the internals of a modern, parallel, NUMA architecture, we will still need to have a C-like language that will introduce higher level features to help us ease writing of non-architecture dependent parts. And it will most probably be C.
- sigjuice 8y agoWhere does it say in the ISO C standard that C must be translated to assembly code or machine code of any sort? EDIT: Various C interpreters exist
- cryptonector 8y ago> The root cause of the Spectre and Meltdown vulnerabilities was that processor architects were trying to build not just fast processors, but fast processors that expose the same abstract machine as a PDP-11. [...] This strikes me as a flavor of the VLIW+compilers-could-statically-do-more-of-the-work argument, though TFA does not mention VLIW architectures. C or not, making compilers do more of the work is not trivial, it is not even simple, not even hard -- it's insanely difficult, at least for VLIW architectures, and it's insanely difficult whether we're using C or, say, Haskell. The only concession to make is that a Haskell compiler would have a lot more freedom than a C compiler, and a much more integrated view of the code to generate, but still, it'd be insanely hard to do all of the scheduling in the compiler. Moreover, the moment you share a CPU and its caches is the moment that static scheduling no longer works, and there is a lot of economic pressure to share resources. There are reasons that this make-the-compilers-insanely-smart approach has failed. It might be more likely to be successful now than 15 years ago, and it might be more successful if applied to Rust or Haskell or some such than C, but, honestly?, I just don't believe this will work anytime soon, and it's all academic anyways as long as the CPU architects keep churning out CPUs with hidden caches and speculative execution. If you want this to be feasible, the first step is to make a CPU where you can turn off speculative execution and where there is no sharing between hardware threads. This could be an extension of existing CPUs. A much more interesting approach might be to build asynchrony right into the CPUs and their ISAs. Suppose LOADs and STOREs were asynchronous, with an AWAIT-type instruction by which to implement micro event loops... then compilers could effectively do CPS conversion and automatically make your code locally async. This is feasible because CPS conversion is well-understood, but this is a far cry from the VLIW approach. Indeed, this is a lot simpler than the VLIW approach. TFA mentions CMT and ULtraSPARC, and that's certainly a design direction, but note that it's one that makes C less of a problem anyways -- so maybe C isn't the problem... Still, IMO TFA is right that C is a large part of the problem. Evented programs and libraries written in languages that insist on immutable data structures would help a great deal. Sharing even less across HW/SW threads (not even immutable data) would still be needed in order to eliminate the need for cache coherency, but just having immutable data would help reduce cache snooping overhead in actual programs. But the CPUs will continue to be von Neuman designs at heart.
- DannyB2 8y agoThe sophistication of the compiler does not mean the language is high level. The meaning of a high level language is to do with abstraction away from the hardware. C programmers often wince at languages that are highly abstracted away from the hardware. But those are what are "high level" languages. Especially languages that remove more and more of the mechanical bookkeeping of computation. Such as garbage collection (aka automatic memory management). Strong typing or automatic typing. Dynamic arrays and other collection structures. Unlimited length integers and possibly even big-decimal numbers of unlimited precision in principle. Symbols. Pattern matching. Lambda functions. Closures. Immutable data. Object programming. Functional programming. And more. By comparison C looks pretty low level. Now I'm not knocking C. If there were a perfect language, everyone would already be using it. Consider the Functional vs Object debate. (Or vi vs emacs, tabs vs spaces, etc) But all these languages have a place, or they would not have a widespread following. They all must be doing something right for some type of problem. C is a low level language. And there is NOTHING wrong with that! It can be something to be proud of!
- kiriakasis 8y agoOne of the point of the article is that C is relatively high level by your definition. Basically it says that the C abstract machine has very little in common with most existing processor. moreover it makes the point that in the last decades of research for CPUs the focus was "make C go fast" wich ultimately cause meltdown.
- salgernon 8y agoIt feels like the author really isn't talking so much about the limitations of C on modern architectures, but the architecture itself. Possibly relevant is this (short?) discussion[1] from 2011 about a CPU more closely designed for functional programming. [1] https://news.ycombinator.com/item?id=2645423 https://news.ycombinator.com/item?id=2645423
- zwieback 8y agoGreat article if you're willing to read past headlines. I would have liked to see a mention of small processors that are still hugely popular (microcontrollers, etc.) where C is still a good fit.
- z3t4 8y agoI wonder if it's easier for a compiler/cpu to optimize "async" code ? And I often find myself having an array in JavaScript that calls the same function on each item in the array, it would be nice if such cases would be made parallel, which I think is possible to do in C++. Is that ever gonna happen in JavaScript !?
- arseraptor 8y agoAh yes, David Chisnall. Another Cambridge wannabe without a hope of tenure track who thinks he is cleverer than he really is and makes a bunch of trite points over and over hoping to get some attention -- not realising they've been made for over 20 years. Have an original thought David, and stop feeling smug. You're not.
- dang 8y agoWe've banned this account for repeatedly violating the site guidelines. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- qsdf38100 8y ago"processors wishing to keep their execution units busy running C code" What? This is non sense, the processor is not running C code! The processor can only run machine code, regardless of the language used to write the source code.
- monocasa 8y agoEh, the past thirty years has had CPUs designed to run C. That's the whole point of RISC: the idea of 'let's just pare down the CPU to what actually gets compiled, and now we have less gates in the critical path and we can run our chips faster'.
- qsdf38100 8y agohmmm, I think that CPUs instruction sets inherit mainly from the 8086, and https://en.wikipedia.org/wiki/Intel_8086 https://en.wikipedia.org/wiki/Intel_8086 mentions few languages that had influence on the 8086 design, but don't mention C at all.
- mar77i 8y agoAs far as I understood what I do about C, is that most of C's here called "quirks" have actually been enablers for much of the portability and performance of modern platforms. Therefore I don't like "undefined behavior" and the like being criticised for being such a "hindrance". I hence doubt the author's familiarity with C is much beyond the basics, which kind of makes the case for why the author also had to namedrop Spectre and Meltdown, which were caused by the fact that later optimizations were unsound, ie. the Tomasulo algorithm. The problematic with the article somewhat remind me of the problems with LCTHW, and that the author of LCTHW was unable to figure out what the deal was about had been admitted by themselves. https://zedshaw.com/2015/01/04/admitting-defeat-on-kr-in-lcthw/ https://zedshaw.com/2015/01/04/admitting-defeat-on-kr-in-lct... Sorry to re-repost this article again. I just somewhat perceive two variants same "smells" in both.
- dikaiosune 8y agofrom the article: > David Chisnall is a researcher at the University of Cambridge, where he works on programming language design and implementation. He spent several years consulting in between finishing his Ph.D. and arriving at Cambridge, during which time he also wrote books on Xen and the Objective-C and Go programming languages, as well as numerous articles. He also contributes to the LLVM, Clang, FreeBSD, GNUstep, and Étoilé open-source projects, and he dances the Argentine tango. If tango experience isn't enough to make his opinion credible, I imagine being an LLVM and Clang contributor are pretty good qualifications.
- mar77i 8y agoI don't understand how someone who ended up working on a C compiler feels intimidated by the standard/-s such software needs to adhere to. And if such people work on a C compiler, we're not logically in for a ride of WTFs?
- sytelus 8y agoInteresting tidbits from article: A modern Intel processor has up to 180 instructions in flight at a time (in stark contrast to a sequential C abstract machine, which expects each operation to complete before the next one begins). A typical heuristic for C code is that there is a branch, on average, every seven instructions. If you wish to keep such a pipeline full from a single thread, then you must guess the targets of the next 25 branches. The Clang compiler, including the relevant parts of LLVM, is around 2 million lines of code. Even just counting the analysis and transform passes required to make C run quickly adds up to almost 200,000 lines (excluding comments and blank lines).
- ChuckMcM 8y agoI enjoyed reading this, mostly because it made me angry, then curious, then thoughtful all in one go. Partly because I really like the PDP-11 architecture, and it's 'separated at birth' twin the 68K, it greatly influenced me in how I think about computation. I also believe that one of the reasons that the ATMega series of 8 bit micros were so popular was that they were more amenable to a C code generator than either the 8051 or PIC architectures were. That said, computer languages are similar to spoken languages in that a concept you want to convey can be made more easily or less easily understood by the target by the nature of the vocabulary and structure available to you. Many useful systems abstractions, queues, processes, memory maps, and schedulers are pretty easy to express in C, complex string manipulation, not so much. What has endeared C to its early users was that it was a 'low constraint' language, much like perl, it historically has had a fairly loose policy about rules in order to allow for a wider variety of expression. I don't know if that makes it 'low' but it certainly helped it be versatile.
- angry_octet 8y agoIt is instructive to consider GPUs and their compilers. The death of OpenGL in favour of Vulcan has come about because OpenGL is unable to express low level constructs which are essential to achieving performance. GPU drivers are actually compilers that recompile shaders to efficient machine expressions. Thus the fundamental limitation is that the processor has only a C ABI. If there were a vectorisation and parallel friendly ABI, then it would be possible to write high level language compilers for that. It should be possible for such an ABI to coexist with the traditional ASM/C ABI, with a mode switch for different processes.
- angry_octet 8y agos/vulcan/vulkan damn autocorrect.
- Const-me 8y agoOne reason for that is for many applications latency is much more critical than bandwidth. For PCs that’s input-to-screen latency, for servers that’s request-to-response. It’s possible to make multicore processors with simpler cores, design OS and language ecosystem around it, etc. Such tradeoffs will surely improve bandwidth, but will harm latency. Another reason is most IO devices are inherently serial. Ethernet only has 4 pairs, and wifi adapters are usually connected by a single USB or PCIx lane. If a system has limited single threaded (i.e. serial, PDP11-like) performance, it gonna be hard to produce or consume these gbits/sec of data.
- anfilt 8y agoI hate the idea of "low-level". There is not really such a thing. You should be using a language suitable for the domain your working in. Sadly, too many programming languages try to be the end all be all. C is language that is great for working at the system domain. Ideally, we would have small minimalist languages for various problem domains. In reality maintaining and building high quality compilers is a lot work. Moreover, a lot of development will just pile together whatever works. That aside, you could build a computer transistor by transistor, but it's probably more helpful to think at the logic gate level or even larger units. Heck even a transistor is just a of piece of silicon/germanium that behaves in a certain way. So there are levels abstraction, but is an abstraction low-level? I think term probably came about to refer lower layers of abstraction that build what ever system your using. So unless your using something that nothing can be added upon. Everything, even what people would call high level can be low-level. Heck, people call JS a high level language, but there are compilers that compile to JS. This makes a JS a lower level system that something else is built upon. This just again shows why I would say that low-level is often thrown around with connotation that is not exactly true.
- kev009 8y agoThe meta point from the article is that this is as much a hardware problem as it is a language or developer one. An arms race was waged to create CPUs that are very effective in running sequential programs; to the point that what they present to the program is a very much a facade and they hide an increasing great deal of internal implementation detail. By David's postulation, even the native assembly language for the CPU is not low level. To drive this juxtaposition home, I'd point to PALcode on Alpha processors in which C (and others) can very much be a low level language. Very few commercial processors let you code at the microcode level. The overarching premise is then brought home by GPU programming, which shows that you don't necessarily need to be writing at the ucode level if the ecosystem was built around how the modern hardware functioned.
- sriku 8y agoI couldn't read the article, but based on the comments, would it change the way we use C whether we declared it a "low level language" or not?
- Sean1708 8y agoThe article is actually about how closely C maps to what is actually run on the hardware and whether hardware would look significantly different today if people didn't expect C to map closely.
- 11thEarlOfMar 8y ago>403 Error - Access Forbidden We are sorry ... ... but we have temporarily restricted your access to the Digital Library. Your activity appears to be coming from some type of automated process. To ensure the availability of the Digital Library we can not allow these types of requests to continue. The restriction will be removed automatically once this activity stops. We apologize for this inconvenience. Please contact us with any questions or concerns regarding this matter: portal-feedback@hq.acm.org The ACM Digital Library is published by the Association for Computing Machinery. Copyright � 2010 ACM, Inc.
- nickpsecurity 8y agoI emailed them about it. Hopefully it will be fixed soon. Just keep a tab or bookmark. :)
- Animats 8y agoI got that, too. I'm using Firefox on Linux, with both Ghostery and Privacy Badger enabled. Not impressed with the ACM.
- molteanu 8y agoSame here.
- suprfnk 8y agoGot that too. Interesingly enough, these automated processes were able to get through: https://web.archive.org/web/20180501183242/https://queue.acm.org/detail.cfm?id=3212479 https://web.archive.org/web/20180501183242/https://queue.acm... https://webcache.googleusercontent.com/search?q=cache:sClfdAKpYXcJ:https://queue.acm.org/detail.cfm%3Fid%3D3212479+&cd=1&hl=en&ct=clnk&gl=nl https://webcache.googleusercontent.com/search?q=cache:sClfdA...
- klez 8y agoCould it be the influx of traffic with the same referral link that tripped some defense mechanism?
- nottorp 8y agoIt's still on after 9 hours from the OP. I suppose someone in ACM management is obsessed with their intellectual property being stolen, including their public articles. Good luck to them.
- smadge 8y agoMakes me wonder if x86 could be extended to expose the underlying parrellelism. How much faster would my Prolog and Haskell programs run if all branches were executed simultaneously and only the successful path down my search tree returned?
- tome 8y agoProbably not much faster, otherwise you would have just implemented that by hand.
- hokus 8y agohttp://web.archive.org/web/20180502001551/https://queue.acm.org/detail.cfm?id=3212479 http://web.archive.org/web/20180502001551/https://queue.acm....