Saturday, May 18, 2013

Visualizing Go Benchmarks with benchviz

It's often useful to compare benchmark results between Go releases or your own programs. A new tool, benchviz, written in Go with the SVGo package, makes SVG visualizations designed to help you spot regressions and speedups and their magnitudes quickly.

Benchviz reads data from the benchcmp command, found in $GOROOT/misc/benchcmp, which produces output like this:

$ benchcmp old new
benchmark                 old ns/op    new ns/op    delta
BenchmarkBinaryTree17  131488202467 112637283111  -14.34%
BenchmarkFannkuch11     61976254131  61972329989   -0.01%
BenchmarkGobDecode        424145307    383073401   -9.68%
BenchmarkGobEncode        115032849    120332484   +4.61%
BenchmarkGzip           13868472766  13493855517   -2.70%

benchmark                  old MB/s     new MB/s  speedup
BenchmarkGobDecode             1.81         2.00    1.10x
BenchmarkGobEncode             6.67         6.38    0.96x
BenchmarkGzip                  1.40         1.44    1.03x
BenchmarkGunzip               11.23        11.55    1.03x

The benchcmp command produces two kinds of comparisons. "delta-style" shows the percent change between the measurements (negative numbers for going faster, positive numbers for slowdowns). The "speedup-style" shows how many times faster or slower the benchmarks are. Measurements less than 1.0 are considered performance regressions.

Benchviz supports two styles: the "bar" style draws a barchart-like view; regressions point to the left and speedups go to the right. The "inline" view shows portportionally-sized bars as "highlights" over the benchmark names. In both cases, colors indicate speedups or regressions.

If you add lines to the benchcmp output beginning with '#', benchviz will display these comment lines appropriately

Bars and inline styles

Two styles of speedup benchmarks

To install benchviz:

$ go install github.com/ajstarks/svgo/benchviz

You can run benchviz in a pipeline:

$ benchcmp old new | benchviz > bench.svg

or it can read from files:

$ benchviz linux-amd64-282dcbf1423.txt > linux.svg

Benchviz has options to control layout (overall width and height, top and left margins; the location and size of the bars; maximum speedup and delta) and style (bars or inline, colors, horizontal rules, single column data view)

$ benchviz -?
flag provided but not defined: -?
Usage of benchviz:
  -bh=20: bar height
  -col=false: show data in a single column
  -dm=100: maximum delta
  -h=768: height
  -left=100: left margin
  -line=false: show lines between entries
  -rcolor="red": regression color
  -scolor="green": speedup color
  -sm=10: maximum speedup
  -style="bar": set the style (bar or inline)
  -title="": title
  -top=50: top
  -vp=512: visualization point
  -vw=300: visual area width
  -w=1024: width

Thanks to Dave Cheney and Andrew Gerrand for feedback and encourgement.

Tuesday, September 25, 2012

OpenVG on the Raspberry Pi

The Raspberry Pi, drawn by the Raspberry Pi

The Raspberry Pi includes a GPU-backed OpenVG standard library, making it an excellent platform for graphics programming. This post describes a high-level library written on top of OpenVG that can be used with either C or Go.

Design

The library, hosted at Github, is a thin layer on top of the native OpenVG library (see /opt/vc/lib), adopting its coordinate system (origin at the lower left, x increasing to the left-to-right, y increasing up), and types (floating point coordinates and dimensions).

The library is designed to be small and logical---a programmer should be be able to keep the entire API in their head, and at a glance get a sense of what a client program will do. The usual pattern is to define programs in terms of functions that use high-level graphics objects likes circles, lines, and curves, with little to no barrier between the conception of the design and its programmed realization---the library is designed to get to the pictures quickly, with a minium of ceremony and boilerplate. If a picture can be created with a vector drawing tool, the designer/programmer should be able to create an equivalent (or better) illustration using the library.

Another measure of the API is its ability to program pictures defined in other APIs such as Processing or SVGo.

API

The API is organized in terms of shapes, lines, curves, text, images, attributes, and transformations. (note that the library adopts Go's convention of using upper-case names for "public" functions)

Shapes, lines and curves
Circle(x,y,r) Circle centered at (x,y) with radius r
Ellipse(x,y,w,h) Ellipse centered at (x,y), with radii w, h
Rect(x,y,w,h) Rectangle with lower left at x,y width of w, height of h
Roundrect(x,y,w,h,rw,rh) Rounded rectangle with lower left at (x,y), width (w), height (h), corner radii (rw,rh)
Line(x1,y1, x2,y2) Line between (x1, y1) and (x2, y2)
Polyline(x,y) Polyline with coordinates in (x,y) arrays
Polygon(x,y) Polygon with coordinates in (x,y) arrays
Arc(x,y,w,h,a1,a2) Arc centered at x,y width (w), height (h), between angles a1,a2
Cbezier(bx,by, cx,cy, px,py, ex,ey) Cubic Beziér curve between (bx, by) and (ex, ey), with control points at (cx,cy) and (px,py)
Qbezier(bx,by, cx,cy, ex,ey) Quadratic Beziér curve between (bx, by) and (ex, ey), with the control point at (cx,cy)
Image and Text
Image(x,y,w,h,name) Place the JPEG image file "name", and dimensions of (w,h) at (x,y)
Text(x,y,s,font,size) Place the text in s at (x,y), set in the named font and size
TextMid(x,y,s,font,size) Align the text centered at (x,y), set in the named font and size
TextEnd(x,y,s,font,size) Align the text with its end at (x,y), set in the named font and size
TextWidth(s,font,size) Return the width of a string of text set in the named font and size
SerifTypeface Specifies the built-in serif typeface (Deja Vu Sans)
SansTypeface Specifies the built-in sans-serif typeface (Deja Vu Serif)
MonoTypeface Specifies the built-in monospaced typeface (Deja Vu Sans Mono)
Attributes
Fill(red,green,blue,alpha) Set the fill color specified by the (red,green,blue) triple. Color transparency is defined by alpha
FillLinearGradient(x1,y1,x2,y2,stops,n)Linear gradient fill
FillRadialGradient(cx,cy,fx,fy,r,stops,n)Radial gradient fill
Stroke(red,green,blue,alpha) Set the stroke color
StrokeWidth(w) Set the stroke width
Background(red,green,blue) Set the background color
Transformations
Translate(x,y) Translate the coordinate system to (x,y)
Scale(x,y) Scale the coordinate system by (x,y)
Shear(x,y) Warp the coordinate system by (x,y)
Rotate(r) Rotate the coordinate system around the angle r (degrees)
Structure
init() Graphics initialization
finish() Graphics cleanup
Start(w,h) Begin the picture
End() End the current picture
SaveEnd(filename) Save the raw raster to filename, and empty string saves the raster to the standard output file

Text is rendered with TrueType fonts, using data generated by an included separate program, font2openvg. The library embeds data for sans, serif and monospace fonts in a single weight. Other fonts may be added if needed. The format for saved pictures is a stream of RGBA values, in scanline order, sized to the display. The included Go program raw2png converts the raw raster files to PNG.

Here's a "reference card" for the library, built with itself:

OpenVG refcard

Here is the formal description of the C API

Examples

Every first program displays "hello, world" -- here is the graphics equivalent.

// first OpenVG program
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "VG/openvg.h"
#include "VG/vgu.h"
#include "fontinfo.h"
#include "shapes.h"

int main() {
    int width, height;
    VGfloat w2, h2, w;
    char s[3];

    init(&width, &height);                                      // Graphics initialization

    w2 = (VGfloat)(width/2);
    h2 = (VGfloat)(height/2);
    w  = (VGfloat)w;

    Start(width, height);                                       // Start the picture
    Background(0, 0, 0);                                        // Black background
    Fill(44, 77, 232, 1);                                       // Big blue marble
    Circle(w2, 0, w);                                           // The "world"
    Fill(255, 255, 255, 1);                                     // White text
    TextMid(w2, h2, "hello, world", SerifTypeface, width/10);   // Greetings 
    End();                                                      // End the picture
    fgets(s, 2, stdin);                                         // Pause until RETURN]
    finish();                                                   // Graphics cleanup
    exit(0);
}

hellovg

This example is a function to rotate text:

// rotext draws text, rotated around the center of the screen, progressively faded
void rotext(int w, int h, int n, char *s) {
    VGfloat fade = (100.0 / (VGfloat) n) / 100.0;
    VGfloat deg = 360.0 / n;
    VGfloat x = w / 2, y = h / 2;
    VGfloat alpha = 1.0;    // start solid
    int i, size = w / 8;

    Start(w, h);
    Background(0, 0, 0);
    Translate(x, y);
    for (i = 0; i < n; i++) {
        Fill(255, 255, 255, alpha);
        Text(0, 0, s, SerifTypeface, size);
        alpha -= fade;             // fade
        size += n;                 // enlarge
        Rotate(deg);
    }
    End();
}

rotext

The Go Package

Thanks to the work of the Go community, specifically Dave Cheney and Shenghou Ma (known as minux on the Go mailing lists), Go is a first-class language on the Raspberry Pi -- building a Go version of the library is straightforward using cgo. When building Go initially on your Raspberry Pi, it's useful to decrease the GPU RAM, but once it's built, you will need at least 64MB of GPU RAM to run the programs. Use the raspi-config program to adjust your memory split. While you are there, experiment with the new "Turbo" overclocking mode. Your builds will go faster.

The key to a successful build is respecting the Go toolchain conventions -- the code is built under $GOPATH/src/github.com/ajstarks/openvg, so that during development I can say:

$ cd $GOPATH/src/github.com/ajstarks/openvg
$ go install .
$ cd go-client/shapedemo
$ go run shapedemo.go demo 1

Incidentally, I grew weary of the twitchiness of editing using vi over ssh on my Mac, and using Plan 9 from user space, I can use the text editor sam between the Raspberry Pi and the Mac for much more responsive editing.

Sam editing openvg files on the Raspberry Pi

The Go version includes with a few niceties such as named colors, and in general, where the C library uses arrays, Go uses slices, and where OpenVG uses the VGfloat type the Go package uses float64. Also, the C library only supports JPEG images, but the Go library supports both JPG and PNG image formats, thanks to the standard image library.

A formal description of the Go API

Here is the Go version of the hellovg program:

// first OpenVG program
package main

import (
    "bufio"
    "github.com/ajstarks/openvg"
    "os"
)

func main() {
    width, height := openvg.Init()                            // Graphics initialization

    w2 := float64(width / 2)
    h2 := float64(height / 2)
    w := float64(width)

    openvg.Start(width, height)                               // Start the picture
    openvg.BackgroundColor("black")                           // Black background
    openvg.FillRGB(44, 77, 232, 1)                            // Big blue marble
    openvg.Circle(w2, 0, w)                                   // The "world"
    openvg.FillColor("white")                                 // White text
    openvg.TextMid(w2, h2, "hello, world", "serif", width/10) // Greetings 
    openvg.End()                                              // End the picture
    bufio.NewReader(os.Stdin).ReadBytes('\n')                 // Pause until [RETURN]
    openvg.Finish()                                           // Graphics cleanup
}

The rotext function:

// rotext draws text, rotated around the center of the screen, progressively faded
func rotext(w, h, n int, s string) {
    fade := (100.0 / float64(n)) / 100.0
    deg := 360.0 / float64(n)
    x := float64(w) / 2.0
    y := float64(h) / 2.0
    alpha := 1.0
    size := w / 8

    openvg.Start(w, h)
    openvg.Background(0, 0, 0)
    openvg.Translate(x, y)
    for i := 0; i < n; i++ {
        openvg.FillRGB(255, 255, 255, alpha)
        openvg.Text(0, 0, s, "serif", size)
        alpha -= fade // fade
        size += n     // enlarge
        openvg.Rotate(deg)
    }
    openvg.End()
}

Building and running

You will need at least 64MB of GPU RAM, and the only other dependecy is the JPEG library, install it like this:

$ sudo apt-get install libjpeg8-dev

Makefiles control the building of the C library and its clients.

$ make                             # builds the C and Go libraries
$ cd client  
$ make test                        # builds C clients

$ ./shapedemo                      # show a reference card
$ ./shapedemo advert               # show the library "billboard"
$ ./shapedemo raspi                # show a self-portrait
$ ./shapedemo image                # show test images
$ ./shapedemo astro                # the sun and the earth, to scale
$ ./shapedemo text                 # show blocks of text in serif, sans, and mono fonts
$ ./shapedemo rand 100             # show 100 random shapes
$ ./shapedemo rotate 10 a          # rotated and faded "a"
$ ./shapedemo gradient             # show linear and radial gradient fills
$ ./shapedemo test "hello, world"  # show a test pattern, with "hello, world" at mid-display in sans, serif, and mono.
$ ./shapedemo fontsize             # show a range of font sizes (see Better Products Through Typography)
$ ./shapedemo demo 10              # run through the demos, pausing 10 seconds between each one.

The Go package and clients are built using the go tool: (make sure you are on a recent Go release. As of this writing, the openvg library has been tested on Go 1.1 and 1.1.1

$ go get github.com/ajstarks/openvg
$ go install github.com/ajstarks/openvg/...

The Go clients include:

  • shapedemo -- cycle through several demos (see above)
  • colortab -- named color table
  • raspi -- the Raspberry Pi self-portait
  • hellovg -- hello (graphics) world
  • randcircle -- show random circles

randcircle

Issues and Opportunities

The library is young, and can be improved: the font handling is less than ideal, and Rob Bishop of the Raspberry Pi foundation has pointed out the Raspberry Pi includes a vector font library. The handling of image data between Go and C is not optimal and is the only performance regression between the Go and C versions of the library. Other areas of improvement include better handing of paths (currently the library creates and destroys paths for each graphics object, negatively effecting performance), and adding mouse and keyboard handling. Image handling and scaling can also be improved.

The API is fairly stable, and I'd like to see what kind of programs can be written, letting real-world needs drive API changes (gradient fills are on the to-do list). UPDATE: linear and gradient fills were added on October 2, 2012

Example programs include graphical information/status displays, where portions of the display can be updated concurrently using goroutines, or even presentation software that works from a simplified markup. (try the Go version of the shapedemo with the "loop" argument for a hint of what's possible). The library could also be the basis of a graphics learning environment that is takes advantage of fully accelerated 2D-graphics.

Happily, there is very little Raspberry Pi-specific code, so theoretically the library could be ported to any system that supports OpenVG, useful in future GUI framework and tools.

Have fun programming pictures on your Raspberry Pi

advert

Wednesday, July 11, 2012

AIGA Symbols

AIGA Symbols by ajstarks
AIGA Symbols, a photo by ajstarks on Flickr.

This display of AIGA symbols from The Noun Project was created with the SVGo library and the "nouns" program.

nouns -j -n 10  aiga/*.svg

The program works by loading SVG files from thenounproject, wrapping them in group elements, and then applying random location, scale, color, opacity, and rotation. These properties are define by the type:

type Props struct {
    x, y    int
    c       string
    s, o, r float64
}

Here's the function that does the work:

func Jumble(s *svg.SVG, icons []string, w, h, n int) {
    for c := 0; c < n; c++ {
        for _, i := range icons {
            RandProps(w, h).Apply(s, i)
        }
    }
}

Given a list of icons names, defined by SVG group ids, create and apply random properties bound by a width and height to the ids

Saturday, June 30, 2012

Raspberry Pi and SVGo tools

Raspberry Pi and SVGo tools by ajstarks
Raspberry Pi and SVGo tools, a photo by ajstarks on Flickr.

Here's a screenshot of SVGo tools running on the Raspberry Pi. On the left is tsg (twitter search grid) referring to twitter posts mentioning raspberrypi and on the is left f50 (flickr50) showing flickr photos tagged with raspberrypi.

Both tools produce links to detail -- a tweet and the actual flickr photo.

The Raspberry Pi is using the Midori browser to show the SVG content generated by the tools in the terminal window.

Sunday, April 22, 2012

Tumblr Grid

Attending the inaugural golang ny group at the Tumblr offices, inspired me to add tumblrgrid to the collection of SVGo clients. Tumblrgrid makes a SVG file that displays a flexible, clickable grid of pictures from a set tumblr blogs, possibly filtered with tags. Data may be read from a live network or from a local cache. You can get tumblrgrid with:
$ go get github.com/ajstarks/svgo/tumblrgrid
You will need to edit the source to add your own tumblr API key. The command options are:
 -f=false: read from local files
 -g=5: gutter (pixels)
 -n=30: picture limit
 -nc=5: number of columns
 -p=false: link to original post
 -tag="": filter tag
 -tw=75: thumbnail width
An interesting use of tumblrgrid is to view pictures from related tumblrs. For example, this list:
f0o0od.tumblr.com
ign0ranceisbliiiss.tumblr.com
agilaagira.tumblr.com
geek-art.tumblr.com
represents a chain of tumblrs that refer to each other. After collecting them in a file called tlist, this command line:
$ tumblrgrid -nc 4 -n 20 `cat tlist`
produces:
Changing the command line to
$ tumblrgrid -nc 2 -n 20 `cat tlist`
reduced the number of columns to 2 which causes the labels to be rotated.

Saturday, October 15, 2011

Documenting Code and Pictures

For the SVGo workshop I had to document many examples of code+pictures, so I created a workflow to create consistently formatted illustrations of SVGo code and the pictures they produce. Below is the script that automates the process. The heart of the script is a small Go program, codepic which creates a SVG file of code and picture:
Here's the script:
#!/bin/sh
for i in $*
do
    base=`basename $i .go`
    gofmt -w -spaces -tabindent=false -tabwidth=4 $i &&
    goc $base && 
    ./${base} > ${base}.svg && 
    codepic -codeframe=f -font Inconsolata -fs 14 -ls 16 $i > ${base}-slide.svg && 
    svg2pdf ${base}-slide.svg
done
for each file the script:
  1. formats the source
  2. builds the source
  3. runs the program and captures its output
  4. make a slide showing the code and output
  5. convert the slide to PDF for inclusion in Keynote

Sunday, August 7, 2011

Stock/Product Comparisons with SVGo



The latest visualization tool written with SVGo is stockproduct, a tool to compare stock prices with the release of products over time. This is a Go version of the tool described earlier. Like other SVGo tools, stockproduct reads data from XML (see example below) and produces SVG.

<stockproduct title="Apple Purchases and Stock Price">
<sdata price="12.40" date="2002-04" product="Ti PowerBook" image="tipb.jpg"/>
<sdata price="7.38" date="2002-08" product="Jaguar" image="jaguar.png"/>
<sdata price="261.09" date="2010-04" product="iPad 3G" image="ipad3g.jpg"/>
<sdata price="399.68" date="2011-07" product="Lion" image="lion.png"/>
</stockproduct>

The example above plots my purchases of Apple products vs. the stock price.

Stockproduct scales the graph by the specified size, and the graph can be placed anywhere on the SVG canvas. Note that every chart component: bars, images, product names. prices, scales, and images may be turned on and off using command line options.

This example:

compares the adjusted closing price vs. operating system releases from Microsoft and Apple, ordered by the date of release. Between 1991-2001, both organizations were able to keep pace, with Microsoft releasing Windows 3.1, 95, 98, and Apple releasing System 7, 8, and 9.

However, between 2001 and 2005 Apple steadily released five versions of Mac OS X (Cheetah, Puma, Jaguar, Panther, and Tiger), but in the same time period, Microsoft released only XP. After 2005, Apple released Leopard, Snow Leopard and Lion, where Microsoft released VIsta and Windows 7.

From 1991-2007, there were no large differences in stock prices, but in 2007, the there was large gain for Apple, growing from $184.70 to $386.90, with Microsoft's price staying flat, at $23.75 at the release of Windows 7 in 2007.

In fact the range of prices and releases during past 20 years is Microsoft: $2-23.75, and 6 operating system releases, with Apple's stock price ranging from $4.11 - $386.90, with 11 operating system releases (not including iOS).

Here's the same data with Apple products on the left, peaking at Lion, and Microsoft products on the right: