The Goop (Go Object-Oriented Programming) package provides support for dynamic object-oriented programming constructs in Go, much like those that appear in various scripting languages. The goal is to integrate fast, native-Go objects and slower but more flexible Goop objects within the same program.
Goop provides the following features, which are borrowed from an assortment of object-oriented programming languages:
support for both ex nihilo and constructor-based object creation
the ability to add, replace, and remove data fields and method functions at will
dynamically modifiable inheritance hierarchies (even on a per-object basis)
type-dependent dispatch (i.e., multiple methods with the same name but different argument types)
Install Goop with
go get github.com/lanl/goop
Once you install Goop, you can view the API locally with
godoc, for example by running
to display the Goop documentation on screen or by running
to start a local Web server then viewing the HTML-formatted documentation at http://localhost:6060/pkg/github.com/lanl/goop/ in your favorite browser.
Goop is unfortunately extremely slow. Goop programs have to pay for their flexibility in terms of performance. To determine just how bad the performance is on your computer, you can run the microbenchmarks included in
go test -bench=. -benchtime=5s github.com/lanl/goop
On my computer, I get results like the following (reformatted for clarity):
BenchmarkNativeFNV1 2000000000 4.59 ns/op BenchmarkNativeFNV1Closure 2000000000 4.59 ns/op BenchmarkGoopFNV1 100000000 70.5 ns/op BenchmarkMoreGoopFNV1 10000000 794 ns/op BenchmarkEvenMoreGoopFNV1 5000000 2517 ns/op
goop_test.go for the complete source code for those benchmarks. Basically,
BenchmarkNativeFNV1is native (i.e., non-Goop) Go code for computing a 64-bit FNV-1 hash on a sequence of 0xFF bytes. Each iteration (“op” in the performance results) comprises a nullary function call, a multiplication by a large prime number, and an exclusive or with an 0xFF byte.
BenchmarkNativeFNV1Closureis the same but instead of calling an ordinary function each iteration, it invokes a locally defined closure.
BenchmarkGoopFNV1defines a Goop object that contains a single data field (the current hash value) and no methods. Each iteration performs one
Seton the Goop object.
BenchmarkMoreGoopFNV1replaces the hash function with an object method. Hence, each iteration performs one
Set, and one
Callon the Goop object.
BenchmarkEvenMoreGoopFNV1adds support for type-dependent dispatch to the hash-function method. Although only one type signature is defined, Goop has to confirm at run time that the provided arguments do in fact match that signature.
Another way to interpret the data shown above is that, on my computer at least, a function closure is essentially free;
Set cost approximately 33 ns apiece; a
Call of a nullary function costs about 724 ns; and type-dependent dispatch costs an additional 1723 ns.
How does Goop compare to various scripting languages? Not well, at least for
BenchmarkMoreGoopFNV1 and its equivalents in other languages. The following table shows the cost in nanoseconds of an individual
BenchmarkMoreGoopFNV1 operation (a function call, a read of a data field, a 64-bit multiply, an 8-bit exclusive or, and a write to a data field):
|Language||Run time (ns/op)|
|[Incr Tcl] 8.6.0||24490|
|Go 1.3.1 + Goop||794|
|Python 2.7.3 + PyPy||206|
In short, you’ll want to do most of your coding in native Go and use Goop only when your application requires the extra flexibility that Goop provides. Then, you should cache as many object members as possible in Go variables to reduce the number of
Goop is provided under a BSD-ish license with a “modifications must be indicated” clause. See http://github.com/lanl/goop/blob/master/LICENSE.md for the full text.
Triad National Security, LLC (Triad) owns the copyright to Goop, a component of the LANL Go Suite (identified internally as LA-CC-11-056).
Scott Pakin, [email protected]