July 31, 2015

GopherCon 2015: Favorite talks

Posted in Software at 04:58 by graham

GopherCon was in Denver again this year, and a lot of fun it was, mostly the meeting of wonderful people. Judging by the sponsors, Go continues to be the language of infrastructure (docker, coreos, etcd, mesos, kubernetes, influxdb, etc). Here are my three favorite talks:

  • Rick Hudson: Go GC: Latency problem solved. This was probably the highlight of the conference for me. Rick is a fun speaker, and he’s talking about the key feature in Go 1.5. The garbage collector used to freeze your application for increasingly large amounts of time as you used more memory. It no longer does.
  • Dmitry Vyukov: Go Dynamic Tools. Dmitry covers three tools: The race detector, go-fuzz, and the new execution tracer. The Go tooling is amazing.
  • Audrey Lim: A Beginner’s Mind. Audrey is a lawyer who became a programmer recently, and she recounts her journey – but wait, read on. I confess when her talk was announced I thought I would not find it interesting. I was very wrong, because Audrey is phenomenal. She tried Python, Ruby and Node JS first, and detailed exactly why those were hard for a complete beginner. She made some very astute observations about how the language you use shapes how you think about programming, and the projects you will undertake.

Here are all the other GopherCon 2015 videos.

Robert Griesemer’s keynote highlighted the Pascal (via Oberon) heritage of Go, and remarked that Pascal is a European language family. The C heritage of Go that is more commonly referenced is North American. He hoped Go is a merging of the two software cultures.

And finally, joke of the conference has to go to Tomás Senart.

July 30, 2015

We are Equality

Posted in Society at 05:33 by graham

When the President of the United States of America wants to send an email, we don’t close email-space, and delay your email so his very important one can go through. A homeless person in a public library has exactly the same email service as the richest, most powerful person you can imagine. They both use services such as gmail or yahoo, and beneath that are the same SMTP servers. There is no combination of money, power, or personal connections that can secure you a “better” email service. In email, we are equal.

Pregnant Nigerian teenagers and the ruling family of Saudi Arabia use Facebook or Twitter at the exact same service level, with the same user interface. If you want to write a document, you can use Google Docs, which is what Google’s internal teams use during a crisis. Whoever you are, you can use the same tools, for free, as the people who build the tools.

You can raise money on the same platforms as celebrity musicians or basketball players (Kickstarter, Indiegogo). The Encyclopedia Britannica used to cost upwards of $1,500. Today, Wikipedia provides far more knowledge, for free, for everyone, everywhere. You can rent as many powerful computers as you want, for $1/hour (Linode, Digital Ocean). As the joke goes, on the Internet, no-ones knows you’re a dog.

Technology, and particularly software, have been an incredible equalizing force.

If you have $200 for a computer (or much less for a used model), and occasional access to both the Internet and electricity, you can use, for free, the same tools that a 20-year computer industry veteran such as myself uses (Ubuntu Linux, Go). You have access to all the same learning materials as I do. You can participate in the same forums, communicate with the same people, on the same terms. In the words of the Hacker Manifesto: “If it makes a mistake, it’s because I screwed it up. Not because it doesn’t like me…”.

When you stop to think about it, this is unprecedented. Powerful, affluent, in-group people have always had better homes, transport, and food, breathed better air, and lived longer lives, than the less powerful. There is almost no other facet of human existence where we are all treated exactly equally. It is debatable whether humans, in the physical world, are even capable of this.

All this is possible, maybe inevitable, because of the tools that we in the software industry have built, because of the Internet, and the Open Source model. It makes me incredibly proud to call myself your equal.

July 24, 2015

Building shared libraries in Go: Part 2

Posted in Software at 22:12 by graham

In part 1 we called a very simple Go shared library from Python. Let’s do a more complex example, passing string and []byte, from C++, and getting back a []byte.

Calling Go from C++

Save the following as concat/main.go:

package main

import "C"

//export Concat
func Concat(sIn string, bIn []byte, bOut []byte) {
    n := copy(bOut, sIn)
    copy(bOut[n:], bIn)
}

func main() {}

We add the import "C" so that cgo gives us a header file to #include. Build the shared library and header:

go build -buildmode=c-shared -o libconcat.so concat

In libconcat.h we have this signature:

extern void Concat(GoString p0, GoSlice p1, GoSlice p2)

GoString and GoSlice are defined further up in the header file like this:

typedef struct { char *p; GoInt n; } GoString;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

Copy libconcat.so to /usr/lib/ (or wherever your libraries live). Now let’s call Concat from C++:

#include <vector>
#include <string>
#include <iostream>
#include "libconcat.h"

int main() {
    std::string s_in {"Hello "};
    std::vector<char> v_in {'W', 'o', 'r', 'l', 'd'};
    std::vector<char> v_out(11);

    GoString go_s_in{&s_in[0], static_cast<GoInt>(s_in.size())};
    GoSlice go_v_in{
        v_in.data(),
        static_cast<GoInt>(v_in.size()),
        static_cast<GoInt>(v_in.size()),
    };
    GoSlice go_v_out{
        v_out.data(),
        static_cast<GoInt>(v_out.size()),
        static_cast<GoInt>(v_out.size()),
    };

    Concat(go_s_in, go_v_in, go_v_out);

    for(auto& c : v_out) {
        std::cout << c;
    }
    std::cout << '\n';
}

Save that as concat.cpp. Copy libconcat.h into the same directory. Build and run:

g++ --std=c++14 concat.cpp -o concat -lconcat
./concat

I need the static_cast<GoInt> because a GoInt is a signed type (long long on my machine), but size() returns unsigned type size_t. Apart from wrapping things in Go[String|Slice|etc], this is exactly like calling an ordinary shared libary, because that’s what we built, an ordinary shared library.

The one very important caveat is that you should not pass pointers to Go allocated memory back to the C++ side; that’s why we use an output parameter (v_out). Even if you maintain a reference to it on the Go side so that the garbage collector doesn’t reclaim it, the Go runtime reserves the right to move that memory if they build a copying garbage collector. Lots of details in issue #8310.

Passing C++ allocated memory to Go is fine. Go will not garbage collect memory it did not allocate. There rules here are the same as for cgo.

July 15, 2015

Building shared libraries in Go: Part 1

Posted in Software at 03:22 by graham

Since 1.5, we can write C-style shared libraries (.so) in Go. These can be used directly from C and C++, but also from any language that can use a C shared library, which is most languages. Here’s the original design document. Let’s do a very simple example and call it from Python. In Part 2 we’ll do a more complex example and call it from C++.

Calling Go from Python

package main

import "C"

//export DoubleIt
func DoubleIt(x int) int {
        return x * 2
}

func main() {}

Save that in your GOPATH as doubler/main.go. The exported symbols (the functions the shared library provides) must be in a main package, with a required but ignored main function. They must be marked with magic comment //export <name> (no space), because go build will call cgo.

go build -o libdoubler.so -buildmode=c-shared doubler

This puts libdoubler.so in your current directory. Let’s check we really have a dynamic shared library:

$ file libdoubler.so 
libdoubler.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked ...

$ nm -D libdoubler.so | grep "T DoubleIt"
000000000005d230 T DoubleIt

Well that looks good. Let’s call it from Python, because Python’s ctypes makes this incredibly easy:

>>> import ctypes
>>> lib = ctypes.CDLL("libdoubler.so")
>>> lib.DoubleIt(21)
42

Ta da!

For me this ability to build normal shared libraries makes Go a first-class language on Unix. In later posts we’ll do a more complex example with C++, and time permitting a SWIG example with Ruby.

July 10, 2015

How memory is allocated

Posted in Software at 16:38 by graham

tl;dr man 2 brk

Last year when I was learning assembler, I was asking myself how to allocate memory without malloc. Usually memory is either allocated for us by our language, or we do it with new or malloc. But malloc is a library function, it’s not a system call. How does malloc itself get memory from the kernel? To answer that we need to look at the layout of a program in memory.

On Linux amd64, every process gets it’s own 128 Tb virtual address space. The program code, global data, debugging information and so on are loaded at the bottom of that space, working ‘upwards’ (bigger numeric addresses). Then comes the heap, where we are going to allocate some memory. Where the heap ends is called the program break. Then there is a very large gap, which the heap will grow into. At the top of the address space (0x7fffffffffff) is the stack, which will grow downwards, back towards the top of the heap. Here is a graphic of virtual memory layout

To allocate memory on the heap, we simply ask the kernel to move the program break up. The space between old program break and new program break is our memory. The system call is brk. First we have to find out where it is now. brk returns the current position, so we simply have to call it. We pass it 0, which is an invalid value, so that it doesn’t change anything.

    mov $12, %rax   # brk syscall number
    mov $0, %rdi    # 0 is invalid, want to get current position
    syscall

When that returns, the current position is in rax. Let’s allocate 4 bytes, by asking the kernel to move our break up by four bytes:

    mov %rax, %rsi  # save current break

    mov %rax, %rdi  # move top of heap to here ...
    add $4, %rdi    # .. plus 4 bytes we allocate
    mov $12, %rax   # brk, again
    syscall

We can now store anything we want at the address pointed at by rsi, where we saved the start of our allocated space. Here is a full assembly program which puts “HI\n” into that space, and prints it out. alloc.s. Compile, link, run:

as -o alloc.o alloc.s
ld -o alloc alloc.o
./alloc

To free memory, you do the opposite, you move the break back down. That allows the kernel to re-use that space. Happy allocating!