virtcontainers/qemu: reduce memory footprint

There is a relation between the maximum number of vCPUs and the
memory footprint, if QEMU maxcpus option and kernel nr_cpus
cmdline argument are big, then memory footprint is big, this
issue only occurs if CPU hotplug support is enabled in the kernel,
might be because of kernel needs to allocate resources to watch all
sockets waiting for a CPU to be connected (ACPI event).

For example

```
+---------------+-------------------------+
|               | Memory Footprint (KB)   |
+---------------+-------------------------+
| NR_CPUS=240   | 186501                  |
+---------------+-------------------------+
| NR_CPUS=8     | 110684                  |
+---------------+-------------------------+
```

In order to do not affect CPU hotplug and allow to users to have containers
with the same number of physical CPUs, this patch tries to mitigate the
big memory footprint by using the actual number of physical CPUs as the
maximum number of vCPUs for each container if `default_maxvcpus` is <= 0 in
the runtime configuration file,  otherwise `default_maxvcpus` is used as the
maximum number of vCPUs.

Before this patch a container with 256MB of RAM

```
              total        used        free      shared  buff/cache   available
Mem:           195M         40M        113M         26M         41M        112M
Swap:            0B          0B          0B
```

With this patch

```
              total        used        free      shared  buff/cache   available
Mem:           236M         11M        188M         26M         36M        186M
Swap:            0B          0B          0B
```

fixes #295

Signed-off-by: Julio Montes <julio.montes@intel.com>
This commit is contained in:
Julio Montes
2018-05-09 13:34:20 -05:00
parent 90e3ba6027
commit 07db945b09
11 changed files with 76 additions and 15 deletions

View File

@@ -44,6 +44,7 @@ func makeRuntimeConfigFileData(hypervisor, hypervisorPath, kernelPath, imagePath
image = "` + imagePath + `"
machine_type = "` + machineType + `"
default_vcpus = ` + strconv.FormatUint(uint64(defaultVCPUCount), 10) + `
default_maxvcpus = ` + strconv.FormatUint(uint64(defaultMaxVCPUCount), 10) + `
default_memory = ` + strconv.FormatUint(uint64(defaultMemSize), 10) + `
disable_block_device_use = ` + strconv.FormatBool(disableBlock) + `
enable_iothreads = ` + strconv.FormatBool(enableIOThreads) + `
@@ -129,6 +130,7 @@ func createAllRuntimeConfigFiles(dir, hypervisor string) (config testRuntimeConf
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
HypervisorMachineType: machineType,
DefaultVCPUs: defaultVCPUCount,
DefaultMaxVCPUs: uint32(goruntime.NumCPU()),
DefaultMemSz: defaultMemSize,
DisableBlockDeviceUse: disableBlockDevice,
BlockDeviceDriver: defaultBlockDeviceDriver,
@@ -513,6 +515,7 @@ func TestMinimalRuntimeConfig(t *testing.T) {
InitrdPath: defaultInitrdPath,
HypervisorMachineType: defaultMachineType,
DefaultVCPUs: defaultVCPUCount,
DefaultMaxVCPUs: defaultMaxVCPUCount,
DefaultMemSz: defaultMemSize,
DisableBlockDeviceUse: defaultDisableBlockDeviceUse,
DefaultBridges: defaultBridgesCount,
@@ -658,10 +661,13 @@ func TestNewShimConfig(t *testing.T) {
func TestHypervisorDefaults(t *testing.T) {
assert := assert.New(t)
numCPUs := goruntime.NumCPU()
h := hypervisor{}
assert.Equal(h.machineType(), defaultMachineType, "default hypervisor machine type wrong")
assert.Equal(h.defaultVCPUs(), defaultVCPUCount, "default vCPU number is wrong")
assert.Equal(h.defaultMaxVCPUs(), uint32(numCPUs), "default max vCPU number is wrong")
assert.Equal(h.defaultMemSz(), defaultMemSize, "default memory size is wrong")
machineType := "foo"
@@ -670,15 +676,24 @@ func TestHypervisorDefaults(t *testing.T) {
// auto inferring
h.DefaultVCPUs = -1
assert.Equal(h.defaultVCPUs(), uint32(goruntime.NumCPU()), "default vCPU number is wrong")
assert.Equal(h.defaultVCPUs(), uint32(numCPUs), "default vCPU number is wrong")
h.DefaultVCPUs = 2
assert.Equal(h.defaultVCPUs(), uint32(2), "default vCPU number is wrong")
numCPUs := goruntime.NumCPU()
h.DefaultVCPUs = int32(numCPUs) + 1
assert.Equal(h.defaultVCPUs(), uint32(numCPUs), "default vCPU number is wrong")
h.DefaultMaxVCPUs = 2
assert.Equal(h.defaultMaxVCPUs(), uint32(h.DefaultMaxVCPUs), "default max vCPU number is wrong")
h.DefaultMaxVCPUs = uint32(numCPUs) + 1
assert.Equal(h.defaultMaxVCPUs(), uint32(numCPUs), "default max vCPU number is wrong")
maxvcpus := vc.MaxQemuVCPUs()
h.DefaultMaxVCPUs = uint32(maxvcpus) + 1
assert.Equal(h.defaultMaxVCPUs(), uint32(numCPUs), "default max vCPU number is wrong")
h.DefaultMemSz = 1024
assert.Equal(h.defaultMemSz(), uint32(1024), "default memory size is wrong")
}