8 ms·
On comparing languages, C++ and Go
- tptacek 13y agoI've been following this whole business card raytracer story and wonder if people might be missing the forest for the trees. It would be a little nutty to suggest that Golang 1.1 is going to give optimized C code a run for its money. Nobody could seriously be suggesting that. What is surprising is that the naive expression of an "interesting" compute-bound program in both languages are as close as they are. Most C/C++ code --- the overwhelming majority, in fact --- is not especially performance sensitive. It often happens to have performance and memory footprint demands that exceed the capabilities of naive Python, but that fit squarely into the capabilities of naive C. The expectation of many C programmers, myself included, is that there'd still be marked difference between Go and C for this kind of code. But it appears that there may not be. This doesn't suggest that I'd want to try to fit Golang into a kernel module and write a driver with it, but it does further suggest that maybe I'd be a little silly to write my next "must be faster than Python" program in C.
- noelwelsh 13y agoRay-tracing, especially the simple kind in this example, is all about vector maths. CPUs are extremely good at this type of task. Finding Go performs well at this shouldn't be surprising. Any decent compiler will be able to produce good code for this task as it maps very closely to what CPUs do best, meaning you don't need much fancy analysis. I think the majority of languages in popular use are faster than Python. I believe that Go is popular with the Python / Ruby crowd because idiomatic Go is quite close to what they do already. I.e. you don't need to learn much to shift from Python or Ruby to Go. Using a language like Scala, for instance, is a much bigger jump.
- kid0m4n 13y agoActually, looking deeply at Go from a performance perspective (as I have been, the last couple of days) has revealed a bunch of low hanging fruits/missed optimization opportunities in the Go compiler. That was 50 % the intent of the grand father blog post
- packetslave 13y agoSure, there's plenty of work still to be done. Go 1.0 is only 18 months old, with 1.1 only 6 months old. C++ has 30 years of history behind it, MS VC++ is 20 years old, Intel's C++ compiler is at least 10 years old, etc.
- corresation 13y agoFinding Go performs well at this shouldn't be surprising. Go doesn't do SIMD at all (see note 1). Personally I leverage Go coupled with the Intel Compiler (Go happily links with and uses very high performance C-built libraries, where I'm rocking out with SSE3 / AVX / AVX2). To respond to something that Ptacek said above, many of us do expect Go to achieve C-level performance eventually. There is nothing stopping the Go compiler from using SIMD and automatic vectorization, it just doesn't yet. There is nothing about the language that prohibits it from a very high level of optimization, and indeed the language is generally sparse in a manner that allows for those optimizations. *1 - For performance critical code you are supposed to use gccgo, which uses the same intermediary as the C compiler, allowing it to do all of the vectorization and the like. Unfortunately for this specific code gccgo generates terrible code, yielding a runtime that is magnitudes slower (albeit absolutely tiny). Haven't looked into why that is.
- noelwelsh 13y agoMy understanding is that automatic vectorization is still quite sensitive to how code is written. The compiler may fail to vectorize one implementation of an algorithm, while vectorize another, due to details in the implementation of both the code and the compiler. My point is not about vectorization though. Code that uses mostly vectors, math, and function calls has a very direct translation to machine code. I expect all compilers to generate approximately the same machine code for this type of code, assuming vectorization doesn't come into play. So I don't expect to see large differences in performance. Of course there will be some difference, but not the order of magnitude one sees between compiled (statically or JITed) languages and interpreted languages.
- sampo 13y ago> assuming vectorization doesn't come into play. Now that 256 bit AVX registers that process 4 numbers in one go, even when one uses 64bit floats (and 8 with 32bit floats), vectorization more and more comes into play. Using 64bit floats with 128bit SSE registers, it was kinda possible to ignore the vectorization, as it was less than 2x speedup. But no more.
- 13y ago
- plorkyeran 13y ago> It would be a little nutty to suggest that Golang 1.1 is going to give optimized C code a run for its money. Nobody could seriously be suggesting that. AFAICT, the entire situation started only because the article was submitted to /r/Golang and /r/C++ with the trollbait title "Business Card Ray Tracer: Go faster than C++", and not because of anything in the article itself (which was actually a pretty good article).
- frozenport 13y agoI don't think its nutty at all. Ideally the language shouldn't be tied to performance - the compiler and optimizer will do it. His method of optimization hints that the compiler is still immature. From another angle, consider that in HPC FORTRAN codes will often get the best performance - and FORTRAN doesn't have real pointers.
- npalli 13y agoThat's interesting. I had the opposite reaction regarding Golang capabilities while following this saga. Not sure how 'idiomatic' the Golang code is, at first glance it just seems less expressive (more lines of code) than either c++ or java!. I didn't think that was possible. So whenever people talk about expressiveness of Golang it just seems like a design gone bad. The designers wanted a programming language with the expressiveness of python and the speed of C, they ended up with a language with the expressiveness of C and the speed of python.
- pbsd 13y agoI agree, I honestly don't see where the expressiveness claims of Go come from. I've always put it in the Java-like bin of languages (which is not necessarily a bad thing). I suppose the one thing that Go does well (compared to C++ or Java) is builtin concurrency and communication across tasks.
- RamiK 13y agoBrevity and expressiveness are not the same.
- yohanatan 13y agoAre you sure about that? Given that any [Turing-complete] language feature can be implemented in any other [Turing-complete] language, the only conceivable difference is in fact length of implementation. In other words, it is possible to express anything in one Turing-complete language that is possible to express in another.
- bluecalm 13y agoC++ code is 5x faster after some standard optimizations so it's not really the same league. Faster than Python for sure but still unusable for anything CPU bound is the message I get from all of this. This doesn't show anything new though, programming math/graphics is perfect use for C/C++ and you won't really benefit from anything Go has to offer and some of its features actually become annoyance for this kind of application. The biggest strength of of Go doesn't really really shine either as simple parallelism needed for ray tracer is matter of few lines of code in both C and C++.
- corresation 13y agoC++ code is 5x faster after some standard optimizations There is nothing standard about the optimizations -- direct AVX use is enormously uncommon, even among extremely high performance code.
- abelsson 13y agoI'll grant you that - AVX is pretty uncommon. I originally wrote it with SSE2 (there's a working version in the next to last commit on the github repo), but rewrote it using AVX because.. well, I hadn't used it before. But I wouldn't say writing media and signal processing inner loops using SIMD intrinsics is uncommon. The style of optimization I illustrate is pretty common, perhaps minus the AVX code path. Most widely used video/image processing, ray tracing and other compute bound libraries will probably be SIMD optimized in some fashion (probably with different code paths for different processors). You gain 1-8x performance, which is pretty significant. It's on the same order of magnitude speedup as threading your program. I have yet to see anyone truly and systematically trusting automatic vectorization, but perhaps there are libs out there I've missed. Anyone know of some?
- corresation 13y agoBut I wouldn't say writing media and signal processing inner loops using SIMD intrinsics is uncommon. But at that point this has nothing to do with Go or C++, and I find this whole discussion rather disingenuous (at first I thought you were detailing the maturity of C(++) compilers and their superior support of auto-vectorization, which would be a reasonable angle): You can import the Intel math libraries and call them from Go (I know, as I do it regularly. See my submissions).
- samth 13y agoThere have been languages in this space for a very long time. Pascal and Algol 68 are both very old examples (see the Go vs Brand X comparison). Then there's Modula or Oberon not quite as long ago. In the last 20 years, everything from Java to OCaml to Scala to C# to F# to Haskell to Common Lisp to Lua have been developed. So it's not especially interesting that Go is in this space as well. The most surprising thing about Go is that its developers seem never to have heard of any of the above languages (with the exception of Java).
- tptacek 13y agoI don't think it's interesting that Go has this combination of attributes. I think it's interesting that the "right" boring systems programming language got a degree of tooling and traction sufficient to make it, unlike Modula and Oberon, a viable mainstream choice. I am fine with boring languages. The first language love of my life is C. It's hard to get more boring than C. If a system I build is going to be clever or sophisticated, I'm fine with that being expressed in my code, rather than as the product of the environment I happen to be working in.
- pjmlp 13y agoOnly because it has Google as the Goodfather, otherwise no one would care about Go.
- Teckla 13y agoIt's hard to get more boring than C. C... boring? I don't think so. Not with 200+ undefined or implementation defined behaviors. Not when even something as simple as "a = b + c" can evoke nasal demons. C is so loosely defined that it keeps you on your toes constantly. Every single line is like walking down a dark corridor with poisonous snakes... and your torch just guttered out. There's nothing boring about C. It's pure thrill and danger. If you're not experiencing that, you're probably writing terribly non-portable, brittle code, ready to unexpectedly invoke undefined or implementation defined behavior.
- samth 13y agoFirst, Go isn't a systems programming language, any more than Java is. Second, C is _far_ from boring. It's boring now because we all live in its shadow, but the idea that we should have high-level languages for writing low-level software in? That's C, right there. Lots of other languages that now seem boring were interesting to start (Java, Perl, ...). What's frustrating about Go, and the hype it gets, is that it isn't interesting in any of those ways, and yet so many people, including its designers, seem to think it's revolutionary.
- icambron 13y ago> Personally, I’m hoping for Rust. That was my thought while reading article; Rust seems like the answer here. I'm coming from the opposite direction than the OP: I'm unwilling to give up the expressiveness of Ruby and friends in order to write micro-optimized C++ code, and I'm hoping Rust will give me the best of both worlds.
- evincarofautumn 13y agoYeah, everyone’s got their eye on Rust as far as low-level expressive programming goes. Nimrod[1] also looks quite good: > Nimrod is a statically typed, imperative programming language that tries to give the programmer ultimate power without compromises on runtime efficiency. In my spare time, I’m working on a statically typed concatenative language called Kitten[2] with similar goals. [1]: http://nimrod-code.org/ http://nimrod-code.org/ [2]: http://github.com/evincarofautumn/kitten http://github.com/evincarofautumn/kitten
- Dewie 13y agoDoes Kitten also try to not make (too many) compromises on runtime efficiency?
- evincarofautumn 13y agoIt’s very much a work in progress, but yes, as far as the language design goes. Right now there’s a horrifically inefficient AST-walking interpreter, essentially the simplest thing that can possibly work. That gives us integration tests, a standard library, and a REPL. We are working on the design and implementation of a native runtime as free time allows. The language looks and feels fairly high level, like a hybrid imperative-functional language. But in reality, it’s closer to (stack machine) assembly. There is a relatively straightforward mapping to x86 or ARM, so it’s easy to look at some code and mentally compile it at -O0 the way you might in C. Each Kitten instruction usually corresponds to one or two x86 instructions. Evaluation is strict, because excessive laziness can negatively impact predictability and performance, and you can get many of the benefits of laziness in different ways. All types have immutable value semantics; sharing is essentially an optimisation, and can be implemented with a tracing GC or a reference-counting one because the language is cycle-free. And function definitions are often extremely short, so they benefit from aggressive inlining. The general feeling is that we want absolutely as lightweight a runtime as possible—no type tagging, value boxing, runtime dispatch, reflection, garbage collection, &c. unless the programmer asks for them. At the same time, we want to avoid infecting code with low-level details such as pointers, lifetimes, and ownership. Correctness first, followed shortly by performance. Kitten is attacking the same problems as Rust and Nimrod, but from a very different direction. And of course we are only a small handful of people working in our spare time, and I write most of the code. It would be wonderful to have some more people on board.
- AYBABTME 13y agoI think all there's to get from these posts is that: Presumption: Writing Go code is more fun than C++ code. Demonstration: You can write performance Go code that's not too far from C++ code. Result: Cool, here's a more fun than C++ language I can use as a step down the complexity path when I need performance. Or like tptacek said.
- kybernetyk 13y agoFun is really very subjective.
- kid0m4n 13y agoOne correction: Presumption: Writing correct Go code is more fun that C++
- tinco 13y agoOfcourse, if you want your site to be up when it has 25 upvotes on hacker news, it's best not to worry if your web application is written in C++, Go or in Ruby (or sadly but likely, PHP), but that it doesn't actually have to spawn hundreds of processes and hundreds of connections and allocate hundreds of megs of ram to accomodate your visitors. So, please just use nginx to host some static html files for your blog, and fetch your discussion boards asynchronously..
- kid0m4n 13y agoYep. Octopress really helps with that
- corresation 13y agoSo, please just use nginx to host some static html files for your blog Wordpress + W3 Total Cache can handle a hundred HNs with ease. There is absolutely no reason to go to the past, and poorly configured blogs don't justify that Luddite argument.
- frozenport 13y agoLeave Britney alone! I don't see why people feel that C++ needs to be replaced, when I write C++ I have many levels of scope - and while dangerous it is not impossible and the empowerment makes me feel like a god. Programming is not incremental. If we spend all day writing a python back-end and when it doesn't give the performance numbers that day was a complete waste. When I think about C++ I know that a code written in C++ will take me 100% of the way - even if it takes longer to write.
- betterunix 13y ago"I don't see why people feel that C++ needs to be replaced" Here are some of my reasons: 1. It is impossible to write high-level code without dealing with (and often getting bogged-down by) low-level issues in C++. Why should I be forced to choose between different "smart" pointer types? Why should I be forced to decide how variables should be captured by a lexical closure? Sure, such decisions might make sense when you want to squeeze out a constant-factor improvement in performance, but they do nothing to help you get things done in the first place. 2. Error handling and recovery is needlessly and pointlessly complicated. You can throw exceptions, except for the places where you cannot, and once caught you there is not much you can do to fix the problem. It is so bad that the C++ standard library actually requires certain errors to not be reported at all. 3. Extending the language is impractical. Look at what it took just to add a simple feature, lexical closures, to the language: modifications to the compiler. At best C++ gives you operator overloading, but you do not even have the ability to define new operators. Lisp, Scala, and numerous other high-level languages give programmers the ability to add new syntax and new features to the language without having to rewrite the compiler. I am not familiar enough with Go to say that it addresses any of this, but I know why I stopped using C++ and why I have not regretted that decision. All the above make writing reliable code difficult. I actually switched away from C++ when I needed my code to scale better, because improving the scalability required a high-level approach and I did not have time to debug low-level problems. Even C++ gurus wind up having to deal with dangling pointers, buffer overflows, and other needless problems with their code -- that takes time and mental effort away from important things in most cases. "When I think about C++ I know that a code written in C++ will take me 100% of the way - even if it takes longer to write." The same is true of any programming language if the amount of time spent on the program is irrelevant. I am not sure what sort of work you do, but for what I have been working on, getting things done is considered higher-priority than squeezing out a constant factor improvement. Nobody complains about faster code, but everyone complains about late, buggy, and incomplete code.
- Jayschwa 13y agoIf you want to "poke at the processor" with Go, its toolchain makes it pretty easy to use assembly in your package.
- pbsd 13y agoGo uses an assembly syntax completely different from everyone else. That's mighty annoying.
- RamiK 13y agoHa? Doesn't it work through the 'import "C"' and then it's just regular C?
- pbsd 13y agoI meant something like this, i.e. not going through C at all: http://golang.org/src/pkg/syscall/asm_freebsd_amd64.s http://golang.org/src/pkg/syscall/asm_freebsd_amd64.s
- mseepgood 13y agoIt looks like AT&T assembly without the %s. Not really that weird.
- RamiK 13y agoOh. You mean the Plan 9 assembler... It's pretty good looking to me so I haven't really considered that an issue. I guess you're on the other side of the AT&T vs. Intel assembly fence... But just how much assembly are we writing this days anyhow? I say, just wrap it all up in Go (or C through cgo) and be done with it.
- corresation 13y agoYou can use C files and GCC syntax in your Go project. Go will use Cgo and compile it with GCC.
- _ak 13y ago
- zxcdw 13y agoThe article doesn't mention reasoning for using -O2 instead of -O3 or -Ofast. It seems that they give better performance in this case, as does adding -funroll-all-loops(when combined with -Ofast gives almost 10 % speedup). I am compiling with GCC 4.8.1, so perhaps authors 4.6.3 (what?) behaves differently.
- eonil 13y agoI don't think ray-tracer is a good example for Go. Ray-tracer workload can be fully split into each hardware core. Only input data needs to be shared, and even the input mostly doesn't make any race condition. The algorithm even can run on GPU. This is handicap for Go. Go wants to solve - safe and easy concurrency without race condition for complex logic. So (IMO,) Go needs to make some overhead (or sacrifice some performance feature) for its goal. But in ray-tracer example, this ability mostly not required.
- z3phyr 13y agoC++ cant be so easily replaced by Rust, even though rust seems to be a better language. The more possible case is that Rust's great ideas get ported into C++NewX standard. Even if Rust is to be our new systems labguage, we will have to wait for 12-15 years for it to actually happen.
- pjmlp 13y ago"On comparing languages, gcc vs 6g" Yet again the fallacy that comparing implementations equals to comparing languages. On the left corner the 6g compiler toolchain, which the authors admit that has yet to suffer lots of improvements in the optimizer. On the right corner, the battle tested gcc optimizer, with circa 30 years of investment, aided by language extensions not part of any ANSI/ISO standard, that are not language specific. Of course "C++" wins. People, just spend time learning about compiler design instead of posting such benchmarks. And before someone accuses me of Go fanboy, I think my complaints about Go's design are well known by some here.