10 ms·
Things that make Go fast
- HeroesGrave 12y agoThings that make Go fast* *compared to non-native languages like Python and Java. Could people please stop calling their favourite language fast just because it beats an interpreted/VM language.
- Dewie 12y agoIs Go even faster than Java at this point? Last I saw, it wasn't quite there. The only comparison with regards to Java was with the representation of integers, IIRC, which isn't really a native vs VM issue.
- NateDad 12y agoFor pure processing speed, Go is pretty comparable to Java for code that does not heavily rely on generics. http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?test=all&lang=go&lang2=java&data=u64q#faster-programs-measurements http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?t... What Dave's presentation mentions is the memory... note the huge difference in memory footprints of the programs. That's where java loses heavily, and that can affect speed as well, which was his point.
- ssmoot 12y ago> for code that does not heavily rely on generics Does adding Generics heavy code move the needle in Java's favor? As a Scala developer your comment just jumped out at me because even doing a lot of Akka this past week, there's hardly a line of code that doesn't instantiate or manipulate a Generic. Just curious. I'm sure Go will get faster. I'm sure it's better suited to certain problems (memory constrained especially).
- seanmcdirmid 12y agoNo. If you don't use generics in Java, you get the same memory footprint of not using generics (or since it doesn't have type parameters, using any) in Go. So the author is making an unfair apples-oranges comparison when the apples-apples comparison is quite obvious: one can write an int-list in Java just as easily as they can in Go. Frankly, this is just strong intellectual dishonesty.
- stcredzero 12y agoFrankly, this is just strong intellectual dishonesty. If you're going to harp on rigor, then you need to consider then eliminate the prospect of an honest mistake.
- seanmcdirmid 12y agoIntellectual dishonesty doesn't mean lying, it could just mean "honest mistakes" that fail to apply proper reasoning and comparisons.
- stcredzero 12y agoRight now, you seem to be making an "honest mistake" with my referents. You seem (atypically) weirdly consistent with such honest mistakes in these threads. What is the lie, exactly?
- pjmlp 12y agoNot only that. Usually these comparisons cleverly leave out AOT compilers for the said languages to make theirs look better. In Java's case there are quite a few JVMs, many of those with AOT compilation to choose from, even implemented in Java itself.
- marktangotango 12y agoFor Java can you name any AOT compilers besides Excelsior JET and GCJ?
- pjmlp 12y agoGCJ is dead. Yes, CodenameOne, JamaicaVM, Aonix Perc and J9 all support AOT compilation besides normal JIT. The Oracle Hotspot replacement project, Graal allows for AOT compilation via SubstrateVM. There there is RoboVM for targeting iOS applications, with WP support getting added now. Android is replacing Dalvik with ART, which does AOT compilation at installation time. Probably a few more that I am not aware.
- marktangotango 12y agoThanks for the info, interesting the first four you mention are commercial products. Two you may find interesting: avian vm, and xml vm at one point could translate jvm bytecode to c for compilation with gcc.
- 4ad 12y agoNo, compared to nothing. Go is fast (or at least that's what Go programmers feel when they are using it). If they want to know why, they can read this article. This article is not Go vs. Java vs. C++ advocacy, don't try to make it looks like it is. It makes no claim Go employs better optimization technology than C++.
- Alupis 12y agoThe comparison between GO and Java seems unfair, given they compare a primitive variable with an object... which has methods and a bunch of other things to increase it's size (for good reason). Sure GO may be quick... but a JIT'ed java program will run at native C speed... because it's been compiled down to native code at that point... (and most language performance comparison's I've seen pop up generally ignore this fact and measure "performance" by timing runtime which includes the JVM firing up and executing cold/non-jit'ed code... not real-world scenarios for high performance code.)
- vanderZwan 12y ago> The comparison between GO and Java seems unfair, given they compare a primitive variable with an object... which has methods and a bunch of other things to increase it's size (for good reason). Honest question: will it be JIT'ed to the point where you no longer need 24 bytes to store an Integer in a List on a 64 bit JVM? Because if not, that comparison doesn't strike me as unfair, given that he explicitly mentions memory bound situations. Furthermore, will an array of Integers be a tightly packed set of integers, or value references to Integers? In general Go's equivalent to objects are structs, which seem to avoid this overhead of Java objects. Again, honest question; if Java can JIT all that away, awesome! :) For the record, the people on the Go-nuts board are pretty adamant about microbenchmarks not being all that representative, and that people should give the JVM some time to do its optimisation (Robert Griesemer was one of the original programmers on the HotSpot compiler after all).
- pron 12y agoWell, the JDK's ArrayList, for example, will box ints into Integers, but there are primitive int lists that won't. With Go, AFAIK, the problem is exactly the same. Can you write, say, a heap or a read-black tree that can store wither ints and strings, that will embed the ints? Also, value types are making it into the JVM in Java 9. I don't think that will solve the packed representation in a generic data structure problem, but neither does Go. OTOH, Java does optimizations that Go never can. For example, Java can inline interface calls, which Go can't (and due to the way Go defines interfaces, method calls are slower than Java even when not inlined in either). Having said that, there are things about Go that I like better than Java: 1) no implicit primitive promotions (widening conversions), 2) a small standalone executable, 3) excellent start up time. The latter two make Go suitable for quick, small programs, like command line tools etc. However, there are more things I like about Java the Go doesn't have: 1) dynamic linking, 2) final variables, 3) concurrent data structures, 4) awesome monitoring and profiling tools, 5) generics, 6) state-of-the-art GC, 7) a polyglot VM (I also prefer Java's exceptions and explicit interface implementation, but these are minor considerations). So whenever I need serious, long-running, server-side software, I'd stick with Java over Go. Particularly now that Java has fibers (goroutines)[1] (I'm the main author). [1]: https://github.com/puniverse/quasar https://github.com/puniverse/quasar
- astrange 12y agoFunction calls aren't that slow in an OoO processor - they're perfectly predictable branches, so it can just start decoding from over there. There might be a cache miss, but there might also be fewer cache misses, or even better the CPU might skip decoding with a µop cache. Really, the purpose of inlining is so inline functions can be specialized for their new context, which can easily make the total code size smaller. On x86, size/speed tradeoffs just don't happen like they used to.
- gsg 12y agoThat's not the whole story. There are other costs associated with calls such as spilling and imprecision of data flow analyses around a call site.
- hendzen 12y agoEscape analysis, dead code elimination, and function inlining are standard optimizations taught in an undergraduate compilers course. Go is cool, but I wouldn't really cite those as justifications for why.
- Nitramp 12y agoYes for dead code elimination and function inlining, not so sure about escape analysis. The author acknowledges that, but there's a detail in Go: it does the function inlining at compile time (unlike e.g. Java JITs), but still manages to inline across compilation units (unlike C++, modulo LTO). That's nice, and presumably what he wanted to point out. It's also nice that in Go, these things are very straight forward due to the overall simplicity of the system (unlike C++). The dead code elimination is just a supporting fact for why that's useful, and again works across compilation boundaries. I'm not sure about your assertion of escape analysis, at least Java JITs only learned that trick as of lately, and are still pretty bad at it. C++ again suffers from cross-compilation unit visibility; even if your LTO can detect an inlineable call, its AFAIK not possible at that time to move heap allocations to the stack. This is an interesting pattern in Go, the longer one looks at it, the more you understand that it's a whole bunch of good decisions in various subsystems coming together.
- seanmcdirmid 12y ago> at least Java JITs only learned that trick as of lately, and are still pretty bad at it. Did the author claim that Go was faster than Java? As far as I can tell, the JITs are still kicking the Go compiler's butt on "effectiveness." > C++ again suffers from cross-compilation unit visibility; even if your LTO can detect an inlineable call, its AFAIK not possible at that time to move heap allocations to the stack. Is the author really trying to explain why Go is faster than C++? > This is an interesting pattern in Go, the longer one looks at it, the more you understand that it's a whole bunch of good decisions in various subsystems coming together. One cannot validate these patterns yet because Go is still slower than the languages it supposedly innovates on in performance techniques.
- 12y ago
- robryk 12y agoNitpick: goroutine context switch can also happen at function calls (when the stack is being enlarged).
- Rapzid 12y agoI guess taking advantage of that could be tricky. If your function gets inlined...D'oh!
- skj 12y agoIf your function is inlined (which happens at compile time), then there won't be any stack growth and the point is moot.
- Rapzid 12y agoThis is a form of pre-emptive scheduling. It doesn't happen when the stack size needs to increase, it causes the stack check to fail. A bit of classic Dmitry cleverness: https://docs.google.com/document/d/1ETuA2IOmnaQ4j81AtTGT40Y4_Jr6_IDASEKg0t0dBR8/edit https://docs.google.com/document/d/1ETuA2IOmnaQ4j81AtTGT40Y4... http://golang.org/doc/go1.2#preemption http://golang.org/doc/go1.2#preemption Anyway, I was just offering this scenario up as a bit of curious humour where somebody might think they are providing an escape hatch but the compiler in-lines their call foiling their plans :)
- stcredzero 12y agoCurious humor == Classic too clever by half.
- azam3d 12y agoGo should replace Java in Android Development
- pling 12y agoGo should replace Dalvik and the half arsed Java runtime implementation on Android yes, but I'd take a proper mature JVM over both on any device.
- pjmlp 12y agoI am looking forward to the Google IO presentation about ART. Looking at the official languages both in the iOS and WP 8.x SDKs, Google should at least give first class support to all major JVM languages.
- buster 12y agoRust and python should, for speed, GC-lessness, memory-efficiency and ease of programming.
- pjmlp 12y agoNot likely to happen. - The ticket requesting support is open since 2012; - Android team is very Java biased and see the NDK as something that was imposed on them; - The Go team is still discussing how to add dynamic loading support to make it work in Android
- Artemis2 12y agoUnfortunately, Go's compiler is not as fast as it could be; most of the optimizations presented here were already made by compilers in the 80s. The fact that modern compilers are a really complex piece of software that took dozens of years to write and improve to the state we are at doesn't helps. Hopefully, switching to a compiler written in pure Go in Go1.4 (IIRC) will allow code maintainers to benefit of Go's simplicity.
- zwieback 12y agoI was surprised to see stack-check preambles mentioned here. Does that really happen on every function call? Or does it happen on a context switch? Usually stack-checking on function entry is considered something that makes code slow.
- 4ad 12y agoYes, it happens on every function call. It costs 3 machine instructions. That is nothing. There is no other "context-switch" other than the one triggered by this check (and other similar mechanisms), Go is cooperatively scheduled; all preemption is voluntary.
- zwieback 12y agoWow, what can you do in three instructions and what happens when the stack check fails? Sounds intriguing, think I'll read up on that...
- nwmcsween 12y agoIf the stack check fails it has to 'allocate' a new stack and copy over the old one and point to the new stack?
- 4ad 12y agoLet's take a look at Linux. Other systems are similar. ; go tool objdump -s main.main a TEXT main.main(SB) /private/tmp/a/a.go a.go:9 0x400c10 64488b0c25f0ffffff FS MOVQ FS:0xfffffff0, CX a.go:9 0x400c19 483b21 CMPQ 0(CX), SP a.go:9 0x400c1c 7707 JA 0x400c25 a.go:9 0x400c1e e8ddf90100 CALL runtime.morestack00_noctxt(SB) a.go:9 0x400c23 ebeb JMP main.main(SB) a.go:10 0x400c25 e8d6ffffff CALL main.foo(SB) a.go:11 0x400c2a c3 RET a.go:11 0x400c2b 0000 ADDL AL, 0(AX) a.go:11 0x400c2d 0000 ADDL AL, 0(AX) a.go:11 0x400c2f 00 ? On linux/amd64 we can use the Local Executables TLS access procedure. In particular, we use a negative offset from the FS segment register to get a TLS slot (our job is simpler because we are always the main executable). MOVQ FS:0xfffffff0, CX We make use of two TLS variables, g and m (soon we will only use one), a pointer to g is at -16(FS). We access it in this first instruction. g is an instance of struct G, see go/src/pkg/runtime/runtime.h:/struct.G. It contains many things, but it starts like this: struct G { uintptr stackguard0; uintptr stackbase; ... In particular the first word (at offset zero) is the stackguard, which indicates the stack limit (it is also used for voluntary preemption, but that doesn't matter here). This instruction in the stack check preamble: CMPQ 0(CX), SP Compares the current stack pointer with the stackguard. In most cases we have enough stack, so the next instruction just skips past the preamble to the real function code. JA 0x400c25 When we don't have enough stack, we call a function in the runtime (one of the runtime.morestack functions). This function allocates a new stack segment (from the heap). Currently we use contiguous stacks, so if we have complete type information in the current stack we can just copy the old stack to the new stack segment fixing any pointers as dictated by the type information, and then we switch the stack pointer. If we don't have enough type information (or in previous Go versions), we use segmented stacks. We allocate a new stack segment, but we don't copy the stack; we just switch the stack pointer and we take care to be able to do the reverse operation when we return from the function. Take a look at the next instruction after the call to runtime.morestack. JMP main.main(SB) We just jump to the beggining of the function like nothing has happened. Then the algorithm repeats, but we won't fails the stack limit check again, so it will skip it. Why it jumps to the begining of the function instead of just continuing in the body of the function is left as an exercise to the reader. We used the Local Executables TLS access model here, sometimes we have to use the Initial Executable model. If we ever allow Go programs to be loaded by C programs as dynamic objects, we would have to use more complicated models. On ARM we just use a register instead of using any form of TLS. On most systems Go binaries set the FSbase register to some value on the heap, but when we use cgo, or on platforms that don't support static binaries we don't touch FSbase, as it was already set up by libc. Functions that use little stack (under 120 bytes) can be excepted from this stack check.
- fiatmoney 12y agoHey, as long as we're talking about Go performance - can we please, please get some kind of wide vector intrinsics (ie, no cgo overhead) in a library, or at least aggressive compiler generation of vector ops that actually use AVX & the ARM NEON equivalent? Right now peak floating-point performance isn't even within half of what it should be on a very recent CPU, and I'd love to be able to deploy Go that exposes machine learning models to a network interface.