A simpler way to embed data

In my post about how to efficiently put data into a Go binary, I mentioned that strings are immutable, and can be accessed without causing the Go runtime to copy them. This turns out to be the key to a simpler way to achieve what I wanted to do. By simpler I mean, “no cgo”. That’s a nice simplification, because up until recently, your final static binary image linked to the cgo code dynamically, and that made using my technique impossible in the context of the Tiny runtime, where there is no dynamic linker. Recently cgo has changed, but at the same time, I’ve discovered how to use native strings to do what I want, so let’s see how it works. ...

December 17, 2010 · 3 min · jra

RTM's puzzle

Here is a little program to that implements RTM’s series for Cliff Stoll. package main import "fmt" func count(s []int) int { i := 1 x := s[0] for ; i < len(s); i++ { if s[i] != x { break } } return i } func next(s []int) []int { res := []int{} for len(s) > 0 { n := count(s) res = append(res, n, s[0]) if n == len(s) { break } s = s[n:] } return res } func max(s []int) (m int) { for _, x := range s { if x > m { m = x } } return } func main() { s := []int{1} for i := 0; i < 200; i++ { s = next(s) fmt.Println(len(s), max(s)) } }

December 17, 2010 · 1 min · jra

Where is bytes.NewReaderAt?

I have a nice source of []byte slices (see last post), and now I’d like to do something with them that resembles a filesystem. I was planning on just using a map from name to file contents, which would work ok. But then I remembered seeing the archive/zip package, and I thought how much cooler it would be to just make my prototype filesystem, zip it up, put the zip file into my Go program (see last post) and then access the filesystem via the archive/zip package later. ...

December 13, 2010 · 2 min · jra

Fat Constants, Thin Constants

I play from time to time with a patch for Go that makes the Tiny runtime more capable. My current goal is to get a new backend for exp/draw working which writes to the SVGA screen. It would be cool to be able to decode the Go mascot and have him flying around the screen or something. In the Tiny runtime, there’s no OS, so there’s no disk drivers, so there’s no filesystem, so there’s no files. Which makes decoding a PNG and showing it kind of hard. But if the program carries along the data with it, in the form of a []byte, then you could use bytes.NewReader to turn it into an io.Reader and then pass it to image/png.Decode. So that’s what I set out to do. ...

December 13, 2010 · 7 min · jra

Passing function pointers through channels in Go

Is is possible? Of course! package main func add(x, y int) int { return x + y } func mul(x, y int) int { return x * y } func runner(ch chan func(int, int)(int)) { for f := range ch { println("f(1, 2) = ", f(1, 2)) } } func main() { ch := make(chan func(int, int)(int)) go runner(ch) ch <- add ch <- mul ch <- add close(ch) } Is it useful? Probably, but this demo doesn’t show how yet… have to think about what patterns this makes possible. ...

December 3, 2010 · 1 min · jra

Where's all the magic? In the linker...

I have been trying to make a post per week about Go, but that requires learning something interesting during the week. I’m currently cycling between several little Go toys as I get the time. One is to make Go on raw hardware more useful/interesting. Another is a clone of the console server from conserver.com written in Go. Neither one of those little projects is at a point where I can really explain much about it, but not for want of trying… This week my Go console server project taught me that netchan cannot send channels (OK, I wasn’t really shocked at this, but I was hoping that it might work), and so I’ll need to make my own protocol, and include some proxy channel reader/writers at each end of the TCP connection to send the data into the channel like I want. On the raw-hardware side, I got stuck on code that ends up incorrect once it ends up in the ELF file. Go figure. ...

November 15, 2010 · 6 min · jra

Who said life is fair? The Go scheduler certainly didn't...

In my last post, I showed a program that had a strange behavior that caught my eye. I was trying to look at how Go handles shared access to globals, but the program also had the unintended effect of measuring the “fairness” of the Go scheduler. Here’s the program again, for context: package main import "runtime" var x int var ch = make(chan bool) func f(inc int) { for { println("f: ", inc) x += inc ch <- true } } func main() { go f(1) go f(-1) runtime.Gosched() for { _ = <- ch _ = <- ch println("main sees x = ", x) } } When you run this with GOMAXPROCS=3, and you are on a machine with at least three cores, you get precisely what you’d expect: three system threads, each one running one goroutine, and total fairness: f(1) happens once for every f(-1), and the long-run value of x is around 0, but sometimes higher or lower. ...

November 8, 2010 · 6 min · jra

What's happening here? And when?

A while ago, I posted to the Go users list about what seemed like a problem in how Go was choosing registers versus global variables. Roger’s answer was “go RTFM”, which was precisely the right thing to do. However, it took reading it twice (I’d read it before) and some hard pondering to connect what I was reading to what I was seeing. In order to save you, the reader, from the same experience, here’s a more detailed explanation of how “happens before” applies to programs where coroutines are writing to and reading from globals. Disclaimer: You really shouldn’t be doing this. In Go, you “share memory by communicating, not communicate by sharing memory”. Asking questions about “what happens when two coroutines write to a global?” means you are already thinking about communicating by sharing, and you are in for a world of hurt. But in this case, I was exploring the concurrency model in coroutines in the absence of calls into the runtime (i.e. cooperative or not?) and so I wanted to avoid channels. ...

November 1, 2010 · 5 min · jra

Go Fun - the cost of threads

Here’s a little program I wrote in Go. In it, I wanted to explore how it might work to create a giant “onion” of threads, passing messages in towards the center, where they would be reflected and sent back out. Why? Just for fun, to solidify my grasp on the syntax, to see what would happen. Here’s my program: package main import ( "fmt" "time" ) func make2() (chan int, chan int) { return make(chan int), make(chan int) } func makeAndEcho(ct int, in, out chan int) { var in2, out2 chan int; echo := func () { for !closed(in) { x := <-in out <- x } } pass := func () { for !closed(in) { select { case x := <-in: { in2 <- x } case x := <-out2: { out <- x } } } } if (ct == 0) { go echo() } else { in2, out2 = make2() makeAndEcho(ct-1, in2, out2) go pass() } } func try(depth, n int) { in, out := make2() makeAndEcho(depth, in, out) before := time.Nanoseconds() for i := 0; i < n; i++ { in <- 1 _ = <- out } close(in) after := time.Nanoseconds() transits := int64(n*depth*2) fmt.Printf( "%v round-trips, %v nsec/trip, %v nsec/transit for %v transits\n", n, (after-before)/int64(n), (after-before)/int64(transits), transits); } func main() { try(10, 10000) try(100, 10000) ch := make(chan int, 1) before := time.Nanoseconds() for i := 0; i < 200000; i++ { ch <- i _ = <- ch } after := time.Nanoseconds() fmt.Printf( "no threads: %v nsec/transit for %v transits\n", (after-before)/200000, 200000) } When you run it, you get this: ...

October 12, 2010 · 3 min · jra