The State of Go: Where we are in February 2016
talks.golang.org
talks.golang.org
I remember a thread discussing a pauseless GC on the dev mailing-list; where Gil Tene, of Azul C4's fame, commented on having a clear policy on safepoints from the beginning was paramount to having a good GC. It looked like the community is very biased towards doing the fundamental things well, and attracting the right people.
And on top of that we're get BLAS and LAPACK bindings from https://github.com/gonum
The first thing to be aware of is that with modern collectors (I have no idea how modern Go's new collector is though), GC pause time depends on how much live data there is in the young generation. So you can easily have enormous heaps with very low pause times if all your objects die young and hardly ever make it into the old generations, because then you never really need to collect the rest of the heap at all.
Of course, outside of synthetic benchmarks, many apps don't show such pleasant behaviour, and often GC algorithms face difficult tradeoffs that are only really knowable by the developers. For instance, do you want low pause times, or less CPU used by the collector (higher throughput)? It turns out that's a fundamental tradeoff and the right answer usually depends whether your app is a user facing server (needs low pause times) or a batch job (better to pause for long periods but complete faster). No runtime can know that, which is why the JVM has tons of tuning knobs. Left to its own devices you can theoretically get away with only tweaking a single knob which is target pause time (in G1). Set it lower and CPU usage of the collector goes up but it'll try and pause for less time. Set it higher and the collector gets more efficient.
Or you can just buy Zing and get rid of GC pauses entirely.
So a stat by itself like "20ms GC pauses on 200GB heaps" doesn't mean much by itself. You can get very low pause times with huge heaps out of the JVM as well:
http://www.slideshare.net/HBaseCon/dev-session-7-49202969
but of course, you have to pay the piper somehow ... assuming non-weird heap usage the program will run slower overall.Also, I suspect that the fact that JVMs use a generational GC (and a compacting GC) blows everything else out of the water when it comes to fragmentation. There's no way a best-fit malloc implementation can possibly beat bump allocation in the nursery for low fragmentation.
Of course that isn't always what you want (e.g. desktop apps) ... sometimes you'd rather spend the CPU and minimise the heap size. The latest Java versions on some platforms will keep track of total free system RAM and if some other program is allocating memory quickly, it'll GC harder to reduce its own usage and give back memory to the OS.
In the benchmarks game I suspect there aren't any other programs running at the same time, so Java will go ahead and use all the RAM it can get. Measuring it therefore won't give reasonable results as the heap will be full of garbage.
Value types don't have much to do with fragmentation, if anything they make it worse because embedding a value type into a larger container type results in needing larger allocations that are harder to satisfy when fragmentation gets serious. But ultimately a similar amount of data is going to end up in the heap no matter what. Yes, you can save some pointers and some object headers, so it'll be a bit less. But not so much that it solves fragmentation.
> Changes to the language: None
https://talks.golang.org/2016/state-of-go.slide#6
We generally get our entire stack upgraded to the latest release within 1-3 months with little effort. It wouldn't be much more work to be ready to upgrade on release day, but we haven't found a reason to worry about it.
Go is such a breath of fresh air compared to past Java and Python jobs where production was usually at least a major release version behind the latest and there was extra effort spent getting everyone using the same implementation (Oracle Java v OpenJDK or Ubuntu's Python v CentOS's -- there are differences!).
Conservative releases aren't a must-have for a language, but I do appreciate having one less operational headache to consider.
Python has been around since the early 90s, while Go was conceived very recently. Sooner or later, the quirks that resulted from designing the language so long ago had to be addressed, and that's why there's Python 3. The same can be seen in Java and Ruby.
Once Go is used actively for ~9 years (Python 2.0 to 3.0) and suffers no changes that break backwards compatibility, we can compare it with Python.
* when "enum" keyword was added, if you had a variable named enum you had to rename it.
* If you use any of the sun.* packages you always ask for trouble on major upgrades.
* GC changes over the years can change how your program runs (latency changes, OOM issues). This probably applies to Go as well.
So while your awesome CMS tweaks from JDK7 become worthless once you switch to G1 on JDK8, with Go all you can do to optimize the GC is to create less garbage to begin with.
Not trying to say one approach is better than the other: Go's approach is operationally simpler but far less sophisticated than the JVM's.
This isn't actually true, Go has a single tunable: "GOGC" https://golang.org/pkg/runtime/
Using sun.* packages or relying in GC behaviour is a way to make Java code not portable across certified JVMs.
For example I took part in some projects that were married to IBM JVM, because they were relying on its features.
You don't really have a choice in the matter. If you're writing high throughput or low latency applications you are dependent on the JVM's GC behavior, period.
Just as a very basic example, a certified JVM 8 is not required to have G1.
As for high throughput or low latency applications, yeah actually one is dependent on the whole stack, hence why HPFT is already moving into FPGAs.
IIRC, that caution about using sun.* packages was mentioned by Sun in docs of early Java versions, like 1.2 / 1.4 etc. Not sure about more recent ones.
So that means you either run different JDKs for different services or you stick with the lowest common denominator.
* In order to upgrade the JVM, all the software you run has to work with the new JVM. If there's even one minor library that doesn't work or is suboptimal, you can't cut over. This is not an issue with Go because everything is compiled to an x86_64 binary there, whereas in Java everything is a jar file that must be run under (almost always a single) JVM.
* During a JVM version change, often Oracle removes or modifies non-public APIs that you need to achieve acceptable performance. For example, there is still no public way to free a DirectByteBuffer or create a FileChannel from a FileDescriptor, so you have to use the non-public APIs. Go almost never has this sort of problem since they tend to provide public APIs for everything you need, including platform-specific things.
* We run really big JVM heaps (>100 GB), and so minor changes in the GC behavior or default settings can cause major issues. For example, JDK8 changed the defaults for many GC tunables. It takes weeks of work at least for us to validate that there are no significant regressions. This is an issue that I would expect Go to have as well since they are changing the GC.
* Enterprise customers are extremely risk-averse, and they're not enthusiastic about deploying a new JVM. Operationally, they don't see any upside, only downsides. Of course JVM upgrades have to happen eventually, but they usually happen when new software is rolling out as well. Oracle's decision to stop shipping security updates for older JVM versions has "helped" in a sense by making the issue seem more urgent.
* Open source projects don't like dropping support for users running older software. There is usually someone around to argue against dropping support for anything that rolled out within the last 5 years.
The private APIs thing is indeed one of the most common issues, along with needed upgrades to bytecode rewriting libraries.
I think both Go and Java have been good about avoiding backwards incompatible changes in the standard library. Both languages have an explicit policy of avoiding these whenever it is at all possible.
Frankly, the Go standard library is a lot better written than the Java one. For example, I dare you to figure out how to call statvfs from Java, or figure out how many hardlinks there are to a specific file inode. Or make an asynchronous DNS lookup. Even simple things like creating a socket without doing a DNS lookup are very difficult to achieve in the Java standard library.
However we also have projects where the Java version is married to whatever the Websphere deployment of the day supports.
And on Android, well there is no upgrade at all. Which is yet another reason to use the NDK, even with all the 3rd class developer treatment, at least the C++ compiler gets updated and doesn't depend on the Android version of the target devices.
I suspect this really boils down to operational complexities of Linux distros, not Java. If your package manager only lets you install one JVM then maybe this seems "complicated" relative to Go, but that's not a Java problem.
WRT the standard library, yes the Java standard library doesn't expose UNIX specific syscalls. It exposes stuff at a higher level instead because it tries to be portable. That's a different tradeoff to what Go makes but I wouldn't say that makes it badly written. To me badly written would mean buggy, confusingly designed, too small or too big etc. If you want to write non-portable software then that means you may have to link in an extra library or so (like JNA).
Creating sockets does not do DNS lookups in Java. You may be thinking of the URL class, which does, and there's a URI class that avoids that.
The desire for a single JVM comes partly from the architecture of Hadoop itself. Hadoop is structured as a framework (you give your MR job to YARN and it runs it by creating new JVMs for you).
The Java standard library is weak in many areas. The "write once, run anywhere" ideology is part of it, but there are also just... weak parts.
I don't think the situation is all that different. All the Java code ends up being binary as well, at least after running a while.
At the moment, I think most people's Go projects simply have fewer dependencies. If you poke about on some of the popular Go projects, you'll see some of the bad coding practices that will result in upgrade breakages like checking for exact error strings (unfortunately needed sometimes, I know), so I foresee the same issues over time.
Considering the nature of the work, the programming field seems to be particularly rife with "common wisdom" that's not supported by data. This especially goes towards language design. (In the Chris Granger talk that was recently posted here, he noted that programmers who said they "never" used the mouse, only the keyboard, actually used the mouse 50% of the time.)
The reality of the programming field is that it's "almost a field" [1] -- struggling just as much with empiricism as alchemy was before it evolved into the science of chemistry. Language features definitely suffer from the irrationality of the almost-a-field of programming.
Golang seems to be led by good empiricists who are targeting a specific set of use cases for programming in the large.
[1] - https://news.ycombinator.com/item?id=9812487Oh, and I've already made use of the whitespace-stripping in text templates, too! :)
One thing I was worried about from the focus on reducing maximum GC pause times (i.e. latency) was that this might negatively affect GC throughput. For example, maybe the pauses are shorter but there are many more of them. The project I'm working on at the moment exercises the GC heavily but is not concerned with latency (it's bulk data-processing), and I didn't see any significant regression in performance/throughput from 1.5 to 1.6rc1. So, yay.
And hi by the way ;)
EDIT: I realise this is a vague question. I suppose I was wondering if the order of magnitude GC performance in Go is likely to interfere with game loops you might find in reasonably CPU/ GPU intensive Indie games (i.e. NOT Crysis).
So always take the JIT/GC complains in Unity3D context with that caveat in mind.
On one side they did a great job increasing the visibility of C# among game developers, which tend to only switch languages when the OS and console SDKs push them to do so.
On the other hand, they spread the feeling that C# is bad for game development among developers that don't understand "language != implementation" and take their Unity's experience as how C# implementations performs in general.
However I also should say that they are aware of it and planing to improve the situation after their IL2CPP compiler stabilizes.
Apparently younger generations are unaware that C was seen as a managed language in the 80's and early 90's, with compilers not generating good enough code for game development.
In the 90's I have seen lots of Turbo Pascal and C code where the functions where plain wrappers for inline Assembly.
So unless you intend to write Crysis in Go, there are lots of games you can write with it.
It's a voxel game based on OpenGL. It implements much of vanilla minecraft. Annecdotally, I'd say it performs much better for me than minecraft itself does, at longer view distances, and with essentially no observable GC pauses (of which minecraft suffers quite visibly). Also, a stabler heap size, etc.
The capabilities are definitely there.
I'm spending some of my weekend moments playing around with more GL stuff based on what I've learned from reading this project, and it's quite fun. Build times are right up there where you'd expect, too -- seconds or less! (The first build takes a few moments for running gcc for the c bindings to GL, but after that, those cache nicely.) A 1-second turnaround for recompiling a whole game is an incredible breath of fresh air.
And of course, I shipped my demo game to a friend on a mac the same day I started writing. I'm on a linux. Not bad.
Given that a Go 1.6 GC will still take about 4ms, you have 12.7ms to generate a frame, which can be too limiting for some CPU-intensive games, but is perfectly acceptable for many games.
(In Go 1.5, a GC was much more likely to make you drop a frame, as it could easily average 40ms.)
On the other hand, there are less high-quality libraries in Go than in C++ or in JS. That may be the most limiting factor.
Their secret is they keep the game state heap really small. Like, 50 megabytes, maybe.
One approach is just to program as if you had less CPU, as if there were always a GC running. I suppose if you have some code that isn't smoothness critical (game AI, say), or CPU-affecting detail settings you can twiddle without looking too glitchy, maybe you can figure out some way to shed work when you start to fall behind.
It'd be really cool to see someone attempt a game or such in Go--boundary pushing's always fun, more so when the boundaries are recently expanded.
No sane game developer would use anything other than Assembly.
The more things change, the more they stay the same.
"It'd be really cool" was absolutely sincere; the Go folks have given us some new toys and it'd be neat to see how far we can take 'em.
The culture in the gamedev world is such that the big teams only switch tooling when forced to do so. Only amateurs tend to try out new ways.
Back when the move from Assembly to higher level languages started, many games would be filled with inline Assembly.
The compilers weren't that good generating code and they didn't want to loose the power of Assembly.
Just like now with the managed runtimes and having tons of C and C++ underneath. Languages that weren't that speedy 30 years ago.
We need to keep the spirit of taking things far alive, because in computing seeing is believing.
Whether you can write the whole game in it - I don't know, but it'll be pretty good for tools/editors/pipeline/etc.
If KSP works in C#, you can write a game in Go no problem.
Unless you're asserting that e.g. Python is not interpreted; or you're using the relatively recent native toolchain, C# is interpreted. That's its original state and its widest deployment pattern.
It is true that .NET programs are traditionally distributed as CLR bytecode. This is similar to a .class file or a .pyc file. But the CLR does now, and always has, had a JIT. E.g., the first line or two of https://msdn.microsoft.com/en-us/library/ht8ecch6(v=vs.71).a... , which is for .NET 1.1, which explicitly states that, even at that time, the bytecode is JITed prior to execution.
No and no.
> But the CLR does now, and always has, had a JIT.
And Python has had a JIT[0] for as long as the framework has existed.
[0] https://en.wikipedia.org/wiki/Psyco now replaced by the pypy project
- JIT and AOT compilation via NGEN up to .NET 4.5.2
- Starting with .NET 4.6, RyuJIT which uses the Visual C++ backend and exposes SIMD support to .NET languages
- When targeting Windows 8 and 8.1, AOT compilation to native code in a format called MDIL. Basically requires dynamic linking on device, everything else will be native code already
- When targeting Window 10 store applications onwards, AOT compilation to static executables
- .NET Compact Framework also always JITs
- .NET Micro Framework is the only one that does interpret MSIL
Also Microsoft .NET JIT compilers, with the exception of the .NET Micro Framework always jit the code, there is no threshold to trigger it like on most JVMs.
Python is interpreted in what was its most popular form, but may not be now, CPython.
This is using the definition of a JIT as an execution engine which takes in some form of bytecode and, at runtime, emits architecture-specific assembly code to a page, marks that page executable, and changes the IP to that page.
If CPython executes in that manner then I'm mistaken about CPython's execution engine and would also consider CPython to be a JIT.
Found a blog post by Joel Webber, who I have not heard of, but he was working on a minecraft clone and had some advice on avoiding GC and memory layout:
http://www.j15r.com/blog/2015/01/25/Game_Development_in_Go
Also found an engine, Azul 3D, and they made this claim:
https://azul3d.org/doc/faq.html#what-about-the-garbage-colle...
Then there is termloop, which is a terminal based engine:
https://github.com/JoelOtter/termloop
Fun for indie game stuff.
So, yeah. There's that.
To me adding the - to the template tag {{foo -}} to get rid of whitespace on that side of the tag is totally unintuitive and a really kludgy solution. Sure, it's terse and being terse can be nice, but terseness to me probably doesn't even make it into my top 10 concerns when designing a language or library.
In my mind a lot more thought and consideration should be put into solutions for problems that are going into core libraries. Stuffing cute hacks into the core libraries willy nilly leads you to PHP. The consequences of which I deal with daily.
> Sure, it's terse and being terse can be nice, but terseness to me probably doesn't even make it into my top 10 concerns when designing a language or library.
The whole point of this feature is to be terse, otherwise you could already use template comment to trim formatting whitespace.
[0] http://jinja.pocoo.org/docs/dev/templates/#whitespace-contro...
cf http://www.perl.com/pub/2001/01/tt2.html
> If these tags are replaced with [%- -%] then the preceding or following linefeed is suppressed.
I get that. I don't think you understood my objection, which was that being terse is not as important to me as being elegant and easily understood. {{foo -}} is not clear looking at it what that - is gonna do. You have to already know, or look it up. There is basically no way to know from context what the desired behavior is. That's bad design IMHO.
Once you use the Go(/Jinja/Perl) syntax once or twice, then putting a minus sign to remove whitespace will be easily understood.
I am interested in an alternative that you find more elegant.
Why not make it a filter/pipeline (I haven't used template/text in ages, does it support this?), like in Django {{ thing|trimleft }}.
So "{{ foo -}} bar" renders the same as "{{ foo }}bar".
Bikeshedding about text/template really is just bikeshedding about text/template moreso than Go qua Go.
If you compare a slice of structs with 1000 elements, it'll be one object (and allocation) in golang. Equivalent array in JVM requires the array itself + 1000 Objects, 1001 allocations. In this case, golang has lot less object graph to gc.
Of course slice of 1000 interfaces or pointers faces the same 1001 issue in golang as well.
You could emulate same gc load cost in JVM at cost of runtime performance by storing the objects in a byte array and [de]serialize as needed, but that's neither idiomatic or acceptable solution most of the time.
In the meantime, Azul and IBM JVMs JITs are able to optimize "value types" if the classes follow certain patterns or with some annotation help.
From the QCon talk linked to in the slides it sounds like the Go GC is benefiting from the reduced number of objects being allocated and the fact that those objects can never move. Makes things a lot simpler if you can get away with it, but I can imagine a reference into an array, and the inability to move objects could combine in bad ways if you're unlucky.
[1] http://openjdk.java.net/jeps/189 [2] https://www.azul.com/products/zing/
In particular, Go's GC is not yet generational from the talks I've seen, which is a large throughput loss compared to the GC of the JVM.
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gc...
Go will receive those feature requests too. The Go developers may not be as willing to provide so many knobs (which is a position I'm entirely sympathetic to, don't get me wrong). But the settings always exist, regardless of whether Google hammers in values for them or leaves them adjustable. GC is full of tradeoffs; they're fundamental to the problem.
There are many other flags too, and you can tune them if you want to squeeze more performance out of your system, but you don't have to use them if you don't want to.
Sometime just -O2 or -O3 aren't not enough, regardless how much tuning has gone into them.
If your memory manager does not compact the heap (i.e. never moves anything), then this implies a couple of things:
1. You can run out of memory whilst still technically having enough bytes available for a requested allocation, if those bytes are not contiguous. Most allocators bucket allocations by size to try and avoid the worst of this, but ultimately if you don't move things around it can always bite you.
2. The allocator has to go find a space for something when you request space. As the heap gets more and more fragmented this can slow down. If your collector is able to move objects then you can do things generationally which means allocation is effectively free (just bump a pointer).
In the JVM world there are two state of the art collectors, the open source G1 and Azul's commercial C4 collector (C4 == continuous compacting concurrent collector). Both can compact the heap concurrently. It is considered an important feature for reliability because otherwise big programs can get into a state where they can't stay up forever because eventually their heap gets so fragmented that they have to restart. Note that not all programs suffer from this. It depends a lot on how a program uses memory, the types of allocations they do, their predictability, etc. But if your program does start to suffer from heap fragmentation then oh boy, is it ever painful to fix.
The Go team have made a collector that does not move things. This means it can superficially look very good compared to other runtimes, but it's comparing apples to oranges: the collectors aren't doing the same amount of work.
The two JVM collectors have a few other tricks up their sleeves. G1 can deduplicate strings on the heap. If you have a string like "GET" or "index.html" 1000 times in your heap, G1 can rewrite the pointers so there's only a single copy instead. C4's stand-out feature is that your app doesn't pause for GC ever, all collection is done whilst the app is running, and Azul's custom JVM is tuned to keep all other pause times absolutely minimal as well. However IIRC it needs some kernel patches in order to do this, due to the unique stresses it places on the Linux VMM subsystem.
Java code also tends to make more allocations than Go code, simply because Java does not (yet) have value types, and Go does. This isn't really anything to do with the GC, but it does mean that Java _needs_ a more powerful GC just to handle the sometimes much greater volume of allocations. It also makes Java programmers sometimes have to resort to hacks like arrays of primitive types (I've done this before).
People like to talk about how important generational GC is, and how big a problem it is that Go doesn't have it. But I have also seen that if there is too high a volume of data in the young-gen in Java, short-lived objects get tenured anyway. In practice, the generational assumption isn't always true. If you use libraries like Protobuffers that create a ton of garbage, you can pretty easily exceed the GC's ability to keep up with short-lived garbage.
I'm really curious to see how Go's GC works out for big heaps in practice. I can say that my experience with Java heaps above 100 GB has not been good. (To be fair, most of my Java experience has been under CMS, not the new G1 collector.)
As an example, Windows has a special malloc called the "low fragmentation heap" specifically to help fight this kind of problem - if fragmentation was never an issue in practice, such a feature would not exist.
CMS was never designed for 100GB+ heaps so I am not surprised your experience was poor. G1 can handle such heaps although the Intel/HBase presentation suggested aiming for more like 100msec pause times is reasonable there.
The main thing I'd guess you have to watch out for with huge Go heaps is how long it takes to complete a collection. If it's really scanning the entire heap in each collection then I'd guess you can outrun the GC quite easily if your allocation rate is high.
32-bit is painful and messy. If possible, one thing that may help is to allocate large (virtual memory wise) objects once in the beginning of a new process and have separate heaps for different threads / purposes. Not only heap fragmentation can be issue, but also virtual memory fragmentation. Latter is usually what turns out to be fatal. One way to mitigate issues with multiple large allocations is to change memory mapping as needed... Yeah, it can get messy.
64-bit systems are way easier. Large allocations can be handled by allocating page size blocks of memory from OS (VirtualAlloc / mmap). OS can move and compact physical memory just fine. At most you'll end up with holes in the virtual memory mappings, but it's not a real issue with 64 bit systems.
Small allocations with some allocator that is smart enough to group allocations by 2^n size (or do some other smarter tricks to practically eliminate fragmentation).
Other ways are to use arenas or multiple heaps. For example per thread or per object.
There are also compactible heaps. You just need to lock the memory object before use to get a pointer to it and unlock when you're done. The heap manager is free to move the memory block as it pleases, because no one is allowed to have a pointer to the block. Harder to use, yes, but hey, no fragmentation!
Yeah, Java is better in some ways for being able to compact memory always. That said, I've also cursed it to hell for ending up in practically infinite gc loop when used memory is nearing maximum heap size.
There's no free lunch in memory management.
At Cloudera, we still mostly use CMS because the version of G1 shipped in JDK6 wasn't considered mature, and we only recently upgraded to JDK7. We are currently looking into defaulting to G1, but it will take time to feel confident about that. G1 is not a silver bullet anyway. You can still get multi-minute pauses with heaps bigger than 100GB. A stop-the-world GC is still lurking in wait if certain conditions are met, and some workloads always trigger it... like starting the HDFS NameNode.
G1 has improved a lot over time. What I've been writing was based on the assumption of using the latest version of it.
Yes, full stop-the-world GCs are painful, but they'll be painful in any GC. If Go runs out of memory entirely then I assume they have to do the same thing.
Both of these can dramatically reduce GC pressure too.
So the golang pool is good for the case where you have GC heavy but non-latency sensitive operations, but not the more general performance sensitive problems.
The experience of every JIT developer is that dynamic type checks do matter a lot in hot paths.
So your had rolled one will not have the typing overhead we are discussing, but it will have 2 much worse issues.
1. sync.Pool's have thread local storage, something your own pools will not have.
2. sync.Pool's are GC aware; meaning if the allocator is having trouble it can drain "free" pool objects to gain memory. Your custom pool will not have this integration.
I have a feeling that the performance you gained not type-checking you will loose by not having #1.[edit] As pcwalton points out. My whole argument is actually null and void due to type erasure...doh.
If that branch is mispredicted, we're talking about 12-20 cycles. Ok, I assume it's a range check and thus (nearly) always not taken. So if it's in hot path, it'll always be correctly predicted. Modern CPUs will most likely fuse cmp+jae into one micro-op, so predicted-not-taken + mov will take 2 cycles (+latency).
"cmp/jae/cmp/jne/mov" will of course be fused into 3 micro-ops. But don't you mean "cmp/jae/cmp/je/mov"? I'm assuming second compare is a NULL check (or at least that instructions are ordered that way second branch is practically never taken). I think that also takes 2 cycles (both branches execute on same clock cycle + mov), but not sure how fused predicted-not-takens behave.
L3 miss for that mov, well... might well be 200 cycles.
The first compare is a bounds check against the array backing the pool, and the second compare is against the type field on the interface, not a null check. Golang interfaces are "fat pointers" with two words: a data pointer and a vtable pointer. So the first cmp is against a register, while the second cmp is against memory, data dependent on the register index. The address of the cmp has to be at least checked to determine if it faults, so I would think at least some part of it would have to be serialized after the first branch, making it slower than the version without the type guard.
Well, I didn't profile that case. Who knows what will really happen. Modern x86 processors are hard to understand.
> ... while the second cmp is against memory, data dependent on the register index
Hmm... that sounds like something that would dominate the cost? Memory access and data dependency. Ouch.
Also of course in that case, second compare+branch can't be fused, because cmp has a memory operand.
[1] http://www.stefankrause.net/wp/?p=64 [2] http://www.ibm.com/developerworks/library/j-jtp09275/
https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/me...
Also that they went VM instead of AOT like those languages main implementations.
Oh well, at least they are now on the roadmap for Java 10, 30 years later.
That said, JVMs do not actually stack allocate anything. They do a smarter optimisation called scalar replacement. The object is effectively decomposed into local variables that are then subject to further optimisation, for instance, completely deleting a field that isn't used.
Value types will be added to the JVM eventually in the Valhalla project. Go fans may note here that Go has value types, but this is a dodge - the bulk of the work being done so far in Valhalla is a major upgrade of the support for generics, because the Java (and .NET) teams believe that value types without generic specialisation is a fairly useless feature. If they didn't do that you could have MyValueType[] as an array, but not a List<MyValueType> or Map<String, MyValueType> which would make it fairly useless. Go gets around this problem by simply not letting users define their own generic data structures and baking a few simple ones into the language itself. This is hardly a solution.
<li>{{.}}</li>
{{end -}}This seems like a bit of a hack to be honest. Would anything break if
{{range .}}
<li>{{.}}</li>
{{end}}worked as expected?
And even then the presence or absence of whitespace in HTML does have rendering impacts (though I don't remember one offhand — aside from linebreaks — it's been a long time since I last hit one), so the template author must be able to control it and importantly to keep whitespace present between static and dynamic items.
[0] html/template is a relatively thin layer over text/template with built-in XSS security where the original does "raw" output by default
Whitespaces shouldn't be touched, just parse them though and don't process them.
That would make the templates look more like those of Django and Jinja2, which most seem comfortable with.
That's what html/template does by default, the point of the addition is this can be inconvenient as you may want whitespace for source readability but can't have whitespace in the output.
> That would make the templates look more like those of Django and Jinja2, which most seem comfortable with.
The behaviour outlined here is the same as jinja's: output text nodes as-is by default (whitespace and all), specify `-` to trim whitespace on the corresponding side of a template item.
This is actually a feature that's been in Jinja forever that I've always wanted in Go's text/template
http://jinja.pocoo.org/docs/dev/templates/#whitespace-contro...
This is a problem I have in my Jade (aka Pug) templates, which trims whitespace around the contents of each element by default -- and there, I have to append `#{' '}` HTML literals at the end of lines in order to assert whitespace where I need it. But overall, the whitespace-trimming in Jade is great becuause it's a targeted, somewhat-opinionated tool, contrary to Golang standard library.
If you need/want templating to work differently, though, there's no reason to not use some other template library. Or you could fork the standard library, make modifications to suit your needs, and use that instead. It's very easy to do that with Go.
However, everytime Go is discussed at HN I see many posts criticizing the language for various reasons and this has put me off getting started learning Go.
Some of the criticisms of Go are valid and some are just haters doing what haters do--hating. Keep in mind Go is very young for a programming language. It's only about 6 years old, but it's use is becoming more and more widespread as it matures.
Although you are correct in that the standard library is pretty much all you need to write CLI apps, this library is definitely useful if you're willing to pull in a third-party dependency: https://github.com/codegangsta/cli
A lot of the criticisms turned me off as well, but I dove in anyway.
I stopped paying attention to the criticisms when I found out how incredibly productive I was able to be in Go.
Give it a try and make your own call on it. The cognitive overhead of jumping into Go is so small compared to many other languages.
I'm not using Go now, though I have tried it out years ago. Your point is exactly why it stays on my radar for possible future use. Especially if you consider any sort of business aspect, ramping up the help. I would strongly consider using it for any serious backend project in a business environment due to the performance, ease of learning and compatibility promise.
I have one script that writes postgres, does a HTTP request and needs to read file. 100 locs resulted in 7.2M binary size.
I'm wondering how it holds up without compaction and in the face of fragmented heaps.
Best part of Golang, more languages need to borrow this feature. Second best is how relatively easy it is to get going with it.
Noticed couple e-commerce companies and Google itself in Singapore. Few Russian companies are doing small infra project.
Anyone else seriously investing in Golang?
Walmart, Apple, Facebook, eBay, Intel, Google, Mozilla, IBM, Microsoft, Red Hat, DigitalOcean, Zynga, Yahoo, BBC, VMware, Uber, GitHub, Getty Images, Twitter, Stack Exchange, Docker, SpaceX, Baidu, Qiniu, Imgur, CloudFlare, Bitbucket, Dell, Twitch, Dailymotion, bitly, Cisco, Verizon, Dropbox, Adobe, New York Times, HP, Canonical, Cloud Foundry, 99designs, BuySellAds, CoreOS, MongoDB, Basecamp, Rackspace, Booking, MalwareBytes, Kingsoft, Iron.io, OpenShift, Heroku, Square, Spring, Tumblr, VMWare, Symantec, Comcast, CBS, SendGrid, Digitally Imported, Pivotal, Couchbase, Koding, Shopify, Shutterfly, MaxCDN, Linden Lab, SolarWinds, IMVU, EMC, Teradata, and I'm sure many more which I'm unaware of, are all using Go to some capacity.
I refuse to touch Go until that happens. Go is the asshole of programming languages. It forces you to put code in deeply nested annoying directories
vim foo.go
vim ../../../github.com/blahblah/moreblahblah/blah.go
sigh
But that I could live with. But constantly commenting the code out (which of course leads to commenting out even more code!) is the real PITA.All said and done it looks like a useful, if uninspired, language. Can avoid it so may as well use it. But this nonsense has to stop!
import _ "net"
Make sure you don't check such lines of code in to your repo, because there is a concrete benefit to having the language enforce this import strictness.But the person who would check in code with unused _ imports, would probably abuse a hypothetical "allow unused imports" flag in production. Go is probably not for that person.
Or more to the point, if Go can figure out imports, why require them to be explicit at all?
- The header is cut off
- Slide 20 gives an error when running [c: template: redefinition of template "list"]
- Stable sort example does not seem to work. Gives same output as regular sort
> Most of the code examples won't run except locally and using Go 1.6.
> The playground still runs Go 1.5.