|
5 | 5 | import os
|
6 | 6 | import shutil
|
7 | 7 | import subprocess
|
| 8 | +from concurrent.futures import ProcessPoolExecutor |
8 | 9 |
|
9 | 10 | import pytest
|
10 | 11 |
|
@@ -82,3 +83,80 @@ def test_jailer_startup(
|
82 | 83 | for d in os.listdir(mounts_paths):
|
83 | 84 | utils.check_output(f"umount {mounts_paths}/{d}")
|
84 | 85 | shutil.rmtree(DEFAULT_CHROOT_PATH)
|
| 86 | + |
| 87 | + |
| 88 | +def run_command(cmd): |
| 89 | + """ |
| 90 | + Run the cmd in subprocess. Needs to be outside |
| 91 | + other functions, otherwise the ProcessPoolExecutor |
| 92 | + will not be able to pickle it. |
| 93 | + """ |
| 94 | + return subprocess.run( |
| 95 | + cmd, |
| 96 | + capture_output=True, |
| 97 | + text=True, |
| 98 | + check=True, |
| 99 | + ) |
| 100 | + |
| 101 | + |
| 102 | +@pytest.mark.nonci |
| 103 | +@pytest.mark.parametrize("parallel", [1, 5, 10]) |
| 104 | +@pytest.mark.parametrize("mounts", [0, 100, 300, 500]) |
| 105 | +def test_jailer_startup_parallel( |
| 106 | + jailer_time_bin, tmp_path, microvm_factory, parallel, mounts, metrics |
| 107 | +): |
| 108 | + """ |
| 109 | + Test the overhead of jailer startup without and with bind mounts |
| 110 | + with different parallelism options. |
| 111 | + """ |
| 112 | + |
| 113 | + jailer_binary = microvm_factory.jailer_binary_path |
| 114 | + |
| 115 | + # Create bind mount points. The exact location of them |
| 116 | + # does not matter, they just need to exist. |
| 117 | + mounts_paths = tmp_path / "mounts" |
| 118 | + for m in range(mounts): |
| 119 | + mount_path = f"{mounts_paths}/mount{m}" |
| 120 | + os.makedirs(mount_path) |
| 121 | + utils.check_output(f"mount --bind {mount_path} {mount_path}") |
| 122 | + |
| 123 | + metrics.set_dimensions( |
| 124 | + { |
| 125 | + "instance": global_props.instance, |
| 126 | + "cpu_model": global_props.cpu_model, |
| 127 | + "performance_test": "test_jailer_startup_parallel", |
| 128 | + "parallel": str(parallel), |
| 129 | + "mounts": str(mounts), |
| 130 | + } |
| 131 | + ) |
| 132 | + |
| 133 | + cmds = [] |
| 134 | + for i in range(500): |
| 135 | + jailer = JailerContext( |
| 136 | + jailer_id=f"fakefc{i}", |
| 137 | + exec_file=jailer_time_bin, |
| 138 | + # Don't deamonize to get the stdout |
| 139 | + daemonize=False, |
| 140 | + ) |
| 141 | + jailer.setup() |
| 142 | + |
| 143 | + cmd = [str(jailer_binary), *jailer.construct_param_list()] |
| 144 | + cmds.append(cmd) |
| 145 | + |
| 146 | + with ProcessPoolExecutor(max_workers=parallel) as executor: |
| 147 | + # Submit all commands and get results |
| 148 | + results = list(executor.map(run_command, cmds)) |
| 149 | + |
| 150 | + # Get results as they complete |
| 151 | + for result in results: |
| 152 | + e, s = result.stdout.split() |
| 153 | + metrics.put_metric( |
| 154 | + "startup", |
| 155 | + int(e) - int(s), |
| 156 | + unit="Microseconds", |
| 157 | + ) |
| 158 | + |
| 159 | + # Cleanup mounts and jailer dirs |
| 160 | + for d in os.listdir(mounts_paths): |
| 161 | + utils.check_output(f"umount {mounts_paths}/{d}") |
| 162 | + shutil.rmtree(DEFAULT_CHROOT_PATH) |
0 commit comments