Last active
August 27, 2015 21:38
-
-
Save jimmidyson/81448529975bc5466bb4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# go test -bench=. -benchmem -benchtime=30s | |
testing: warning: no tests to run | |
PASS | |
BenchmarkSetNs 100000 464509 ns/op 14876 B/op 163 allocs/op | |
BenchmarkSysFs 100000 459523 ns/op 14735 B/op 157 allocs/op | |
ok github.com/jimmidyson/benchmark-setns 122.377s |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package benchmarksetns | |
import ( | |
"bufio" | |
"fmt" | |
"log" | |
"os/exec" | |
"regexp" | |
"runtime" | |
"strconv" | |
"strings" | |
"testing" | |
"github.com/fsouza/go-dockerclient" | |
"github.com/vishvananda/netns" | |
) | |
const ( | |
image = "kubernetes/pause" | |
netStatsFile = "/proc/net/dev" | |
) | |
var ( | |
client, _ = docker.NewClient("unix:///var/run/docker.sock") | |
ignoredDevicePrefixes = []string{"lo", "veth", "docker"} | |
re = regexp.MustCompile("[ ]*(.+):([ ]+[0-9]+){16}") | |
stats []interfaceStats | |
) | |
type interfaceStats struct { | |
// The name of the interface. | |
Name string `json:"name"` | |
// Cumulative count of bytes received. | |
RxBytes uint64 `json:"rx_bytes"` | |
// Cumulative count of packets received. | |
RxPackets uint64 `json:"rx_packets"` | |
// Cumulative count of receive errors encountered. | |
RxErrors uint64 `json:"rx_errors"` | |
// Cumulative count of packets dropped while receiving. | |
RxDropped uint64 `json:"rx_dropped"` | |
// Cumulative count of bytes transmitted. | |
TxBytes uint64 `json:"tx_bytes"` | |
// Cumulative count of packets transmitted. | |
TxPackets uint64 `json:"tx_packets"` | |
// Cumulative count of transmit errors encountered. | |
TxErrors uint64 `json:"tx_errors"` | |
// Cumulative count of packets dropped while transmitting. | |
TxDropped uint64 `json:"tx_dropped"` | |
} | |
func startContainer() *docker.Container { | |
img := docker.PullImageOptions{ | |
Repository: "kubernetes/pause", | |
} | |
if err := client.PullImage(img, docker.AuthConfiguration{}); err != nil { | |
log.Fatal("Can't pull image:", err) | |
} | |
cfg := docker.Config{Image: "kubernetes/pause"} | |
createOpts := docker.CreateContainerOptions{Config: &cfg} | |
cntr, err := client.CreateContainer(createOpts) | |
if err != nil { | |
log.Fatal("Can't create container:", err) | |
} | |
if err := client.StartContainer(cntr.ID, &docker.HostConfig{}); err != nil { | |
log.Fatal("Can't start container:", err) | |
} | |
cntr, err = client.InspectContainer(cntr.ID) | |
if err != nil { | |
log.Fatal("Can't inspect container:", err) | |
} | |
return cntr | |
} | |
func removeContainer(cntr *docker.Container) { | |
removeOpts := docker.RemoveContainerOptions{ | |
ID: cntr.ID, | |
Force: true, | |
} | |
err := client.RemoveContainer(removeOpts) | |
if err != nil { | |
log.Fatal("Can't remove container:", err) | |
} | |
} | |
func BenchmarkSetNs(b *testing.B) { | |
cntr := startContainer() | |
defer removeContainer(cntr) | |
stats = []interfaceStats{} | |
b.ResetTimer() | |
for n := 0; n < b.N; n++ { | |
stats = statsFromNs(cntr.State.Pid) | |
} | |
b.StopTimer() | |
} | |
func BenchmarkSysFs(b *testing.B) { | |
cntr := startContainer() | |
defer removeContainer(cntr) | |
stats = []interfaceStats{} | |
b.ResetTimer() | |
for n := 0; n < b.N; n++ { | |
stats = statsFromSysFs(cntr.State.Pid) | |
} | |
b.StopTimer() | |
} | |
func isIgnoredDevice(ifName string) bool { | |
for _, prefix := range ignoredDevicePrefixes { | |
if strings.HasPrefix(strings.ToLower(ifName), prefix) { | |
return true | |
} | |
} | |
return false | |
} | |
func scanInterfaceStats(file string) []interfaceStats { | |
var ( | |
bkt uint64 | |
) | |
// For some reason ioutil.ReadFile(netStatsFile) reads the file in | |
// the default netns when this code is invoked from docker. | |
// Executing "cat <netStatsFile>" works as expected. | |
data, err := exec.Command("cat", file).Output() | |
if err != nil { | |
log.Fatal("Can't cat net stats file:", err) | |
} | |
reader := strings.NewReader(string(data)) | |
scanner := bufio.NewScanner(reader) | |
scanner.Split(bufio.ScanLines) | |
stats := []interfaceStats{} | |
for scanner.Scan() { | |
line := scanner.Text() | |
if re.MatchString(line) { | |
line = strings.Replace(line, ":", "", -1) | |
i := interfaceStats{} | |
_, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", | |
&i.Name, &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped, &bkt, &bkt, &bkt, | |
&bkt, &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped, &bkt, &bkt, &bkt, &bkt) | |
if err != nil { | |
log.Fatal("Can't scan net stats:", err) | |
} | |
if !isIgnoredDevice(i.Name) { | |
stats = append(stats, i) | |
} | |
} | |
} | |
return stats | |
} | |
func statsFromNs(pid int) []interfaceStats { | |
// Lock the OS Thread so we only change the ns for this thread exclusively | |
runtime.LockOSThread() | |
defer runtime.UnlockOSThread() | |
// Save the current network namespace | |
origns, _ := netns.Get() | |
defer origns.Close() | |
// Switch to the pid netns | |
pidns, err := netns.GetFromPid(pid) | |
defer pidns.Close() | |
if err != nil { | |
log.Fatal("Can't get container pid ns", err) | |
} | |
netns.Set(pidns) | |
// Defer setting back to original ns | |
defer netns.Set(origns) | |
stats := scanInterfaceStats(netStatsFile) | |
return stats | |
} | |
func statsFromSysFs(pid int) []interfaceStats { | |
stats := scanInterfaceStats("/proc/" + strconv.Itoa(pid) + "/net/dev") | |
return stats | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment