
Things I like about programming in Go - jgrahamc
http://blog.jgc.org/2012/07/things-i-like-about-programming-in-go.html
======
cletus
I have a hard time getting past it being mostly unusable on 32 bit Linux [1].
That just seems like a fundamental flaw in the garbage collector that moving
to 64 bit simply kicks the can down the street.

And before anyone suggests "just use 64 bit", that actually a really crappy
solution. In most cases it merely (almost) doubles your memory footprint for
no real gain.

[1]: [https://groups.google.com/group/golang-
nuts/browse_thread/th...](https://groups.google.com/group/golang-
nuts/browse_thread/thread/ab1971bb9459025d?pli=1)

~~~
enneff
There's a patch to make the current conservative GC a precise GC, which will
totally fix this problem. It's in the process of being reviewed and integrated
into the core.

<http://codereview.appspot.com/6114046/>

FWIW, the panelists on the recent "Go in production" panel at I/O responded
that garbage collection wasn't an issue for them on 64-bit systems.
<http://www.youtube.com/watch?v=kKQLhGZVN4A>

~~~
pcwalton
That's awesome, kudos. How do you deal with maintaining the distinction
between pointers and values on the stack and in registers? I didn't see any
code to do that, skimming through that patch. Since it's a precise GC, you
need to maintain the distinction.

In Rust we have a very large patchset to LLVM to allow us to do this; it
involves modifying nearly every stage of the LLVM pipeline. Curious how you
did that in gccgo.

Edit: I just looked at the source and it still seems conservative for the
stack and registers. So it's not a precise GC yet and will not eliminate false
positives. (This is of course totally fine, I just wanted to clarify for
myself.)

~~~
enneff
This isn't my patch, so I can't speak to the specifics of it. Also, it isn't
for gccgo, but the gc compiler.

~~~
lobster_johnson
That's too bad. Hopefully someone will improve `gccgo` as well. I actually
started out experimenting with Go using the `gc` compiler, and found
performance (basically manipulating large arrays sequentially) atrocious.
Moving to `gccgo` improved performance four-fold, making my unoptimized Go
test code in some cases faster than my unoptimized C test code.

~~~
luriel
How long ago was that? The gc compilers have seen lots of improvement
recently, and more will be merged soon.

Also, if the problem is so pathological, it might be worth filling an issue.

~~~
lobster_johnson
Go 1.0.2, which is the newest stable release. I will see if I have some extra
time to file a report.

Slightly off topic (but relevant to my comment above), do you know why this:

    
    
        func TraverseInline() {
          var ints := make([]int, N)
          for i := range ints {
            sum += i
          }
        }
    

should be slower than:

    
    
        func TraverseArray(ints []int) int {
          sum := 0
          for i := range ints {
            sum += i
          }
          return sum
        }
    
        func TraverseWithFunc() {
          var ints := make([]int, N)
          sum := TraverseArray(ints)
        }
    

Seeing a huge difference here. With an array of 300000000 ints,
TraverseInline() takes about 750ms, whereas TraverseWithFunc() takes 394ms.
With gccgo (GCC 4.7.1), the different is much slighter, but there's still a
difference: 196ms compared to 172ms. (These are best-case figures, I'm doing
lots of loops.) Go is compiled, not JIT, so I don't see why a function should
offer better performance.

~~~
dmit
I'm not seeing any difference between the two functions using the 1.0.2 x64 gc
compiler. Here's the code I used (slightly modified to compile):
<http://play.golang.org/p/gcf0BOGncQ>

For more complex functions, you could try profiling using the runtime/pprof
package. More info here: <http://blog.golang.org/2011/06/profiling-go-
programs.html>

If you want to dig deeper and are not averse to assembly, you can pass
-gcflags -S to 'go build' and see the compiler output. Here's what I got for
the above code:

    
    
      --- prog list "TraverseInline" ---
      0000 (/tmp/tmp.rkM3kSJ6Hd/trav.go:10) TEXT    TraverseInline+0(SB),$40-8
      0001 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ    $type.[]int+0(SB),(SP)
      0002 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ    $300000000,8(SP)
      0003 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ    $300000000,16(SP)
      0004 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) CALL    ,runtime.makeslice+0(SB)
      0005 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ    24(SP),BX
      0006 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVL    32(SP),SI
      0007 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVL    36(SP),BX
      0008 (/tmp/tmp.rkM3kSJ6Hd/trav.go:12) MOVL    $0,DX
      0009 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) MOVL    $0,AX
      0010 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) JMP     ,12
      0011 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) INCL    ,AX
      0012 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) CMPL    AX,SI
      0013 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) JGE     ,16
      0014 (/tmp/tmp.rkM3kSJ6Hd/trav.go:14) ADDL    AX,DX
      0015 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) JMP     ,11
      0016 (/tmp/tmp.rkM3kSJ6Hd/trav.go:16) MOVL    DX,.noname+0(FP)
      0017 (/tmp/tmp.rkM3kSJ6Hd/trav.go:16) RET     ,
    
      --- prog list "TraverseArray" ---
      0018 (/tmp/tmp.rkM3kSJ6Hd/trav.go:19) TEXT    TraverseArray+0(SB),$0-24
      0019 (/tmp/tmp.rkM3kSJ6Hd/trav.go:20) MOVL    $0,DX
      0020 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) MOVL    $0,AX
      0021 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) MOVL    ints+8(FP),SI
      0022 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) JMP     ,24
      0023 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) INCL    ,AX
      0024 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) CMPL    AX,SI
      0025 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) JGE     ,28
      0026 (/tmp/tmp.rkM3kSJ6Hd/trav.go:22) ADDL    AX,DX
      0027 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) JMP     ,23
      0028 (/tmp/tmp.rkM3kSJ6Hd/trav.go:24) MOVL    DX,.noname+16(FP)
      0029 (/tmp/tmp.rkM3kSJ6Hd/trav.go:24) RET     ,
    
      --- prog list "TraverseWithFunc" ---
      0030 (/tmp/tmp.rkM3kSJ6Hd/trav.go:27) TEXT    TraverseWithFunc+0(SB),$40-8
      0031 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ    $type.[]int+0(SB),(SP)
      0032 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ    $300000000,8(SP)
      0033 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ    $300000000,16(SP)
      0034 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) CALL    ,runtime.makeslice+0(SB)
      0035 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ    24(SP),DX
      0036 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVL    32(SP),CX
      0037 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVL    36(SP),AX
      0038 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) LEAQ    (SP),BX
      0039 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVQ    DX,(BX)
      0040 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVL    CX,8(BX)
      0041 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVL    AX,12(BX)
      0042 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) CALL    ,TraverseArray+0(SB)
      0043 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVL    16(SP),AX
      0044 (/tmp/tmp.rkM3kSJ6Hd/trav.go:30) MOVL    AX,.noname+0(FP)
      0045 (/tmp/tmp.rkM3kSJ6Hd/trav.go:30) RET     ,

~~~
lobster_johnson
Here is my test, with test output for gc and gccgo:

<https://gist.github.com/3053930>

The numbers are from Go 1.0 as that's what is available on Ubuntu Precise, my
test box. (Getting similar numbers with 1.0.2 on a different box where I don't
have gccgo.)

I realized that the first loop was using "i < len(ints)" as a loop test, which
turns out to be more expensive than I thought, and the compiler doesn't
optimize it into a constant (which would be expecting too much, I guess).
After rewriting the test, the function call case is only slightly faster,
although it is still significantly faster with gccgo.

------
phasevar
I've been a Python developer for a decade. Go is the first language to
successfully pull me away from Python. I've been developing in it for almost a
year now and for every reason this blog post points out, I'm in love with it.

~~~
xaa
How has the library situation been? As a fellow Python developer, I'm very
spoiled on libraries and every time I've looked at go, it doesn't seem to be
gaining traction in that area.

The page full of go libraries the OP points to is laughably small -- even
Haskell does better.

The core language though seems quite nice. Although it looks to not support
OpenMP, which is a shame.

EDIT: I understand that Go is young and thus can't match up directly with
Python/Java/Perl/C in sheer number of libraries. I'm more concerned with the
rate of change of language adoption and new library creation, which doesn't
seem to be large. But, the enthusiasm in this thread is quite encouraging.

~~~
luriel
Can you be more specific regarding what libraries you need/miss?

~~~
objclxt
Actually, yes! I picked up Go this week, and I'm actually really enjoying it.
I did, however, hit a slight stumbling block because I needed to
cryptographically sign some files to the PKCS#7 standard.

The 'usual suspects' have library support for this (Ruby, PHP, etc etc): Go
does not (so I invoke openssl from within Go instead).

Putting that aside, it is a great language and one I'm really enjoying using.
I primarily work in Objective-C, so it makes a nice change!

~~~
luriel
I suspect the 'usual suspects' just wrap OpenSSL, you can use Go bindings for
OpenSSL too, but I'm also sure Adam Langley, who is responsible for the Go
crypto lib and the main crypto guru for Chrome will be happy to help get any
bits that you are currently missing into the go.crypto repo.

Hell, Go even has an official ssh library, I don't think any of the 'sual
suspects' has that ;)

<http://go.pkgdoc.org/code.google.com/p/go.crypto/ssh>

~~~
objclxt
To be fair, I didn't actually expect Go to have it covered. I was porting over
some Ruby code, and it just meant a little diversion via the OS/Exec package.

------
jbert
Will go really multiplex goroutines which are blocking on system calls as well
as those blocking on go-level entities (e.g. channels)?

The underlying OS threads will do that, but if goroutines aren't 1-1 with OS
threads (which I don't believe they are), how is this achieved? Are all
current syscalls intercepted?

What about calls out to external C libs which in turn call blocking syscalls?

(I could test, but if go happens to be running all my goroutines in their own
OS threads, I could get a false positive "pass")

~~~
btilly
According to <http://code.google.com/p/try-catch-finally/wiki/GoInternals> it
does.

The magic trick is that it runs the scheduler just before the blocking call,
and if need be it will spawn a new thread to make the blocking call in. That
way the thread that thinks it made the blocking call is free to continue
running other goroutines.

------
ardit33
I tried, and was exited about it, but the lack of easy IDE integration and the
lack of a proper debugger were a turn off for me. For now at least.

Two questions to people that use Go:

1\. Does Go have a good debugger that is easy to use? i.e. I shouldn't have to
mess with the command line, but preferably use it directly from something like
eclipse. It should work even in Windows.

2\. Is goclipse working properly? Last time I used id, while it would show the
syntax fine, the build and run was not working. I had to use the command line.

I think a language should be developed together with its tools. Right now Go
is lacking in this department.

~~~
1337p337
> I think a language should be developed together with its tools.

A lot of people will probably not like this answer, but Go's "tools" are the
Acme editor and the shell. If you're running on Windows, Acme-SAC
(<https://code.google.com/p/acme-sac/>, or just running Acme under "vanilla"
Inferno) is a viable option, and is written in Limbo, a precursor to Go. (It
still has some features Go lacks, but is roughly comparable.)

I wouldn't hold my breath on getting former Bell Labs guys to work on a
graphical debugger or Eclipse support, so the language is somewhat unlikely to
be developed together with those tools.

~~~
luriel
> I wouldn't hold my breath on getting former Bell Labs guys to work on a
> graphical debugger or Eclipse support, so the language is somewhat unlikely
> to be developed together with those tools.

I wouldn't expect them to, and I also really can't understand the appeal of
something as complex as an IDE to write code in a language as simple as Go,
but Goclipse has been around for a while, and there is support for other IDEs:

<http://code.google.com/p/goclipse/> <http://go-lang.cat-v.org/text-editors/>

~~~
krishnakv
I program Go on both emacs and sublime text 2, very good support for both
editors.

------
pjmlp
With so many blogs writing praises about Go, I keep wondering how much people
learn about programming languages in the universities nowadays.

As a compiled language, Go gets usually compared to the experiences people
have with C and C++.

The reality is that 90% of Go features are available in the Pascal family
branch of programming languages since the early 80's.

So most features that people enjoy in Go are all features that would already
be mainstream, if the Pascal branch had won over C to become mainstream.

Even the ability to use a language like Go for systems programming has been
proven in the Native Oberon OS and its descendents (A2) at ETHZ.

~~~
krasin
What's about CSP (channels/goroutines), static duck typing (Go interfaces) and
a very practical standard library?

I had taken a look at Oberon back in 1999 and found it so much worse than
Delphi for writing real programs. (which was a precursor of C#/Windows Forms,
not Go).

~~~
pjmlp
> What's about CSP (channels/goroutines), static duck typing (Go interfaces)
> and a very practical standard library?

CSP and static duck typing belong to the missing 10% I noted.

Still there are available in other languages that also support native code
generation with modules.

> I had taken a look at Oberon back in 1999 and found it so much worse than
> Delphi for writing real programs. (which was a precursor of C#/Windows
> Forms, not Go).

Go method definitions are taken from Component Pascal, an extension to Oberon.

Actually I find Delphi much more powerful than Go for large scale development
in the enterprise world.

~~~
krasin
How much code have you written in Go?

~~~
pjmlp
You mean besides doing a few contributions to the core Go libraries?

Do you want to have the ticket numbers and Mercurial branches information?

~~~
krasin
Paulo, I'm very sorry if my question looks a bit rude. The only goal I had is
to understand if your opinion is theoretical or based on the real experience.

I don't share your opinion on Go syntax, although, I like Pascal family much
more than C and had been using Pascal/Delphi almost exclusively for all my
projects until 2003. After that, Delphi became too outdated and I had to
switch to other languages.

Also, as I have said before, my knowledge about Oberon and Component Pascal is
theoretical, so it might be that you're actually right. Given that these
languages are effectively dead (by github criteria: it does not recognize
Oberon or Component Pascal, but supports DCPU16 assembly), I don't have a
chance to improve this situation (any program written on dead language is
theoretical, because it would not be used in prod)

~~~
pjmlp
No problem.

I did contribute the initial version of the os.user package for Windows, which
was then picked up by the core team. Also started to add Windows to the
exp.gui/draw packages, but gave up on them when they were dropped from Go 1.

I find github criteria a bad one, as many people I know don't even care about
it.

Oberon is still pretty much alive for ETHZ students as Active Oberon, just
check the list of possible assignments,
<http://www.nativesystems.inf.ethz.ch/WebHomeProjects>.

You can see the original method syntax for Component Pascal here,
<http://www.oberon.ch/pdf/CP-Lang.pdf>, section 10.2.

I still lurk in the Go mailing list as a language geek, but lost interest, as
the language feels too minimalist to my taste, given my experience with other
languages.

In a way I belong to the group Rob recently described as C++ programmers.
Given my experience in language design, using Go makes me feel I am back in
high school with Turbo Pascal 6.0.

But Google's weight might nevertheless help Go become mainstream, who knows.

------
jwingy
Maybe I'm not understanding it correctly, but on the point about "it's ok to
block", does that mean Go will continue to serve other requests normally, but
at the point of the blocking code, it automatically has a "callback" to the
next command? (effectively saving the programmer the pain of having to write
code in callback fashion)

Having no prior experience with Go, I found the rest of the article
informative!

~~~
phasevar
Yeah, Go has a concept called Goroutines which are like lightweight threads.
The system multiplexes Goroutines onto system threads. This means if one
Goroutine blocks while waiting on IO, the Go scheduler will automatically
select another Goroutine to run while the first Goroutine waits on IO.

Writing your code in blocking style is just easier to follow than the
callbacks used in Node and other asynchronous event servers.

------
zoowar
Let's not forget excellent documentation that links directly to the source
code.

------
eternalban
CSP is an interesting take on concurrency but lends a degree of opacity to the
scheduling of tasks. So far (working in Go) in minor (or well understood
patterns) it is a non-issue, but I wonder what would happen at large scale. It
is also worth noting that in Go 1 runtime, switching from 1 "processor" to N
can certainly alter the behavior of your program e.g. the same precise [1]
binary may lead to CSP/Go's version of "deadlock" e.g. "all goroutines are
sleep" or not depending on simply 1 bit of difference in the binary. The
current runtime also gives rise to some interesting (and very unintuitive)
performance differences when hacking with the CSP mechanism e.g. peppering GO
sources with unnecessary sleeps, etc. to hack the scheduler.

[1]: well nearly the same. Somewhere in the binary, a call to set maxprocs is
present. Same exact program differing only in the param to this call (i.e. 1
vs 2).

~~~
kaib
We (tinkercad) have actually ended up running once process per core and
limiting the number of concurrent OS threads. This gives significantly faster
task switching times and is more performant from a GC perspective. Given our
system is highly distributed anyway shared memory would mostly be an
optimization and not a primary facility.

------
agentgt
Go is interesting in that doesn't yet support Generics... but might some day.

The reason that interest me is that I like Generics aka parametric
polymorphism but I feel like Scala, Haskell and OCaml take it almost to the
extreme particularly since those languages also have type inference. So it can
be overwhelming for a new user.

That being said if Rust was more mature I would prefer it over Golang (here is
another hacker news comment on this:
<http://news.ycombinator.com/item?id=1951467>).

------
eta_carinae
> And the fact that it's missing exceptions seems like a win because I much
> prefer dealing with errors when they occur.

This person doesn't seem to have much experience with error handling.

You shouldn't deal with errors the way you prefer it, you should deal with
them _the right way_. Sometimes, it's where they occur, but very often, it's
somewhere up the stack.

We learned this lesson in the 90's.

~~~
icebraining
It seems odd to assume he has that opinion because of lack of experience,
considering we're discussing a language developed by people like Rob Pike, who
share his opinion on error handling and can hardly be described as
unexperienced.

~~~
luriel
And people like Ken Thompson.

Accusing the author of this article of being "inexperienced" is also very
amusing: <http://en.wikipedia.org/wiki/John_Graham-Cumming>

And even Java and C++ luminaries like Bruce Eckel have come out in support of
Go's error handling and against exceptions.

~~~
jgrahamc
Not sure that I deserve to be mentioned in the same breath as Ken Thompson.

------
didip
does Go have package management? How do you guys find 3rd party libraries?

~~~
objclxt
You can use 'go get' to download and install packages direct from repository
links, and there is a reasonable definitive list of open sourced libraries
available here:

<http://go-lang.cat-v.org/pure-go-libs>

~~~
genbattle
There's also the Go project dashboard:
<http://godashboard.appspot.com/project>

