A brief overview of the Go programming language and how it might be used to build a simple customisable virtual machine. This is a reduced and updated version of my previous Go virtual machine talks with many code examples.
2. portrait of an artist...
physics major
embedded controllers
software reliability
dynamic languages
network scaling
questionable taste in music
http://github.com/feyeleanor
Eleanor McHugh
Thursday, 30 May 13
5. agnostic
no blessed programming languages
flexible platform abstractions
write once, run everywhere it matters
Thursday, 30 May 13
6. heterogeneous
a system comprises many components
components may differ in purpose and design
but they cooperate to solve problems
Thursday, 30 May 13
7. networks
machines cooperate by sending messages
machine states can be serialised as messages
messages transcend process and host boundaries
Thursday, 30 May 13
9. virtual machine
emulate an existing system
simulate an imagined system
have dynamic control of system state
Thursday, 30 May 13
10. stack-based lambda calculus processor
threaded value cells in a von neumann memory
all operations are expressions are values
lisp
Thursday, 30 May 13
11. stack-based processor
threaded word definitions in a von neumann memory
words thread primitives, word pointers and data
forth
Thursday, 30 May 13
12. stack-based processor with instruction set
harvard memory separates code and data
class loaders convert bytecode to machine state
jvm
Thursday, 30 May 13
13. linux kernel hypervisor
intel X86 virtualisation with hardware execution
QEMU virtual machine runs in user space
kvm
Thursday, 30 May 13
32. package adder
import "testing"
func TestFAdder(t *testing.T) {
error := "Result %v != %v"
f := FAdder{0.0, 1.0, 2.0}
f.Add(1)
if f.Result() != 1.0 { t.Fatalf(error, f.Result(), 1.0) }
f.Subtract(2)
if i.Result() != -1.0 { t.Fatalf(error, i.Result()), -1.0 }
var r Calculator = FAdder{-1.0, 1.0, 2.0}
for n, v := range r.(FAdder) {
if f[n] != v { t.Fatalf("Adder %v should be %v", f, r) }
}
r.Reset()
if r.Result() != *new(float32) {
t.Fatalf(error, r.Result(), *new(float32))
}
}
Thursday, 30 May 13
33. package adder
import "testing"
func TestAddingMachine(t *testing.T) {
error := "Result %v != %v"
a := &AddingMachine{ Adder: FAdder{0.0, 1.0, 2.0} }
a.Add(1)
if f, ok := a.Result().(float32); !ok {
t.Fatal("Result should be a float32")
} else if f != 1.0 {
t.Fatalf(error, a.Result(), 1.0)
}
a.Subtract(2)
if a.Result().(float32) != -1.0 { t.Fatalf(error, a.Result(), -1.0) }
r := FAdder{-1.0, 1.0, 2.0}
for n, v := range a.Adder.(FAdder) {
if r[n] != v { t.Fatalf("Adder %v should be %v", a, r) }
}
}
Thursday, 30 May 13
36. package main
import "fmt"
func main() {
var c chan int
c = make(chan int)
go func() {
for {
fmt.Print(<-c)
}
}()
for {
select {
case c <- 0:
case c <- 1:
}
}
} produces:
01100111010110...
Thursday, 30 May 13
37. package main
import "fmt"
func main() {
var c chan int
c = make(chan int, 16)
go func() {
for {
fmt.Print(<-c)
}
}()
go func() {
select {
case c <- 0:
case c <- 1:
}
}()
for {}
}
produces:
01100111010110...
Thursday, 30 May 13
38. package map_reduce
type SignalSource func(status chan bool)
func Wait(s SignalSource) {
done := make(chan bool)
defer close(done)
go s(done)
<-done
}
func WaitCount(count int, s SignalSource) {
done := make(chan bool)
defer close(done)
go s(done)
for i := 0; i < count; i++ {
<- done
}
}
Thursday, 30 May 13
39. package map_reduce
type Iteration func(k, x interface{})
func (i Iteration) apply(k, v interface{}, c chan bool) {
go func() {
i(k, v)
c <- true
}()
}
Thursday, 30 May 13
40. package map_reduce
func Each(c interface{}, f Iteration) {
switch c := c.(type) {
case []int: WaitCount(len(c), func(done chan bool) {
for i, v := range c {
f.apply(i, v, done)
}
})
case map[int] int: WaitCount(len(c), func(done chan bool) {
for k, v := range c {
f.apply(k, v, done)
}
})
}
}
Thursday, 30 May 13
41. package map_reduce
type Results chan interface{}
type Combination func(x, y interface{}) interface{}
func (f Combination) Reduce(c, s interface{}) (r Results) {
r = make(Results)
go func() {
Each(c, func(k, x interface{}) {
s = f(s, x)
})
r <- s
}()
return
}
Thursday, 30 May 13
43. func Map(c interface{}, t Transformation) (n interface{}) {
var i Iteration
switch c := c.(type) {
case []int: m := make([]int, len(c))
i = func(k, x interface{}) { m[k] = t.GetValue(x) }
n = m
case map[int] int: m := make(map[int] int)
i = func(k, x interface{}) { m[k] = t.GetValue(x) }
n = m
}
if i != nil {
Wait(func(done chan bool) {
Each(c, i)
done <- true
})
}
return
}
Thursday, 30 May 13
44. package main
import "fmt"
import . "map_reduce"
func main() {
m := "%v = %v, sum = %vn"
s := []int{0, 1, 2, 3, 4, 5}
sum := func(x, y interface{}) interface{} { return x.(int) + y.(int) }
d := Map(s, func(x interface{}) interface{} { return x.(int) * 2 })
x := <- Combination(sum).Reduce(s, 0)
fmt.Printf("s", s, x)
x = <- Combination(sum).Reduce(d, 0)
fmt.Printf("d", d, x)
}
produces:
s = [0 1 2 3 4 5], sum = 15
c = [0 2 4 6 8 10], sum = 30
Thursday, 30 May 13
48. package clock
import "syscall"
func (c *Clock) Start() {
if !c.active {
go func() {
c.active = true
for i := int64(0); ; i++ {
select {
case c.active = <- c.Control:
default:
if c.active {
c.Count <- i
}
syscall.Sleep(c.Period)
}
}
}()
}
}
Thursday, 30 May 13
49. package main
import . "clock"
func main() {
c := Clock{1000, make(chan int64), make(chan bool), false}
c.Start()
for i := 0; i < 3; i++ {
println("pulse value", <-c.Count, "from clock")
}
println("disabling clock")
c.Control <- false
syscall.Sleep(1000000)
println("restarting clock")
c.Control <- true
println("pulse value", <-c.Count, "from clock")
}
Thursday, 30 May 13
50. OSX 10.6.2 Intel Atom 270 @ 1.6GHz:
pulse value 0 from clock
pulse value 1 from clock
pulse value 2 from clock
disabling clock
restarting clock
pulse value 106 from clock
OSX 10.6.7 Intel Core 2 Duo @ 2.4GHz:
pulse value 0 from clock
pulse value 1 from clock
pulse value 2 from clock
disabling clock
restarting clock
pulse value 154 from clock
Thursday, 30 May 13
53. package instructions
import "fmt"
type Operation func(o []int)
type Executable interface {
Opcode() int
Operands() []int
Execute(op Operation)
}
const INVALID_OPCODE = -1
type Program []Executable
func (p Program) Disassemble(a Assembler) {
for _, v := range p {
fmt.Println(a.Disassemble(v))
}
}
Thursday, 30 May 13
54. package instructions
type Instruction []int
func (i Instruction) Opcode() int {
if len(i) == 0 {
return INVALID_OPCODE
}
return i[0]
}
func (i Instruction) Operands() []int {
if len(i) < 2 {
return []int{}
}
return i[1:]
}
func (i Instruction) Execute(op Operation) {
op(i.Operands())
}
Thursday, 30 May 13
55. package instructions
type Assembler struct {
opcodes map[string] int
names map[int] string
}
func NewAssember(names... string) (a Assembler) {
a = Assembler{
opcodes: make(map[string] int),
names:make(map[int] string),
}
a.Define(names...)
return
}
func (a Assembler) Define(names... string) {
for _, name := range names {
a.opcodes[name] = len(a.names)
a.names[len(a.names)] = name
}
}
Thursday, 30 May 13
56. package instructions
func (a Assembler) Assemble(name string, params... int) (i Instruction) {
i = make(Instruction, len(params) + 1)
if opcode, ok := a.opcodes[name]; ok {
i[0] = opcode
} else {
i[0] = INVALID_OPCODE
}
copy(i[1:], params)
return
}
Thursday, 30 May 13
57. package instructions
import "fmt"
func (a Assembler) Disassemble(e Executable) (s string) {
if name, ok := a.names[e.Opcode()]; ok {
s = name
if params := e.Operands(); len(params) > 0 {
s = fmt.Sprintf("%vt%v", s, params[0])
for _, v := range params[1:] {
s = fmt.Sprintf("%v, %v", s, v)
}
}
} else {
s = "unknown"
}
return
}
Thursday, 30 May 13
58. package main
import . "instructions"
func main() {
a := NewAssembler("noop", "load", "store")
p := Program{ a.Assemble("noop"),
a.Assemble("load", 1),
a.Assemble("store", 1, 2),
a.Assemble("invalid", 3, 4, 5) }
p.Disassemble(a)
for _, v := range p {
if len(v.Operands()) == 2 {
v.Execute(func(o []int) {
o[0] += o[1]
})
println("op =", v.Opcode(), "result =", v.Operands()[0])
}
}
}
Thursday, 30 May 13