17 ms·
Why Is C Faster Than Java (2009)
- akkartik 5y agoThe flip side of getting great performance is all the exposure to security vulnerabilities. Git seems to have an excellent security track record: https://www.cvedetails.com/vulnerability-list/vendor_id-4008/GIT.html https://www.cvedetails.com/vulnerability-list/vendor_id-4008... However, I think the right claim here is, "be very sure you have the chops to benefit from C's speed without compromising security."
- YPPH 5y agoIs that necessarily the case? From what I've heard, Rust offers great performance and safety.
- II2II 5y agoFrom my understanding, Rust performs more compile time checks for safety. In those cases, you may be able to maintain the performance of C without sacrificing safety. On the other hand, you may still get away with writing more performant C code since C allows the developer to play fast and loose with the rules. I'm not saying that is a good thing, but git can get away with it since it since it probably attracts very competent developers. The other thing mentioned in the post was eliminating overhead. My limited experience with Rust leads me to believe it is closer to C++ than C in this respect. That is to say there is a lot of higher level functionality that you can use and it is optimized for performance in the general case. On the other hand, that functionality is still for the general case, so a competent developer who has a thorough knowledge of what the program is trying to accomplish will be able to write more performant code.
- School-Cotton 5y ago> C allows the developer to play fast and loose with the rules. So does Rust, in unsafe blocks. Is there anything that can be done in C that can’t be done in unsafe Rust?
- vvanders 5y agoOnly thing that comes to mind immediately is VLA/alloca but I'd have to check. Most of the gnarly pointer things are fair game and work just as well as their C counterparts.
- bbatha 5y agoRust supports VLAs, Box<[T]> is pretty common in fact. It just doesn’t have a native way to alloc them on the stack.
- pjmlp 5y agoVLA are thankfully no longer required for ISO C compliance, who had thought it was a good idea in first place? Google even sponsored the work to clean the Linux kernel from them.
- cesarb 5y ago> Is there anything that can be done in C that can’t be done in unsafe Rust? Duff's device <https://en.wikipedia.org/wiki/Duff's_device https://en.wikipedia.org/wiki/Duff's_device>.
- josephcsible 5y agoYou're mixing up a thing you can do with a way that you can do something. You can write Rust code with the same observable behavior as Duff's device in C.
- kaba0 5y agoAlso, Duff’s device is pretty terrible and a modern compiler will output much better code.
- LAC-Tech 5y ago> You're mixing up a thing you can do with a way that you can do something. I don't think they are. > You can write Rust code with the same observable behavior as Duff's device in C. Yes, turing complete languages can do that.
- nine_k 5y agoThat C allows developers to play fast and loose is actually one of the reasons why C is slower than Fortran in many cases. Aliasing does not exist in Fortran, and array dimensions are well-defined, and this alone allows for much more aggressive optimizations, impossible to do safely in C. (Is Fortran still relevant? Try compiling Tensorflow, to say nothing of serious numerical modeling stuff. And yes, I know about __restrict; it's more of a band-aid.)
- therealcamino 5y ago> Aliasing does not exist in Fortran Ha, I have some disgusting use of COMMON blocks from the 80's to show you. But that doesn't affect the optimizer's ability to assume that there's no aliasing, so you're totally correct about that.
- EVa5I7bHFq9mnYK 5y agoThe version of Fortran I worked with had no stack nor heap. Needless to say, it was very fast.
- ekidd 5y ago> On the other hand, you may still get away with writing more performant C code since C allows the developer to play fast and loose with the rules. I have some thoughts on how Rust performance compares with C! I spent yesterday optimizing a sort routine in Rust. I need to sort records where the key length is known at compile-time, and the payload length is different every time sort is called. I expect the average sort call to involve at least 250 million rows and over 400 columns. Speed matters. Here's what I've learned so far, about how Rust versus C question: - Rust has generics with monomorphization. This makes it easy to compile two different versions of the sort routine to run on different length keys. - The very fastest version of my recursive base case, a 20-item insertion sort, currently uses unsafe code. This allows me to eliminate a couple of bounds checks that LLVM isn't eliminating automatically. I may figure out how to do this with safe code before shipping. (In 5+ years of production Rust, I've never needed unsafe for performance. This may be the first time!) - Performance often comes down to cache locality. Both C and Rust give me the fine-grained memory layout control that's essential. Also, this means that "detached key" sorts are almost always a bad choice, even when the alternative is repeatly moving large record payloads around. - The Rust "criterion" benchmarking library makes it super-easy to run valid performance tests. - The Rust "proptest" allows me to generate large amounts of test data and verify properties like "reckless_sort always produces output matching std sort." - "cargo fuzz" will be useful when I try to break the finished code. Seriously, it's a super-nice fuzzer workflow. - Rust's standard quicksort contains all sorts of funky optimizations: It detects semi-sorted arrays, it breaks patterns, it falls back to merge sort when things go wrong. It even marks "cold" paths for LLVM to improve instruction cache usage. - If I get my sort working, the "rayon" library will make it utterly painless and safe to split up the recursive calls over multiple CPUs. Overall, this has been a pretty fun experience. I'm not sure whether the finished product will use unsafe Rust in the inner loop. But the combination of generics, rayon, and tightly integrated test/bench/fuzz tooling has made this a very pleasant experience. Verdict: I am entirely happy using Rust for hot loops. (Even if my current fastest inner loop does use "unsafe" to bypass a few bounds checks I'm not clever enough to eliminate otherwise.)
- chii 5y ago> getting great performance is all the exposure to security vulnerabilities. ironic to post this right after java's biggest vuln to date (log4shell).
- winrid 5y agoWhich had nothing to do with Java or how it manages memory. You could have the same vuln in NodeJS or Python.
- ajross 5y agoWhich argues that maybe language choice just doesn't matter that much for security. It's true that C allows a class of mistakes that don't exist in higher level runtimes. It's equally true that this class of mistakes represents an increasingly vanishing share of real world exploits. Static analysis tools and runtime hardening techniques don't "fix" C, exactly. But in practice they work well enough to push C's foibles down into the noise floor. But at the same time, C remains, and will probably always remain, the easiest language on which to tune and optimize. It's not going anywhere. Our grandkids will still be using systems with C firmware at their core.
- School-Cotton 5y ago> It's equally true that this class of mistakes represents an increasingly vanishing share of real world exploits. Is this true? It seems implausible to me.
- friedman23 5y ago> Which argues that maybe language choice just doesn't matter that much for security No it doesn't. There is actually no logical way to infer from the parent statement that language choice doesn't matter for security. > But in practice they work well enough to push C's foibles down into the noise floor. This goes against findings from research that has been done into the sources of security vulnerabilities. Microsoft and the chrome dev teams have published that 70% of their security bugs are a result of memory safety. If C is used in 100 years it will only be because of inertia. Today there are better choices in the domain of low level systems programming languages.
- throwawaylinux 5y agoThere is a single memory error CVE there, the last one. Assuming a higher level language would not allow that without introducing any other security problems, and assuming the CVEs roughly represent the secureness of the software, then using a higher level language would be a pretty small marginal improvement in security.
- a-dub 5y agoit's kinda funny really. what you gain by eliminating memory bugs you lose in boundless complexity sprawl that is basically encouraged by the design of the language and its major libraries.
- throwaway894345 5y agoThe complexity isn’t new, it’s just moved from manually managing dependencies or reinventing wheels into more fruitful, higher level applications. Of course, some of these are frivolous, but probably no worse than spending one’s complexity budget wading through dependency hell.
- jmspring 5y agoC can be written in a way to minimize vulnerabilities. You use things like critical thinking and sanitize/validate inputs to functions and before you call libraries. The log4j vulnerabilities and others like it where input validation specific to what you expect would not happen in properly paranoid systems.
- adrianN 5y agoThere are approximately zero codebases of nontrivial size that are written in memory unsafe languages and free of serious security holes that would be impossible in safer languages. That is pretty good evidence that the measures you suggest are not very effective.
- oynqr 5y agoDoesn't OpenBSD have a decent track record?
- bear8642 5y agoStill approx. zero, but seL4 kernel's written in C and proven correct preventing many security holes memory control prevents
- iamtedd 5y agoseL4 was released as open-source in 2014. I know of the helicopter drone; have any other projects made use of seL4? If not, why not? My naive guess is that it's too hard to build on the project without introducing vulnerabilities and therefore negating the benefits of the kernel, but I'd love to be shown otherwise.
- erik_seaberg 5y agoWasn’t that generated from Haskell source rather than handwritten, after years of formal verification of not only the source but also the C code generator and the final machine code?
- 5y ago
- 1vuio0pswjnm7 5y agoThere are other factors. Not every application of C has the same amount of exposure to security vulnerabilities. On Github I see people rewriting simple UNIX utilties, e.g, cat, in some "safe" language, e.g., Rust. Yes, it is possible there could be some exploit based on an error in cat, maybe triggered by some malicious input that the user fails to detect, but this would not be a concern to the same degreee as errors in large, complex applications with network and other privileged access that are relatively new. For example, using C to write cat versus using C to write git. Exposure to security vulnerabilities depends not only on the language used, but also when, where and how the application is used.
- throwaway894345 5y agoSecurity is only one reason to rewrite utilities in another language. In particular for these foundational utilities, it means more of your dependency tree can be reproducibly built without the help of specialist package maintainers. Other reasons also include “fun” and “learning”.
- kovac 5y agoConsidering that package mgmt is a well solved problem, rewriting these tools for that in return for slow, bloated alternative sounds like a huge tradeoff to me. Fun and learning on the other hand, sure.
- adrianN 5y agoI'm not so sure that package management is really a well solved problem. Sure, there are some solutions, but as far as I know all leave some things to be desired.
- nineteen999 5y agoThe obvious solution, of course, is to write yet another package manager.
- 5y ago
- twoodfin 5y agoHave any of the relevant limitations been removed in the last decade+? I don’t believe so. Project Valhalla is 9 years on, is there an end in sight? When Java & the JVM were first released, the treatment of primitive vs. what we now call “boxed” types received a lot of criticism, mostly of an aesthetic variety. In retrospect, I wonder how much bigger the Java and JVM ecosystem would be if user-defined, non-reference types had been there from v1.0.
- therealdrag0 5y ago> how much bigger Isn’t it already crazy big? Not sure this was holding it back.
- twoodfin 5y agoThere are many niches where Java & the JVM are absent or fighting an uphill battle to occupy at all. Of those I’m aware of, the only ones where value types wouldn’t help fitness tremendously are on the client side, already a lost cause.
- kaba0 5y agoWhat would those niches be? Non-openjdk JVMs occupy even ultra-niche use cases like hard real time, military usage, embedded (though arguably Java ME is just a subset of Java, so you may or may not consider it the same), and the other direction of crazy performant server machines is well-served by OpenJDK — apparent in how is it used on a very very huge percentage of all web servers with top companies relying on it almost exclusively (apple’s servers are mostly Java, google has plenty of java applications, alibaba is a java webshop, twitter runs scala with graal, etc). Just an interesting note, OpenJDK’s GCs can handle heap sizes well into multiTERAbyte ranges.
- dralley 5y agoSomeone linked the thread from 2012 and even then a few of the concerns had been alleviated. However, hardware has also changed at lot since 2009, so it would be neat if someone did a new analysis.
- sally1620 5y agoIt is quite interesting that most of the problems mentioned don't exist in recent version of C# on .NET Core, considering all the similarities of C# and Java. I would even say, some of the problems didn't exist in C# in 2009. C# always had value types with configurable in memory layout. It also has a very good mmap solution. It also allows for hand optimize things using unsafe blocks.
- est31 5y ago> C# always had value types with configurable in memory layout. It also has a very good mmap solution. It also allows for hand optimize things using unsafe blocks. And C has inline assembly. Doesn't mean that most C code will use inline assembly. Back in 2009, a lot of git utilities were still written in scripting languages. Not sure when it started, but the porting activity of those utilities to C is still ongoing. So the maintainers still want to use a lower level language today. In other projects in the VCS space, we are seeing a similar trend. Hg, originally a project written in Python, is being rewritten in Rust by Facebook, one of the big users of it. Sure, maybe you could have used C# together with some niche features. But it's not going to be fun compared to a language that has zero cost abstractions and that runs on the bare metal. Even if your problem domain demands a managed environment, like extensibility with plugins, I still suggest you to use Rust together with wasm. It's the first choice thanks to its great type system, powerful static analyzer and first class support for resource management that garbage collected languages lack.
- ferdowsi 5y agoIs there a term for this phenomena yet? "if another language is being discussed, Rust must be forced into the discussion, no matter how tenuous the connection"
- MichaelBurge 5y agoThe old term for .NET/Java was "Managed" languages. "Managed C++", "C# is a managed language", because they all manage your memory for you. Rust's primary language feature - the borrow checker - is about adding compile-time checks on resource management(mainly memory), and the original article talks about boxed vs. value types being a major source of inefficiency. So talking about Rust in a comparison of C and Java mentioning memory indirection bottlenecks seems about the most relevant place to discuss it.
- MichaelBurge 5y agoCPUs today have more instructions-per-clock, so things like the extra "& 0xFF" might not affect performance at all. Similar for conversions to/from types: Those are compute on data already in cache/registers, so incur extra instructions but not extra memory fetches and might be free(depending on iop cache). Boxing is still bad, because it often means more uncached memory fetches. Container types that hold references instead of values are also still bad. I notice many higher-level languages are putting more emphasis on adding value-types and unboxed options recently, because memory indirection is a comparatively worse problem for them than it used to be. Memory is faster, caches are larger, so things that compute on bulk bytestream reads(initial loading of program code, reading large packed .git directories, etc.) should be more consistent across languages compared to 2009. Back then, it was easier for language runtime code(GC, JIT, etc.) to push data out of cache and trigger more memory fetches. I haven't benchmarked JGit or git, these are just my prior assumptions.
- hashmash 5y agoHotSpot usually optimizes the signed-to-unsigned conversion away. This is quite evident when looking at the generated machine code when accessing byte arrays. Although I still find it quite silly that the original post is even considering this an important point when the biggest bottleneck for git is the file system.
- bruce343434 5y ago> CPUs today have more instructions-per-clock, so things like the extra "& 0xFF" might not affect performance at all Huh? Yes it does affect performance. That could have been an actually useful instruction, instead we just do "& 0xFF" faster on modern hardware.
- dang 5y agoA thread from 2012: Why is C faster than Java: git vs JGit - https://news.ycombinator.com/item?id=3455673 https://news.ycombinator.com/item?id=3455673 - Jan 2012 (102 comments)
- silisili 5y agoI've had this argument a few times before. My company is full of javaheads and kept claiming that Java could be faster than C for some specific workloads. My argument has always been and still is...a language x written in language y cannot by definition be faster than language y. I understand that languages by themselves aren't fast, but it seems that point should be clear.
- foobarian 5y agoThe problem is that different languages do different things at runtime and this is part of the definition of the language. C has almost no runtime, while the JVM is allowed to recompile code at runtime using profiling data it has collected that very run. This is how it can beat C in certain workloads.
- silisili 5y agoBut...since Java was(maybe still is) written in C, one could literally write the Java runtime and do the exact same things...in C. I know it's a pedantic point, but one I stand by...that there's literally nothing Java can do that C cannot because C can write Java!
- bombcar 5y agoTheoretical max performance is different from actual performance - and since all code on a given machine ends up in machine code all languages are the same speed - which is obviously false.
- silisili 5y agoAgreed. I should have added to my initial post that I think language speed comparisons are absolutely stupid for the reasons I mentioned. I don't go around saying C is faster than Java. I just say Java cannot be faster than C.
- kovac 5y agoBut not all languages emit the same machine code. And I think performance is more sensitive to memory access than machine instructions, so I don't think this point is true.
- kuharich 5y agoPast comments: https://news.ycombinator.com/item?id=1026909 https://news.ycombinator.com/item?id=1026909 (Jan 2, 2010, 107 comments)
- jasfi 5y agoI'll stick with Nim for as long as I have the option. There's more than performance to consider. Nim has memory safety, but compiles to C.
- austincheney 5y agoIt looks like JavaScript is almost at performance parity with Java now: https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/javascript.html https://benchmarksgame-team.pages.debian.net/benchmarksgame/... That begs the immediate question: Why would anybody continue to hire Java developers? I was thinking about that recently and two things came to mind: * The JavaScript ecosystem is incredibly immature. You can deliver some amazingly supreme trash in this language and it will likely execute, most of the time. Nearly everybody is deathly afraid of the platform (the DOM) and requires the worlds largest frameworks merely to put text on a webpage, even though familiarity with the DOM is understanding just a few methods. * Most Java developers are trained by schools. After thinking through those two points I have to realize employers would rather burn money than train developers or pray and hope that with enough open source tools dependencies will compensate for the maturity gap.
- bitcharmer 5y agoI had a look at a couple of the Java benchmarks you've linked to and they are horribly written, completely disregarding stuff like OSR, forgetting about the warm up and ridden with absolutely unnecessary allocations. Either someone did this on purpose or they have no idea how to write efficient Java code and benchmark it.
- sgift 5y agoThe benchmark game is always full of these things. Some people are very motivated to make their language shine on it, so it has good[1] implementations, almost everything else is bad. Really, really bad. It's best to just ignore it. [1] With good defined as fast. Many of the implementations there are so contorted that no one in their right mind would use anything like it in production. But they are fast.
- johnisgood 5y agoYep. There were two implementations of whatever in Ada, but one of them failed to print the right output, so was disregarded in favor of the slower, but correct one. I fixed the implementation and now Ada is way, way faster. If I remember correctly I actually had the conversation with whoever is responsible for that website here on HN, and I posted the correct code here (via a link).
- arketyp 5y agoThere's a thrill to coding in C which is similar to using a great chef's knife. I'm no master with either, will probably hurt myself -- but the limitation is me and my own.
- hyperman1 5y agoMy opinion evolved over the years to the opposite of this, and I really loved C in the '90s. C is like the old sawing machines. Hospitals saw a never ending stream of cut off fingers, and every recent model has safety features. Old timers claim you are a responsible adult human and should just watch out, and technically, they are correct. They hate he safety features which indeed make their job harder. But it is humanly impossible to be 100% responsible at scale. You will have a bad day someday or be sick or whatever, and it will cost you a finger. In a team, 2 people will differ in what 'responsible' exactly means, and a dumb error will eat another finger and cause a lot of angry fingerpointing. C is the same. A group of people, at scale, can't write good enough software all the time. Dumb tiny errors will sneak trough, and C will punish us with instability or security holes. If we want to build any trust in future software, if our profession wants to be engineers instead of hackers, we will require some safety features in our tools. Even if this costs us short term in some extra development time or runtime inefficiency.
- lbriner 5y agoDefinitely agree and even more so today, these systems run everything, back when I started programming, most banks didn't even have online accounts so you mostly got to choose whether you used an app. Nowadays, you are using them whether you like it or not and the developer market, although still tight, is much more prolific now than before meaning there are plenty or average or not-so-good devs now compared to back when it was a niche thing for the cleverest (or most careful?) people.
- sshlocalhost 5y agoYeah I agree
- jillesvangurp 5y agoThe counter argument here is something like Lucene, which is written in Java but has been heavily optimized over the years using many of the same kinds of tricks the Git people used to optimize Git. There are frequent attempts to replicate parts of what Lucene does in different languages; usually under the assumption that it will be faster and better because of magical properties people associate with things like native compilation or the supposed manual memory management skills of the programmers doing the work. Most of these efforts are relatively niche compared to Lucene because they never quite catch up in terms of features, scale, etc. and because the programmers involved are messing around on the fringes of the problem space instead of coming up with algorithmic breakthroughs, which at this point is the quickest way to make things faster. Well, that and cutting some major corners and pretending it's all the same by running some silly benchmark. The fallacy here is confusing language, idioms, memory models, frameworks, etc. and assuming it's all set in stone. It isn't. Just because you are using Java does not mean everything has to be garbage collected, for example. Lucene actually uses memory mapped files, byte buffers, etc. for a lot of things. So, it does not actually need to do a lot of garbage collecting. It uses the same kinds of solutions you'd pick when using C. And they perform ballpark as you'd expect them too meaning that unless you improve the algorithms, you are not going to be magically a lot faster. The same is true of hotspot, the JVM's runtime compiler, which is written in C and, surprise, uses a lot of the same kind of trickery used by the fine people working on e.g. LLVM. So, a lot of things people assume must surely be slower just aren't and a lot of the insurmountable bottlenecks that people assume must surely be there always actually have well known ways of being worked around. Of course there is always room for more optimization. Lucene is well over two decades old now and they still regularly come up with major performance improvements. There's nothing magical about how they do that; just a lot of hard work that goes into it that would be somewhat challenging to improve on just by switching language and compilers.
- fkarg 5y agoAnyone knows if a git (partial) rewrite has been attempted in Rust yet? Might be worth a shot — should be similar performance with stronger guarantees, and last I checked git was a cesspool with bash and Perl mixed in all over the place.
- masklinn 5y ago> Anyone knows if a git (partial) rewrite has been attempted in Rust yet? There’s a project called gitoxide, though I don’t know whether its goal is to rewrite git itself.
- johnisgood 5y agoWhy not a formally verified git, then, using Ada/SPARK? :)
- hamilyon2 5y agoInteresting historical perspective. I heard that modern java addressed some of the shortcomings mentioned in the post: added type similar to c struct and unsigned types. Did JGit take advantage of this?
- jimis 5y agoJGit has advanced a lot, and is in use in software handling huge repositories like Gerrit code review system. Java has also had numerous advancements in the past 10 years. It would be interesting to measure git vs JGit performance today. It would also be interesting to have JGit developers comment on performance after all this time.
- marcodiego 5y agoHighest performing JVM's available are written in C. So, if you can get something to perform well in Java, you can do it in C too: just ship the Java code embedded in the JVM.
- kaba0 5y agoRead up on what a JIT compiler is. Also, OpenJDK is written in C++, not C.
- wheelerof4te 5y agoThe only two common sources of vulnerabilities in C are off-by-one and memory allocation bugs (former being a subtype of later). By defining a strict API for memory allocation and always double-checking your loops, you can almost eliminate these bugs. I will rather spend time to learn some programming discipline, than learn the monstrosity that is Java.