Skip to content

Commit

Permalink
bonniego → gobonniego
Browse files Browse the repository at this point in the history
Per Tim Bray's suggestion:

> maybe "GoBonnieGo"?
  • Loading branch information
cunnie committed Feb 15, 2018
1 parent 6c6814a commit f7750b3
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 51 deletions.
95 changes: 53 additions & 42 deletions README.md
@@ -1,6 +1,6 @@
## `bonniego`
## Go Bonnie Go!

`bonniego` is a _minimal_ implementation of Tim Bray's
`gobonniego` is a _minimal_ implementation of Tim Bray's
[bonnie](https://code.google.com/p/bonnie-64/) written in Go (*bonnie* is
written in C).

Expand All @@ -12,29 +12,29 @@ It presents three disk metrics:
2. Sequential Read (higher is better)
3. IOPS (I/O Operations per Second) (higher is better)

## Getting `bonniego`
## Getting `gobonniego`

Easiest way to get `bonniego` is to download the pre-built binaries on the
[Releases](https://github.com/cunnie/bonniego/releases/). In the following
Easiest way to get `gobonniego` is to download the pre-built binaries on the
[Releases](https://github.com/cunnie/gobonniego/releases/). In the following
example, we are logged into a Linux box and we download and run the Linux
binary:

```
curl -o bonniego -L https://github.com/cunnie/bonniego/releases/download/1.0.0/bonniego-linux-amd64
chmod +x bonniego
./bonniego
curl -o gobonniego -L https://github.com/cunnie/gobonniego/releases/download/1.0.1/gobonniego-linux-amd64
chmod +x gobonniego
./gobonniego
```

Alternatively, you can build `bonniego` from source.
Alternatively, you can build `gobonniego` from source.
[Here](https://gobyexample.com/command-line-arguments) is a good place to
start.

## Examples

`bonniego` can be invoked without parameters; its defaults are reasonable.
`gobonniego` can be invoked without parameters; its defaults are reasonable.

```
bonniego
gobonniego
```

Typical output:
Expand All @@ -49,13 +49,13 @@ Running with the verbose option will print additional timestamped information
to STDERR:

```
bonniego -v
gobonniego -v
```

Yields:

```
2018/02/12 08:05:02 Bonnie working directory: /var/folders/zp/vmj1nyzj6p567k5syt3hvq3h0000gn/T/bonniegoParent649139571/bonniego
2018/02/12 08:05:02 Bonnie working directory: /var/folders/zp/vmj1nyzj6p567k5syt3hvq3h0000gn/T/gobonniegoParent649139571/gobonniego
2018/02/12 08:05:02 Number of concurrent processes: 4
2018/02/12 08:05:02 Total System RAM (MiB): 16384
2018/02/12 08:05:03 Written (MiB): 1024
Expand All @@ -69,108 +69,108 @@ Sequential Read MiB/s: 6729.41
IOPS: 26156
```

You can tell `bonniego` where to place its test files. This is useful if the
You can tell `gobonniego` where to place its test files. This is useful if the
default filesystem is too small or if you want to test a specific disk.
`bonniego` will clean up after itself, and will not delete the directory it's
`gobonniego` will clean up after itself, and will not delete the directory it's
told to run in (you can safely specify `/tmp` or `/` as the directory). Here
are some examples:

```
bonniego -dir D:\
bonniego -dir /tmp
bonniego -dir /zfs/tank
gobonniego -dir D:\
gobonniego -dir /tmp
gobonniego -dir /zfs/tank
```

You may specify the number of threads (Goroutines) to run with the `-procs`
flag. In this example, we spawn 8 threads:

```
bonniego -procs 8
gobonniego -procs 8
```

`-version` will display the current version of `bonniego`:
`-version` will display the current version of `gobonniego`:

```
bonniego -version
gobonniego -version
```

Yields:

```
bonniego version 1.0.0
gobonniego version 1.0.0
```

`bonniego -h` will print out the available command line options and their
`gobonniego -h` will print out the available command line options and their
current default values:

```
Usage of ./bonniego:
Usage of ./gobonniego:
-dir string
The directory in which bonniego places its temp files, should have at least twice system RAM available (default "/tmp/bonniegoParent139558072")
The directory in which gobonniego places its temp files, should have at least twice system RAM available (default "/tmp/gobonniegoParent139558072")
-procs int
The number of concurrent readers/writers, defaults to the number of CPU cores (default 8)
-v Verbose. Will print to stderr diagnostic information such as the amount of RAM, number of cores, etc.
-version
Version. Will print the current version of bonniego and then exit
Version. Will print the current version of gobonniego and then exit
```

## Technical Notes

`bonniego` detects the number of CPU cores and the amount of RAM.
`gobonniego` detects the number of CPU cores and the amount of RAM.

The number of cores may not match the number of physical cores. For example, an
Intel core i5 with two physical cores and hyperthreading is detected as 4
cores.

`bonniego` spawns one thread for each core unless overridden by the `-procs`
`gobonniego` spawns one thread for each core unless overridden by the `-procs`
flag.

`bonniego` writes twice the amount of RAM. For example, on a system with 16
GiB of RAM, `bonniego` would write 32 GiB of data. This is to reduce the effect
`gobonniego` writes twice the amount of RAM. For example, on a system with 16
GiB of RAM, `gobonniego` would write 32 GiB of data. This is to reduce the effect
of the [buffer cache](http://www.tldp.org/LDP/sag/html/buffer-cache.html),
which may give misleadingly good results.

`bonniego` divides the total amount to write by the number of threads. For
`gobonniego` divides the total amount to write by the number of threads. For
example, a 4-core system with 8 GiB of RAM would have four threads each of
which would concurrently write 4 GiB of data for a total of 16 GiB.

`bonniego` writes with buffered I/O; however, it waits for
`gobonniego` writes with buffered I/O; however, it waits for
[`bufio.Flush()`](https://golang.org/pkg/bufio/#Writer.Flush) to complete
before recording the duration.

`bonniego` creates a 64 kiB chunk of random data which it writes in succession
`gobonniego` creates a 64 kiB chunk of random data which it writes in succession
to disk. It's random in order to avoid inflating the results for filesystems
which enable compression (e.g. ZFS).

`bonniego` reads the files concurrently in 64 kiB chunks. Every 127 chunks it
`gobonniego` reads the files concurrently in 64 kiB chunks. Every 127 chunks it
does a byte comparison against the original random data 64 kiB chunk to make
sure there has been no corruption. This probably exacts a small penalty in
read performance.

For IOPS measurement, a `bonniego` thread seeks to a random position in the
For IOPS measurement, a `gobonniego` thread seeks to a random position in the
file and reads 512 bytes. This counts as a single operation. Every tenth seek
instead of reading it will write 512 bytes of data. This also counts as an
operation. The ratio of reads:writes is 10:1, in order to approximate the ratio
that the TPC-E benchmark uses
(<http://www.cs.cmu.edu/~chensm/papers/TPCE-sigmod-record10.pdf>).

`bonniego` uses [`ioutil.TempDir()`](https://golang.org/pkg/io/ioutil/#TempDir)
`gobonniego` uses [`ioutil.TempDir()`](https://golang.org/pkg/io/ioutil/#TempDir)
to create the temporary directory in which to place its files, unless
overridden by the `-dir` flag. On Linux systems this temporary directory is
often `/tmp/`, on macOS systems, `/var/folders/...`.

## Bugs

If `bonniego` fills up the filesystem, it will crash and you will need to find
& delete the `bonniego` files manually. Below is a sample `find` command to
locate the `bonniego` directory; delete that directory and everything
If `gobonniego` fills up the filesystem, it will crash and you will need to find
& delete the `gobonniego` files manually. Below is a sample `find` command to
locate the `gobonniego` directory; delete that directory and everything
underneath:

```
find / -name bonniegoParent\*
find / -name gobonniegoParent\*
```

`bonniego` doesn't work on FreeBSD yet; it depends on the
`gobonniego` doesn't work on FreeBSD yet; it depends on the
[gosigar](https://github.com/cloudfoundry/gosigar) library which doesn't have
the FreeBSD implementations for the calls that the determine the number of CPU
cores and the amount of system RAM.
Expand All @@ -190,9 +190,20 @@ it. And credit must be given to Brendan Gregg's excellent post, _[Active
Benchmarking:
Bonnie++](http://www.brendangregg.com/ActiveBenchmarking/bonnie++.html)_.


### Name

Tim Bray suggested the name, _gobonniego_:

> maybe "GoBonnieGo"?
It's a reference to the refrain of Chuck Berry's song, [Johnny B.
Goode](https://en.wikipedia.org/wiki/Johnny_B._Goode), which repeats the
phrase, "Go Johnny go"

### Impetus

The impetus for writing `bonniego` is to provide concurrency. During a benchmark
The impetus for writing `gobonniego` is to provide concurrency. During a benchmark
of a ZFS filesystem (using *bonnie++*), it became clear that a
the single-threaded performance of *bonnie++* and not disk
speed was the limiting factor.
4 changes: 2 additions & 2 deletions bin/make_all
Expand Up @@ -5,10 +5,10 @@
export GOOS GOARCH
for GOOS in darwin linux; do
for GOARCH in amd64; do
go build -o $GOPATH/bin/bonniego-$GOOS-$GOARCH bonniego.go
go build -o $GOPATH/bin/gobonniego-$GOOS-$GOARCH gobonniego.go
done
done

# Windows has a custom extension
GOOS=windows GOARCH=amd64
go build -o $GOPATH/bin/bonniego-$GOOS-$GOARCH.exe bonniego.go
go build -o $GOPATH/bin/gobonniego-$GOOS-$GOARCH.exe gobonniego.go
14 changes: 7 additions & 7 deletions bonniego.go → gobonniego.go
Expand Up @@ -17,29 +17,29 @@ import (
"time"
)

const Version = "1.0.0"
const Version = "1.0.1"
const Blocksize = 0x1 << 16 // 65,536 bytes, 2^16 bytes

func main() {
var bonnieTempDir, bonnieParentDir, bonnieDir string
var numProcs int
var verbose, version bool
bonnieTempDir, err := ioutil.TempDir("", "bonniegoParent")
bonnieTempDir, err := ioutil.TempDir("", "gobonniegoParent")
check(err)
defer os.RemoveAll(bonnieTempDir)

flag.BoolVar(&verbose, "v", false, "Verbose. Will print to stderr diagnostic information such as the amount of RAM, number of cores, etc.")
flag.BoolVar(&version, "version", false, "Version. Will print the current version of bonniego and then exit")
flag.BoolVar(&version, "version", false, "Version. Will print the current version of gobonniego and then exit")
flag.IntVar(&numProcs, "procs", runtime.NumCPU(), "The number of concurrent readers/writers, defaults to the number of CPU cores")
flag.StringVar(&bonnieParentDir, "dir", bonnieTempDir, "The directory in which bonniego places its temp files, should have at least twice system RAM available")
flag.StringVar(&bonnieParentDir, "dir", bonnieTempDir, "The directory in which gobonniego places its temp files, should have at least twice system RAM available")
flag.Parse()

if version {
fmt.Printf("bonniego version %s\n", Version)
fmt.Printf("gobonniego version %s\n", Version)
os.Exit(0)
}

// if bonnieParentDir exists, e.g. "/tmp", all is good, but if it doesn't, e.g. "/tmp/bonniego_run_five", then create it
// if bonnieParentDir exists, e.g. "/tmp", all is good, but if it doesn't, e.g. "/tmp/gobonniego_run_five", then create it
fileInfo, err := os.Stat(bonnieParentDir)
if err != nil {
err = os.Mkdir(bonnieParentDir, 0755)
Expand All @@ -50,7 +50,7 @@ func main() {
panic(fmt.Sprintf("'%s' is not a directory!", bonnieParentDir))
}

bonnieDir = path.Join(bonnieParentDir, "bonniego")
bonnieDir = path.Join(bonnieParentDir, "gobonniego")
err = os.Mkdir(bonnieDir, 0755)
check(err)
defer os.RemoveAll(bonnieDir)
Expand Down

0 comments on commit f7750b3

Please sign in to comment.