The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3768
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === This moves the `lxd-benchmark` logic to a separate package, and refactors a bit the code to make it reusable. The intent is to use be able to reuse the logic in a separate tool for performance testing.
From 2dfef81b8a04a2a8aa61ac4565757c04d77d5453 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Tue, 5 Sep 2017 12:02:45 +0200 Subject: [PATCH 1/7] benchmark: extract logic to separate package Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- benchmark/benchmark.go | 327 +++++++++++++++++++++++++++++++++++++++++++++ test/lxd-benchmark/main.go | 324 +------------------------------------------- 2 files changed, 330 insertions(+), 321 deletions(-) create mode 100644 benchmark/benchmark.go diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go new file mode 100644 index 000000000..e8fe5e8f7 --- /dev/null +++ b/benchmark/benchmark.go @@ -0,0 +1,327 @@ +package benchmark + +import ( + "fmt" + "io/ioutil" + "strings" + "sync" + "time" + + "github.com/lxc/lxd/client" + "github.com/lxc/lxd/lxc/config" + "github.com/lxc/lxd/shared/api" + "github.com/lxc/lxd/shared/version" +) + +func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, freeze bool) error { + batch := parallel + if batch < 1 { + // Detect the number of parallel actions + cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices") + if err != nil { + return err + } + + batch = len(cpus) + } + + batches := count / batch + remainder := count % batch + + // Print the test header + st, _, err := c.GetServer() + if err != nil { + return err + } + + privilegedStr := "unprivileged" + if privileged { + privilegedStr = "privileged" + } + + mode := "normal startup" + if freeze { + mode = "start and freeze" + } + + fmt.Printf("Test environment:\n") + fmt.Printf(" Server backend: %s\n", st.Environment.Server) + fmt.Printf(" Server version: %s\n", st.Environment.ServerVersion) + fmt.Printf(" Kernel: %s\n", st.Environment.Kernel) + fmt.Printf(" Kernel architecture: %s\n", st.Environment.KernelArchitecture) + fmt.Printf(" Kernel version: %s\n", st.Environment.KernelVersion) + fmt.Printf(" Storage backend: %s\n", st.Environment.Storage) + fmt.Printf(" Storage version: %s\n", st.Environment.StorageVersion) + fmt.Printf(" Container backend: %s\n", st.Environment.Driver) + fmt.Printf(" Container version: %s\n", st.Environment.DriverVersion) + fmt.Printf("\n") + fmt.Printf("Test variables:\n") + fmt.Printf(" Container count: %d\n", count) + fmt.Printf(" Container mode: %s\n", privilegedStr) + fmt.Printf(" Startup mode: %s\n", mode) + fmt.Printf(" Image: %s\n", image) + fmt.Printf(" Batches: %d\n", batches) + fmt.Printf(" Batch size: %d\n", batch) + fmt.Printf(" Remainder: %d\n", remainder) + fmt.Printf("\n") + + // Pre-load the image + var fingerprint string + if strings.Contains(image, ":") { + var remote string + + defaultConfig := config.DefaultConfig + defaultConfig.UserAgent = version.UserAgent + + remote, fingerprint, err = defaultConfig.ParseRemote(image) + if err != nil { + return err + } + + d, err := defaultConfig.GetImageServer(remote) + if err != nil { + return err + } + + if fingerprint == "" { + fingerprint = "default" + } + + alias, _, err := d.GetImageAlias(fingerprint) + if err == nil { + fingerprint = alias.Target + } + + _, _, err = c.GetImage(fingerprint) + if err != nil { + logf("Importing image into local store: %s", fingerprint) + image, _, err := d.GetImage(fingerprint) + if err != nil { + logf(fmt.Sprintf("Failed to import image: %s", err)) + return err + } + + op, err := c.CopyImage(d, *image, nil) + if err != nil { + logf(fmt.Sprintf("Failed to import image: %s", err)) + return err + } + + err = op.Wait() + if err != nil { + logf(fmt.Sprintf("Failed to import image: %s", err)) + return err + } + } else { + logf("Found image in local store: %s", fingerprint) + } + } else { + fingerprint = image + logf("Found image in local store: %s", fingerprint) + } + + // Start the containers + spawnedCount := 0 + nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d" + wgBatch := sync.WaitGroup{} + nextStat := batch + + startContainer := func(name string) { + defer wgBatch.Done() + + // Configure + config := map[string]string{} + if privileged { + config["security.privileged"] = "true" + } + config["user.lxd-benchmark"] = "true" + + // Create + req := api.ContainersPost{ + Name: name, + Source: api.ContainerSource{ + Type: "image", + Fingerprint: fingerprint, + }, + } + req.Config = config + + op, err := c.CreateContainer(req) + if err != nil { + logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + return + } + + err = op.Wait() + if err != nil { + logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + return + } + + // Start + op, err = c.UpdateContainerState(name, api.ContainerStatePut{Action: "start", Timeout: -1}, "") + if err != nil { + logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + return + } + + err = op.Wait() + if err != nil { + logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + return + } + + // Freeze + if freeze { + op, err := c.UpdateContainerState(name, api.ContainerStatePut{Action: "freeze", Timeout: -1}, "") + if err != nil { + logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + return + } + + err = op.Wait() + if err != nil { + logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + return + } + } + } + + logf("Starting the test") + timeStart := time.Now() + + for i := 0; i < batches; i++ { + for j := 0; j < batch; j++ { + spawnedCount = spawnedCount + 1 + name := fmt.Sprintf(nameFormat, spawnedCount) + + wgBatch.Add(1) + go startContainer(name) + } + wgBatch.Wait() + + if spawnedCount >= nextStat { + interval := time.Since(timeStart).Seconds() + logf("Started %d containers in %.3fs (%.3f/s)", spawnedCount, interval, float64(spawnedCount)/interval) + nextStat = nextStat * 2 + } + } + + for k := 0; k < remainder; k++ { + spawnedCount = spawnedCount + 1 + name := fmt.Sprintf(nameFormat, spawnedCount) + + wgBatch.Add(1) + go startContainer(name) + } + wgBatch.Wait() + + logf("Test completed in %.3fs", time.Since(timeStart).Seconds()) + + return nil +} + +func DeleteContainers(c lxd.ContainerServer, parallel int) error { + batch := parallel + if batch < 1 { + // Detect the number of parallel actions + cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices") + if err != nil { + return err + } + + batch = len(cpus) + } + + // List all the containers + allContainers, err := c.GetContainers() + if err != nil { + return err + } + + containers := []api.Container{} + for _, container := range allContainers { + if container.Config["user.lxd-benchmark"] != "true" { + continue + } + + containers = append(containers, container) + } + + // Delete them all + count := len(containers) + logf("%d containers to delete", count) + + batches := count / batch + + deletedCount := 0 + wgBatch := sync.WaitGroup{} + nextStat := batch + + deleteContainer := func(ct api.Container) { + defer wgBatch.Done() + + // Stop + if ct.IsActive() { + op, err := c.UpdateContainerState(ct.Name, api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "") + if err != nil { + logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err)) + return + } + + err = op.Wait() + if err != nil { + logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err)) + return + } + } + + // Delete + op, err := c.DeleteContainer(ct.Name) + if err != nil { + logf("Failed to delete container: %s", ct.Name) + return + } + + err = op.Wait() + if err != nil { + logf("Failed to delete container: %s", ct.Name) + return + } + } + + logf("Starting the cleanup") + timeStart := time.Now() + + for i := 0; i < batches; i++ { + for j := 0; j < batch; j++ { + wgBatch.Add(1) + go deleteContainer(containers[deletedCount]) + + deletedCount = deletedCount + 1 + } + wgBatch.Wait() + + if deletedCount >= nextStat { + interval := time.Since(timeStart).Seconds() + logf("Deleted %d containers in %.3fs (%.3f/s)", deletedCount, interval, float64(deletedCount)/interval) + nextStat = nextStat * 2 + } + } + + for k := deletedCount; k < count; k++ { + wgBatch.Add(1) + go deleteContainer(containers[deletedCount]) + + deletedCount = deletedCount + 1 + } + wgBatch.Wait() + + logf("Cleanup completed") + + return nil +} + +func logf(format string, args ...interface{}) { + fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...) +} diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go index 9c8eb3c11..55de62154 100644 --- a/test/lxd-benchmark/main.go +++ b/test/lxd-benchmark/main.go @@ -2,16 +2,11 @@ package main import ( "fmt" - "io/ioutil" "os" - "strings" - "sync" - "time" + "github.com/lxc/lxd/benchmark" "github.com/lxc/lxd/client" - "github.com/lxc/lxd/lxc/config" "github.com/lxc/lxd/shared" - "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/gnuflag" "github.com/lxc/lxd/shared/version" ) @@ -68,323 +63,10 @@ func run(args []string) error { switch os.Args[1] { case "spawn": - return spawnContainers(c, *argCount, *argImage, *argPrivileged) + return benchmark.SpawnContainers(c, *argCount, *argParallel, *argImage, *argPrivileged, *argFreeze) case "delete": - return deleteContainers(c) + return benchmark.DeleteContainers(c, *argParallel) } return nil } - -func logf(format string, args ...interface{}) { - fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...) -} - -func spawnContainers(c lxd.ContainerServer, count int, image string, privileged bool) error { - batch := *argParallel - if batch < 1 { - // Detect the number of parallel actions - cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices") - if err != nil { - return err - } - - batch = len(cpus) - } - - batches := count / batch - remainder := count % batch - - // Print the test header - st, _, err := c.GetServer() - if err != nil { - return err - } - - privilegedStr := "unprivileged" - if privileged { - privilegedStr = "privileged" - } - - mode := "normal startup" - if *argFreeze { - mode = "start and freeze" - } - - fmt.Printf("Test environment:\n") - fmt.Printf(" Server backend: %s\n", st.Environment.Server) - fmt.Printf(" Server version: %s\n", st.Environment.ServerVersion) - fmt.Printf(" Kernel: %s\n", st.Environment.Kernel) - fmt.Printf(" Kernel architecture: %s\n", st.Environment.KernelArchitecture) - fmt.Printf(" Kernel version: %s\n", st.Environment.KernelVersion) - fmt.Printf(" Storage backend: %s\n", st.Environment.Storage) - fmt.Printf(" Storage version: %s\n", st.Environment.StorageVersion) - fmt.Printf(" Container backend: %s\n", st.Environment.Driver) - fmt.Printf(" Container version: %s\n", st.Environment.DriverVersion) - fmt.Printf("\n") - fmt.Printf("Test variables:\n") - fmt.Printf(" Container count: %d\n", count) - fmt.Printf(" Container mode: %s\n", privilegedStr) - fmt.Printf(" Startup mode: %s\n", mode) - fmt.Printf(" Image: %s\n", image) - fmt.Printf(" Batches: %d\n", batches) - fmt.Printf(" Batch size: %d\n", batch) - fmt.Printf(" Remainder: %d\n", remainder) - fmt.Printf("\n") - - // Pre-load the image - var fingerprint string - if strings.Contains(image, ":") { - var remote string - - defaultConfig := config.DefaultConfig - defaultConfig.UserAgent = version.UserAgent - - remote, fingerprint, err = defaultConfig.ParseRemote(image) - if err != nil { - return err - } - - d, err := defaultConfig.GetImageServer(remote) - if err != nil { - return err - } - - if fingerprint == "" { - fingerprint = "default" - } - - alias, _, err := d.GetImageAlias(fingerprint) - if err == nil { - fingerprint = alias.Target - } - - _, _, err = c.GetImage(fingerprint) - if err != nil { - logf("Importing image into local store: %s", fingerprint) - image, _, err := d.GetImage(fingerprint) - if err != nil { - logf(fmt.Sprintf("Failed to import image: %s", err)) - return err - } - - op, err := c.CopyImage(d, *image, nil) - if err != nil { - logf(fmt.Sprintf("Failed to import image: %s", err)) - return err - } - - err = op.Wait() - if err != nil { - logf(fmt.Sprintf("Failed to import image: %s", err)) - return err - } - } else { - logf("Found image in local store: %s", fingerprint) - } - } else { - fingerprint = image - logf("Found image in local store: %s", fingerprint) - } - - // Start the containers - spawnedCount := 0 - nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d" - wgBatch := sync.WaitGroup{} - nextStat := batch - - startContainer := func(name string) { - defer wgBatch.Done() - - // Configure - config := map[string]string{} - if privileged { - config["security.privileged"] = "true" - } - config["user.lxd-benchmark"] = "true" - - // Create - req := api.ContainersPost{ - Name: name, - Source: api.ContainerSource{ - Type: "image", - Fingerprint: fingerprint, - }, - } - req.Config = config - - op, err := c.CreateContainer(req) - if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) - return - } - - err = op.Wait() - if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) - return - } - - // Start - op, err = c.UpdateContainerState(name, api.ContainerStatePut{Action: "start", Timeout: -1}, "") - if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) - return - } - - err = op.Wait() - if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) - return - } - - // Freeze - if *argFreeze { - op, err := c.UpdateContainerState(name, api.ContainerStatePut{Action: "freeze", Timeout: -1}, "") - if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) - return - } - - err = op.Wait() - if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) - return - } - } - } - - logf("Starting the test") - timeStart := time.Now() - - for i := 0; i < batches; i++ { - for j := 0; j < batch; j++ { - spawnedCount = spawnedCount + 1 - name := fmt.Sprintf(nameFormat, spawnedCount) - - wgBatch.Add(1) - go startContainer(name) - } - wgBatch.Wait() - - if spawnedCount >= nextStat { - interval := time.Since(timeStart).Seconds() - logf("Started %d containers in %.3fs (%.3f/s)", spawnedCount, interval, float64(spawnedCount)/interval) - nextStat = nextStat * 2 - } - } - - for k := 0; k < remainder; k++ { - spawnedCount = spawnedCount + 1 - name := fmt.Sprintf(nameFormat, spawnedCount) - - wgBatch.Add(1) - go startContainer(name) - } - wgBatch.Wait() - - logf("Test completed in %.3fs", time.Since(timeStart).Seconds()) - - return nil -} - -func deleteContainers(c lxd.ContainerServer) error { - batch := *argParallel - if batch < 1 { - // Detect the number of parallel actions - cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices") - if err != nil { - return err - } - - batch = len(cpus) - } - - // List all the containers - allContainers, err := c.GetContainers() - if err != nil { - return err - } - - containers := []api.Container{} - for _, container := range allContainers { - if container.Config["user.lxd-benchmark"] != "true" { - continue - } - - containers = append(containers, container) - } - - // Delete them all - count := len(containers) - logf("%d containers to delete", count) - - batches := count / batch - - deletedCount := 0 - wgBatch := sync.WaitGroup{} - nextStat := batch - - deleteContainer := func(ct api.Container) { - defer wgBatch.Done() - - // Stop - if ct.IsActive() { - op, err := c.UpdateContainerState(ct.Name, api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "") - if err != nil { - logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err)) - return - } - - err = op.Wait() - if err != nil { - logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err)) - return - } - } - - // Delete - op, err := c.DeleteContainer(ct.Name) - if err != nil { - logf("Failed to delete container: %s", ct.Name) - return - } - - err = op.Wait() - if err != nil { - logf("Failed to delete container: %s", ct.Name) - return - } - } - - logf("Starting the cleanup") - timeStart := time.Now() - - for i := 0; i < batches; i++ { - for j := 0; j < batch; j++ { - wgBatch.Add(1) - go deleteContainer(containers[deletedCount]) - - deletedCount = deletedCount + 1 - } - wgBatch.Wait() - - if deletedCount >= nextStat { - interval := time.Since(timeStart).Seconds() - logf("Deleted %d containers in %.3fs (%.3f/s)", deletedCount, interval, float64(deletedCount)/interval) - nextStat = nextStat * 2 - } - } - - for k := deletedCount; k < count; k++ { - wgBatch.Add(1) - go deleteContainer(containers[deletedCount]) - - deletedCount = deletedCount + 1 - } - wgBatch.Wait() - - logf("Cleanup completed") - - return nil -} From 6b459fc01bfea1e68bd9d6b0de6f5e22fc38b8a1 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Tue, 5 Sep 2017 12:24:36 +0200 Subject: [PATCH 2/7] benchmark: extract PrintServerInfo function Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- benchmark/benchmark.go | 41 +++++++++++++++++++++++------------------ test/lxd-benchmark/main.go | 2 ++ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index e8fe5e8f7..26e2a60a1 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -13,6 +13,28 @@ import ( "github.com/lxc/lxd/shared/version" ) +// PrintServerInfo prints out information about the server. +func PrintServerInfo(c lxd.ContainerServer) error { + server, _, err := c.GetServer() + if err != nil { + return err + } + env := server.Environment + fmt.Printf("Test environment:\n") + fmt.Printf(" Server backend: %s\n", env.Server) + fmt.Printf(" Server version: %s\n", env.ServerVersion) + fmt.Printf(" Kernel: %s\n", env.Kernel) + fmt.Printf(" Kernel architecture: %s\n", env.KernelArchitecture) + fmt.Printf(" Kernel version: %s\n", env.KernelVersion) + fmt.Printf(" Storage backend: %s\n", env.Storage) + fmt.Printf(" Storage version: %s\n", env.StorageVersion) + fmt.Printf(" Container backend: %s\n", env.Driver) + fmt.Printf(" Container version: %s\n", env.DriverVersion) + fmt.Printf("\n") + return nil +} + +// SpawnContainers launches a set of containers. func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, freeze bool) error { batch := parallel if batch < 1 { @@ -29,32 +51,14 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin remainder := count % batch // Print the test header - st, _, err := c.GetServer() - if err != nil { - return err - } - privilegedStr := "unprivileged" if privileged { privilegedStr = "privileged" } - mode := "normal startup" if freeze { mode = "start and freeze" } - - fmt.Printf("Test environment:\n") - fmt.Printf(" Server backend: %s\n", st.Environment.Server) - fmt.Printf(" Server version: %s\n", st.Environment.ServerVersion) - fmt.Printf(" Kernel: %s\n", st.Environment.Kernel) - fmt.Printf(" Kernel architecture: %s\n", st.Environment.KernelArchitecture) - fmt.Printf(" Kernel version: %s\n", st.Environment.KernelVersion) - fmt.Printf(" Storage backend: %s\n", st.Environment.Storage) - fmt.Printf(" Storage version: %s\n", st.Environment.StorageVersion) - fmt.Printf(" Container backend: %s\n", st.Environment.Driver) - fmt.Printf(" Container version: %s\n", st.Environment.DriverVersion) - fmt.Printf("\n") fmt.Printf("Test variables:\n") fmt.Printf(" Container count: %d\n", count) fmt.Printf(" Container mode: %s\n", privilegedStr) @@ -69,6 +73,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin var fingerprint string if strings.Contains(image, ":") { var remote string + var err error defaultConfig := config.DefaultConfig defaultConfig.UserAgent = version.UserAgent diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go index 55de62154..f2a2a6f2b 100644 --- a/test/lxd-benchmark/main.go +++ b/test/lxd-benchmark/main.go @@ -61,6 +61,8 @@ func run(args []string) error { return err } + benchmark.PrintServerInfo(c) + switch os.Args[1] { case "spawn": return benchmark.SpawnContainers(c, *argCount, *argParallel, *argImage, *argPrivileged, *argFreeze) From 31e612a9620a56b9273cafa3661e0f452ee7f917 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Tue, 5 Sep 2017 12:40:01 +0200 Subject: [PATCH 3/7] benchmark: extract printTestConfig function Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- benchmark/benchmark.go | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index 26e2a60a1..141066bb4 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -47,28 +47,11 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin batch = len(cpus) } + printTestConfig(count, batch, image, privileged, freeze) + batches := count / batch remainder := count % batch - // Print the test header - privilegedStr := "unprivileged" - if privileged { - privilegedStr = "privileged" - } - mode := "normal startup" - if freeze { - mode = "start and freeze" - } - fmt.Printf("Test variables:\n") - fmt.Printf(" Container count: %d\n", count) - fmt.Printf(" Container mode: %s\n", privilegedStr) - fmt.Printf(" Startup mode: %s\n", mode) - fmt.Printf(" Image: %s\n", image) - fmt.Printf(" Batches: %d\n", batches) - fmt.Printf(" Batch size: %d\n", batch) - fmt.Printf(" Remainder: %d\n", remainder) - fmt.Printf("\n") - // Pre-load the image var fingerprint string if strings.Contains(image, ":") { @@ -330,3 +313,26 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error { func logf(format string, args ...interface{}) { fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...) } + +func printTestConfig(count int, batchSize int, image string, privileged bool, freeze bool) { + privilegedStr := "unprivileged" + if privileged { + privilegedStr = "privileged" + } + mode := "normal startup" + if freeze { + mode = "start and freeze" + } + + batches := count / batchSize + remainder := count % batchSize + fmt.Printf("Test variables:\n") + fmt.Printf(" Container count: %d\n", count) + fmt.Printf(" Container mode: %s\n", privilegedStr) + fmt.Printf(" Startup mode: %s\n", mode) + fmt.Printf(" Image: %s\n", image) + fmt.Printf(" Batches: %d\n", batches) + fmt.Printf(" Batch size: %d\n", batchSize) + fmt.Printf(" Remainder: %d\n", remainder) + fmt.Printf("\n") +} From c5f93f0b5486387b1142e3ebfaadb0cc124861d1 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Tue, 5 Sep 2017 12:54:22 +0200 Subject: [PATCH 4/7] benchmark: extract getBatchSize function Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- benchmark/benchmark.go | 56 +++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index 141066bb4..d9911fd64 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -36,21 +36,15 @@ func PrintServerInfo(c lxd.ContainerServer) error { // SpawnContainers launches a set of containers. func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, freeze bool) error { - batch := parallel - if batch < 1 { - // Detect the number of parallel actions - cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices") - if err != nil { - return err - } - - batch = len(cpus) + batchSize, err := getBatchSize(parallel) + if err != nil { + return err } - printTestConfig(count, batch, image, privileged, freeze) + printTestConfig(count, batchSize, image, privileged, freeze) - batches := count / batch - remainder := count % batch + batches := count / batchSize + remainder := count % batchSize // Pre-load the image var fingerprint string @@ -112,7 +106,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin spawnedCount := 0 nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d" wgBatch := sync.WaitGroup{} - nextStat := batch + nextStat := batchSize startContainer := func(name string) { defer wgBatch.Done() @@ -179,7 +173,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin timeStart := time.Now() for i := 0; i < batches; i++ { - for j := 0; j < batch; j++ { + for j := 0; j < batchSize; j++ { spawnedCount = spawnedCount + 1 name := fmt.Sprintf(nameFormat, spawnedCount) @@ -209,16 +203,11 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin return nil } +// DeleteContainers removes containers created by the benchmark. func DeleteContainers(c lxd.ContainerServer, parallel int) error { - batch := parallel - if batch < 1 { - // Detect the number of parallel actions - cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices") - if err != nil { - return err - } - - batch = len(cpus) + batchSize, err := getBatchSize(parallel) + if err != nil { + return err } // List all the containers @@ -240,11 +229,11 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error { count := len(containers) logf("%d containers to delete", count) - batches := count / batch + batches := count / batchSize deletedCount := 0 wgBatch := sync.WaitGroup{} - nextStat := batch + nextStat := batchSize deleteContainer := func(ct api.Container) { defer wgBatch.Done() @@ -282,7 +271,7 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error { timeStart := time.Now() for i := 0; i < batches; i++ { - for j := 0; j < batch; j++ { + for j := 0; j < batchSize; j++ { wgBatch.Add(1) go deleteContainer(containers[deletedCount]) @@ -310,6 +299,21 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error { return nil } +func getBatchSize(parallel int) (int, error) { + batchSize := parallel + if batchSize < 1 { + // Detect the number of parallel actions + cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices") + if err != nil { + return -1, err + } + + batchSize = len(cpus) + } + + return batchSize, nil +} + func logf(format string, args ...interface{}) { fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...) } From b8bf7d20c70e5328c5944261205b146b07a5c8eb Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Tue, 5 Sep 2017 13:01:34 +0200 Subject: [PATCH 5/7] benchmark: extract GetContainers function Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- benchmark/benchmark.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index d9911fd64..81bdcdda3 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -203,20 +203,15 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin return nil } -// DeleteContainers removes containers created by the benchmark. -func DeleteContainers(c lxd.ContainerServer, parallel int) error { - batchSize, err := getBatchSize(parallel) - if err != nil { - return err - } +// GetContainers returns containers created by the benchmark. +func GetContainers(c lxd.ContainerServer) ([]api.Container, error) { + containers := []api.Container{} - // List all the containers allContainers, err := c.GetContainers() if err != nil { - return err + return containers, err } - containers := []api.Container{} for _, container := range allContainers { if container.Config["user.lxd-benchmark"] != "true" { continue @@ -225,7 +220,22 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error { containers = append(containers, container) } - // Delete them all + return containers, nil +} + +// DeleteContainers removes containers created by the benchmark. +func DeleteContainers(c lxd.ContainerServer, parallel int) error { + batchSize, err := getBatchSize(parallel) + if err != nil { + return err + } + + // List all the containers + containers, err := GetContainers(c) + if err != nil { + return err + } + count := len(containers) logf("%d containers to delete", count) From 311f9a30f0c3fdfb7cb3ac84ffc60fe369fbc245 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Tue, 5 Sep 2017 16:25:20 +0200 Subject: [PATCH 6/7] benchmark: add processBatch function, use it in SpawnContainers and DeleteContainers Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- benchmark/benchmark.go | 144 ++++++++++++++++++++----------------------------- 1 file changed, 57 insertions(+), 87 deletions(-) diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index 81bdcdda3..cb54ed9a2 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -43,9 +43,6 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin printTestConfig(count, batchSize, image, privileged, freeze) - batches := count / batchSize - remainder := count % batchSize - // Pre-load the image var fingerprint string if strings.Contains(image, ":") { @@ -102,14 +99,8 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin logf("Found image in local store: %s", fingerprint) } - // Start the containers - spawnedCount := 0 - nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d" - wgBatch := sync.WaitGroup{} - nextStat := batchSize - - startContainer := func(name string) { - defer wgBatch.Done() + startContainer := func(index int, wg *sync.WaitGroup) { + defer wg.Done() // Configure config := map[string]string{} @@ -119,6 +110,8 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin config["user.lxd-benchmark"] = "true" // Create + nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d" + name := fmt.Sprintf(nameFormat, index+1) req := api.ContainersPost{ Name: name, Source: api.ContainerSource{ @@ -130,26 +123,26 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin op, err := c.CreateContainer(req) if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + logf("Failed to spawn container '%s': %s", name, err) return } err = op.Wait() if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + logf("Failed to spawn container '%s': %s", name, err) return } // Start op, err = c.UpdateContainerState(name, api.ContainerStatePut{Action: "start", Timeout: -1}, "") if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + logf("Failed to spawn container '%s': %s", name, err) return } err = op.Wait() if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + logf("Failed to spawn container '%s': %s", name, err) return } @@ -157,49 +150,19 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin if freeze { op, err := c.UpdateContainerState(name, api.ContainerStatePut{Action: "freeze", Timeout: -1}, "") if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + logf("Failed to spawn container '%s': %s", name, err) return } err = op.Wait() if err != nil { - logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err)) + logf("Failed to spawn container '%s': %s", name, err) return } } } - logf("Starting the test") - timeStart := time.Now() - - for i := 0; i < batches; i++ { - for j := 0; j < batchSize; j++ { - spawnedCount = spawnedCount + 1 - name := fmt.Sprintf(nameFormat, spawnedCount) - - wgBatch.Add(1) - go startContainer(name) - } - wgBatch.Wait() - - if spawnedCount >= nextStat { - interval := time.Since(timeStart).Seconds() - logf("Started %d containers in %.3fs (%.3f/s)", spawnedCount, interval, float64(spawnedCount)/interval) - nextStat = nextStat * 2 - } - } - - for k := 0; k < remainder; k++ { - spawnedCount = spawnedCount + 1 - name := fmt.Sprintf(nameFormat, spawnedCount) - - wgBatch.Add(1) - go startContainer(name) - } - wgBatch.Wait() - - logf("Test completed in %.3fs", time.Since(timeStart).Seconds()) - + processBatch(count, batchSize, startContainer) return nil } @@ -237,28 +200,24 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error { } count := len(containers) - logf("%d containers to delete", count) + logf("%d containers to delete", len(containers)) - batches := count / batchSize + deleteContainer := func(index int, wg *sync.WaitGroup) { + defer wg.Done() - deletedCount := 0 - wgBatch := sync.WaitGroup{} - nextStat := batchSize - - deleteContainer := func(ct api.Container) { - defer wgBatch.Done() + ct := containers[index] // Stop if ct.IsActive() { op, err := c.UpdateContainerState(ct.Name, api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "") if err != nil { - logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err)) + logf("Failed to delete container '%s': %s", ct.Name, err) return } err = op.Wait() if err != nil { - logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err)) + logf("Failed to delete container '%s': %s", ct.Name, err) return } } @@ -277,35 +236,7 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error { } } - logf("Starting the cleanup") - timeStart := time.Now() - - for i := 0; i < batches; i++ { - for j := 0; j < batchSize; j++ { - wgBatch.Add(1) - go deleteContainer(containers[deletedCount]) - - deletedCount = deletedCount + 1 - } - wgBatch.Wait() - - if deletedCount >= nextStat { - interval := time.Since(timeStart).Seconds() - logf("Deleted %d containers in %.3fs (%.3f/s)", deletedCount, interval, float64(deletedCount)/interval) - nextStat = nextStat * 2 - } - } - - for k := deletedCount; k < count; k++ { - wgBatch.Add(1) - go deleteContainer(containers[deletedCount]) - - deletedCount = deletedCount + 1 - } - wgBatch.Wait() - - logf("Cleanup completed") - + processBatch(count, batchSize, deleteContainer) return nil } @@ -324,6 +255,45 @@ func getBatchSize(parallel int) (int, error) { return batchSize, nil } +func processBatch(count int, batchSize int, process func(index int, wg *sync.WaitGroup)) time.Duration { + batches := count / batchSize + remainder := count % batchSize + processed := 0 + wg := sync.WaitGroup{} + nextStat := batchSize + + logf("Batch processing start") + timeStart := time.Now() + + for i := 0; i < batches; i++ { + for j := 0; j < batchSize; j++ { + wg.Add(1) + go process(processed, &wg) + processed++ + } + wg.Wait() + + if processed >= nextStat { + interval := time.Since(timeStart).Seconds() + logf("Processed %d containers in %.3fs (%.3f/s)", processed, interval, float64(processed)/interval) + nextStat = nextStat * 2 + } + + } + + for k := 0; k < remainder; k++ { + wg.Add(1) + go process(processed, &wg) + processed++ + } + wg.Wait() + + timeEnd := time.Now() + duration := timeEnd.Sub(timeStart) + logf("Batch processing completed in %.3fs", duration.Seconds()) + return duration +} + func logf(format string, args ...interface{}) { fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...) } From 093963f7b628ac4728005c56c6dca198d90557f9 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Tue, 5 Sep 2017 16:51:46 +0200 Subject: [PATCH 7/7] benchmark: extract ensureImage function Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- benchmark/benchmark.go | 114 ++++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index cb54ed9a2..eea503b8c 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -43,60 +43,9 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin printTestConfig(count, batchSize, image, privileged, freeze) - // Pre-load the image - var fingerprint string - if strings.Contains(image, ":") { - var remote string - var err error - - defaultConfig := config.DefaultConfig - defaultConfig.UserAgent = version.UserAgent - - remote, fingerprint, err = defaultConfig.ParseRemote(image) - if err != nil { - return err - } - - d, err := defaultConfig.GetImageServer(remote) - if err != nil { - return err - } - - if fingerprint == "" { - fingerprint = "default" - } - - alias, _, err := d.GetImageAlias(fingerprint) - if err == nil { - fingerprint = alias.Target - } - - _, _, err = c.GetImage(fingerprint) - if err != nil { - logf("Importing image into local store: %s", fingerprint) - image, _, err := d.GetImage(fingerprint) - if err != nil { - logf(fmt.Sprintf("Failed to import image: %s", err)) - return err - } - - op, err := c.CopyImage(d, *image, nil) - if err != nil { - logf(fmt.Sprintf("Failed to import image: %s", err)) - return err - } - - err = op.Wait() - if err != nil { - logf(fmt.Sprintf("Failed to import image: %s", err)) - return err - } - } else { - logf("Found image in local store: %s", fingerprint) - } - } else { - fingerprint = image - logf("Found image in local store: %s", fingerprint) + fingerprint, err := ensureImage(c, image) + if err != nil { + return err } startContainer := func(index int, wg *sync.WaitGroup) { @@ -255,6 +204,63 @@ func getBatchSize(parallel int) (int, error) { return batchSize, nil } +func ensureImage(c lxd.ContainerServer, image string) (string, error) { + var fingerprint string + + if strings.Contains(image, ":") { + defaultConfig := config.DefaultConfig + defaultConfig.UserAgent = version.UserAgent + + remote, fp, err := defaultConfig.ParseRemote(image) + if err != nil { + return "", err + } + fingerprint = fp + + imageServer, err := defaultConfig.GetImageServer(remote) + if err != nil { + return "", err + } + + if fingerprint == "" { + fingerprint = "default" + } + + alias, _, err := imageServer.GetImageAlias(fingerprint) + if err == nil { + fingerprint = alias.Target + } + + _, _, err = c.GetImage(fingerprint) + if err != nil { + logf("Importing image into local store: %s", fingerprint) + image, _, err := imageServer.GetImage(fingerprint) + if err != nil { + logf("Failed to import image: %s", err) + return "", err + } + + op, err := c.CopyImage(imageServer, *image, nil) + if err != nil { + logf("Failed to import image: %s", err) + return "", err + } + + err = op.Wait() + if err != nil { + logf("Failed to import image: %s", err) + return "", err + } + } else { + logf("Found image in local store: %s", fingerprint) + } + } else { + fingerprint = image + logf("Found image in local store: %s", fingerprint) + } + return fingerprint, nil +} + func processBatch(count int, batchSize int, process func(index int, wg *sync.WaitGroup)) time.Duration { batches := count / batchSize remainder := count % batchSize
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel