Start the child processes.
(self)
| 219 | executor: Optional[ThreadPoolExecutor] = None |
| 220 | |
| 221 | def initialize(self) -> None: |
| 222 | """ |
| 223 | Start the child processes. |
| 224 | """ |
| 225 | assert (self.processes is None) == (self.executor is None) |
| 226 | if self.processes is not None: |
| 227 | return |
| 228 | |
| 229 | devices = self.get_device_list() |
| 230 | log.debug("Sub-process autotune device list: %s", devices) |
| 231 | |
| 232 | # Launch the child processes and push a msg to "warm up" |
| 233 | self.processes = queue.Queue() |
| 234 | for device in devices: |
| 235 | p = TuningProcess(device=device) |
| 236 | p.initialize() |
| 237 | p.put(Ping()) |
| 238 | self.processes.put(p) |
| 239 | |
| 240 | # Wait for the initialization to finish |
| 241 | for p in self.processes.queue: |
| 242 | assert isinstance(p.get(), Pong) |
| 243 | |
| 244 | # Use a thread pool to manage distributing work to the subprocesses. |
| 245 | # Threads block on an available process, so it makes sense to match |
| 246 | # the number of threads with the number of devices. |
| 247 | self.executor = ThreadPoolExecutor(max_workers=len(devices)) |
| 248 | |
| 249 | # Register the exit handler for the parent process so it will terminate |
| 250 | # the child processes. |
| 251 | global EXIT_HANDLER_REGISTERED |
| 252 | if not EXIT_HANDLER_REGISTERED: |
| 253 | EXIT_HANDLER_REGISTERED = True |
| 254 | import atexit |
| 255 | |
| 256 | atexit.register(self.terminate) |
| 257 | |
| 258 | def get_device_list(self) -> Sequence[Optional[int]]: |
| 259 | """ |