top | item 3349948

From zero to Go: launching on the Google homepage in 24 hours

274 points| enneff | 14 years ago |blog.golang.org | reply

73 comments

order
[+] biot|14 years ago|reply
I'm curious to know how many instances were required and what the cost would have been had someone been billed for it.
[+] yesbabyyes|14 years ago|reply
I really liked the turkey, and I thought the solution looked elegant, so I had a go at it in CoffeeScript and node-canvas: https://gist.github.com/1475034

All requests in less than 66 ms, mean less than 19 ms.

[+] ariwilson|14 years ago|reply
AppEngine instances, unless they're running in a special backend instance, exist in a special containerized machine. These machines are very slow and don't have much RAM - 600 MHz and 128 MB. Based on the source that was posted on the blog, this app was running on a normal instance, as the source did not contain a backends.yaml file.

So it makes sense that a Core 2 Duo running at 2.13 GHz and slow CoffeeScript would be ~2x as fast. My personal experience of running Go code locally (on a Core i5 2500K) versus on a normal AppEngine instance showed a slowdown of around 6x or more.

[+] jphackworth|14 years ago|reply
To be fair, you loadtested with ab, whereas the author launched his feature on the Google homepage.
[+] danielrhodes|14 years ago|reply
This was interesting because I've never seen Go used in production yet. However, the solution is not optimal for performance (as his benchmarking suggests is one of the goals).

If there is a finite number of possible images and each image takes CPU time to generate, then why not lazy cache the images in a CDN after generating them once?

[+] enneff|14 years ago|reply
This works exactly as you describe - the app sets a cache header on the response and Google's geographically distributed front end servers will serve the cached response. You get all this for free by using App Engine.
[+] cma|14 years ago|reply
The more complicated something is, the more chances for failure there are. Remember, this was only for compositing the image that would be shared to google plus -- the one in the browser is done client-side.
[+] ars|14 years ago|reply
This is quite offtopic, but if even programmers are using jpeg when they should use png, what hope is there for anyone else?

The images this generates have mostly flat areas of all one color - it should have been a png.

[+] JabavuAdams|14 years ago|reply
I'm working on a game with highly-stylized art that includes a lot of blobs of single colour. It surprised me at first, but the artists have had real gains using JPG instead of PNG. Also, strangely the artifacts give the art a kind of "texturized" finish that's actually kind of pleasant on tablet screens.
[+] canop_fr|14 years ago|reply
Maybe it's for performance reasons. I have a web server in go that generate (big) png on the fly and most of the time is spent in the png encoding (mostly the compression). PNG encoding is a heavy task.

Here's (from my blog) a pprof profile of my server : http://canop.org/pub/pprof4353.0.svg so that you can see the burden.

(I didn't make comparisons with other languages so I don't know if Go's png encoder is fast or not, I suspect it isn't)

[+] bostonvaulter2|14 years ago|reply
Would the size of the png's be smaller?
[+] nickik|14 years ago|reply
Maybe its just me but all blogpost about go read like they where checked by a marketing guru after the have been written. The always enforce the same basic points, it always sound the same. If I somebody says "go" my mind alwasys jumps to "feels like a interpreted language".
[+] callahad|14 years ago|reply
> somebody says "go" my mind alwasys jumps to "feels like a interpreted language".

Because it does! I'm a pretty hardcore Pythonista who has never really gotten very far with compiled languages. I wrote a simple shell in C, once, but beyond that they never felt right. Somehow, Go manages to get things sufficiently "right" for me.

But more importantly, I think Go has a very strong chance of being the Clojure moment for Algol-derived languages. It's trying to address many of the same problems, and taking a similarly refreshing and pragmatic approach. Not to mention that the system-level language ecosystem is quite overdue for some reinvigoration. The glowing tone of so many Go posts may be, in part, due to a fundamental yearning for something that gets so much right -- something like Go -- to finally take off.

[+] enneff|14 years ago|reply
Well I'm the guy who runs that particular blog, and I can tell you I'm no marketing guru. While Reinaldo and I edited the post together, I'm pretty sure that the line was in his original text. (Just checked: it was.)

Maybe people keep saying Go feels like an interpreted language because it feels like an interpreted language? :-)

[+] shadowmint|14 years ago|reply
Well, when you're looking at posts by someone who loves a language about that language, its not really that surprising to see positive feedback in it; and that feedback is often about the expressive syntax being nice (edit <-- and that its fast).

...but that said, I wouldn't mind some impartial coverage.

Its stuck me several times how defensive and no-I'm-right the go crowd gets when anyone dislikes it. Try jumping on the google group sometime.

Anyone remember that academic paper about relative speeds of c, go and something or another? As I recall Andrew went out of his way to reimplement the whole thing in go, and make a few snarky comments about "I wonder where they got those bench marks from, because that's not what I saw".

[+] comex|14 years ago|reply
Is Go's character type signed? If so, it's possible to make p negative, in which case this wouldn't apply:

        if p >= len(em) {
            panic(fmt.Sprintf("element index out of range %s: "+
                "%d >= %d", t, p, len(em)))
        }
although Go would still do its own runtime check for the dereference.
[+] BarkMore|14 years ago|reply
The variable "p" is a rune. Although the rune type can have negative values, p will only have values >= 0 in this code.
[+] billmcneale|14 years ago|reply
I can't help but cringe whenever I read Go code because the following:

        paths, err := filepath.Glob(dir + "/*.png")
        if err != nil {
            panic(err)
        }
is repeated over and over again.

Didn't we learn in the 90's that exceptions are far, far superior to return codes?

[+] timtadh|14 years ago|reply
Short answer no.

Long answer, the Go authors (Rob Pike et all) have a pedantic issue with exceptions as implemented in Python, Java, etc. They feel they conflate errors and exceptions. Errors, unlike exceptions, are an expected part of the programming process. Exceptions, on the other hand should be for exceptional circumstances.

I did not say that as well as they do so see what Pike has to say about the issue here: http://groups.google.com/group/golang-nuts/browse_thread/thr...

here is an a typical example of the Go community's attitude:

""" I _especially_ don't want exceptions to become an oft-used alternative to multiple levels of error return, as at that point they deteriorate to action at a distance and make understanding large code bases much harder. Been there, done that, got the scars to prove it. (Mostly from C++ and not from Java, but I've seen enough Java to have a healthy fear of runtime exceptions.) """

       -- Giles Lean
[+] enneff|14 years ago|reply
Not really, no. I've always found them more trouble than they're worth. Explicit, in-line error handling works much better for me.

I'm not sure what you mean by "over and over again," in this context, though. There are only three error checks of that kind in this program. It's an unusual program anyway, in that _any_ error condition jumps to the same path: displaying the default image. In most programs you want finer control over error handling than what's shown here.

[+] tibbe|14 years ago|reply
Reading the same code I also cringed, but for a different reason: the validity of `paths` is tied to the validity of `err`; you should never look at at `paths` without first checking `err`. We can solve this much better using sum types (also known as discriminate unions):

    // Pseudo Go:
    maybe_paths := filepath.Glob(dir + "/*.png")
    case maybe_paths of {
      Some paths: {
        // Do things with paths
      }
      None: {
        // Handle error case
      }
    }
This makes it impossible to use `paths` if `Glob` returns an error, as `paths` won't be in scope!

Furthermore, not having sum types forces us to include a distinguish value `null` for reference (pointer) types, as a way to communicate "no value". This is bad as now we cannot distinguish references that should never be `null` from those than can be `null`.

[+] jemfinch|14 years ago|reply
The two decades of experience we've had since the 90s, building things with exceptions, have fairly adequately demonstrated that they're not superior to return codes, despite their (apparent) theoretical advantages.
[+] danssig|14 years ago|reply
A lot of people in this sub-thread don't buy that return codes are inferior, but they really are. The issue is that the low level code can detect the error and knows some strategies that could be done about it but it's only the high level code that knows what's best (here's the best possible approach [1]). If your library is used by a batch job running on some network-detached headless server it will probably have to handle errors differently than a fat client would.

[1] http://www.gigamonkeys.com/book/beyond-exception-handling-co...

[+] liruqi|14 years ago|reply
the link is broken. something wrong with golang blog?
[+] obtu|14 years ago|reply
Great Firewall?
[+] cma|14 years ago|reply
[+] adamio|14 years ago|reply
Transistor density doesn't guarantee performance increase. Is AppEngine's ability to scale and maintain <60ms consistent with Moore?
[+] BarkMore|14 years ago|reply
There's insufficient data to draw conclusions about the performance of the infrastructure. The application probably ran on a shared machine. We don't know the impact of the other processes running on the machine. We don't know how many requests the application handled in parallel, so we cannot divide by latency to get throughput. We don't know if the application ran on a modern machine.