1840b1e01SJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause 2840b1e01SJuraj Linkeš# Copyright(c) 2023 PANTHEON.tech s.r.o. 3840b1e01SJuraj Linkeš 46ef07151SJuraj Linkeš"""CPU core representation and filtering. 56ef07151SJuraj Linkeš 66ef07151SJuraj LinkešThis module provides a unified representation of logical CPU cores along 76ef07151SJuraj Linkešwith filtering capabilities. 86ef07151SJuraj Linkeš 96ef07151SJuraj LinkešWhen symmetric multiprocessing (SMP or multithreading) is enabled on a server, 106ef07151SJuraj Linkešthe physical CPU cores are split into logical CPU cores with different IDs. 116ef07151SJuraj Linkeš 126ef07151SJuraj Linkeš:class:`LogicalCoreCountFilter` filters by the number of logical cores. It's possible to specify 136ef07151SJuraj Linkešthe socket from which to filter the number of logical cores. It's also possible to not use all 146ef07151SJuraj Linkešlogical CPU cores from each physical core (e.g. only the first logical core of each physical core). 156ef07151SJuraj Linkeš 166ef07151SJuraj Linkeš:class:`LogicalCoreListFilter` filters by logical core IDs. This mostly checks that 176ef07151SJuraj Linkešthe logical cores are actually present on the server. 186ef07151SJuraj Linkeš""" 196ef07151SJuraj Linkeš 20840b1e01SJuraj Linkešimport dataclasses 21840b1e01SJuraj Linkešfrom abc import ABC, abstractmethod 22840b1e01SJuraj Linkešfrom collections.abc import Iterable, ValuesView 23840b1e01SJuraj Linkešfrom dataclasses import dataclass 24840b1e01SJuraj Linkeš 25840b1e01SJuraj Linkešfrom framework.utils import expand_range 26840b1e01SJuraj Linkeš 27840b1e01SJuraj Linkeš 28840b1e01SJuraj Linkeš@dataclass(slots=True, frozen=True) 29*3e967643SJuraj Linkešclass LogicalCore: 306ef07151SJuraj Linkeš """Representation of a logical CPU core. 316ef07151SJuraj Linkeš 326ef07151SJuraj Linkeš A physical core is represented in OS by multiple logical cores (lcores) 336ef07151SJuraj Linkeš if CPU multithreading is enabled. When multithreading is disabled, their IDs are the same. 346ef07151SJuraj Linkeš 356ef07151SJuraj Linkeš Attributes: 366ef07151SJuraj Linkeš lcore: The logical core ID of a CPU core. It's the same as `core` with 376ef07151SJuraj Linkeš disabled multithreading. 386ef07151SJuraj Linkeš core: The physical core ID of a CPU core. 396ef07151SJuraj Linkeš socket: The physical socket ID where the CPU resides. 406ef07151SJuraj Linkeš node: The NUMA node ID where the CPU resides. 41840b1e01SJuraj Linkeš """ 42840b1e01SJuraj Linkeš 43840b1e01SJuraj Linkeš lcore: int 44840b1e01SJuraj Linkeš core: int 45840b1e01SJuraj Linkeš socket: int 46840b1e01SJuraj Linkeš node: int 47840b1e01SJuraj Linkeš 48840b1e01SJuraj Linkeš def __int__(self) -> int: 496ef07151SJuraj Linkeš """The CPU is best represented by the logical core, as that's what we configure in EAL.""" 50840b1e01SJuraj Linkeš return self.lcore 51840b1e01SJuraj Linkeš 52840b1e01SJuraj Linkeš 53*3e967643SJuraj Linkešclass LogicalCoreList: 546ef07151SJuraj Linkeš r"""A unified way to store :class:`LogicalCore`\s. 55840b1e01SJuraj Linkeš 566ef07151SJuraj Linkeš Create a unified format used across the framework and allow the user to use 576ef07151SJuraj Linkeš either a :class:`str` representation (using ``str(instance)`` or directly in f-strings) 586ef07151SJuraj Linkeš or a :class:`list` representation (by accessing the `lcore_list` property, 596ef07151SJuraj Linkeš which stores logical core IDs). 60840b1e01SJuraj Linkeš """ 61840b1e01SJuraj Linkeš 62840b1e01SJuraj Linkeš _lcore_list: list[int] 63840b1e01SJuraj Linkeš _lcore_str: str 64840b1e01SJuraj Linkeš 65840b1e01SJuraj Linkeš def __init__(self, lcore_list: list[int] | list[str] | list[LogicalCore] | str): 666ef07151SJuraj Linkeš """Process `lcore_list`, then sort. 676ef07151SJuraj Linkeš 686ef07151SJuraj Linkeš There are four supported logical core list formats:: 696ef07151SJuraj Linkeš 706ef07151SJuraj Linkeš lcore_list=[LogicalCore1, LogicalCore2] # a list of LogicalCores 716ef07151SJuraj Linkeš lcore_list=[0,1,2,3] # a list of int indices 726ef07151SJuraj Linkeš lcore_list=['0','1','2-3'] # a list of str indices; ranges are supported 736ef07151SJuraj Linkeš lcore_list='0,1,2-3' # a comma delimited str of indices; ranges are supported 746ef07151SJuraj Linkeš 756ef07151SJuraj Linkeš Args: 766ef07151SJuraj Linkeš lcore_list: Various ways to represent multiple logical cores. 776ef07151SJuraj Linkeš Empty `lcore_list` is allowed. 786ef07151SJuraj Linkeš """ 79840b1e01SJuraj Linkeš self._lcore_list = [] 80840b1e01SJuraj Linkeš if isinstance(lcore_list, str): 81840b1e01SJuraj Linkeš lcore_list = lcore_list.split(",") 82840b1e01SJuraj Linkeš for lcore in lcore_list: 83840b1e01SJuraj Linkeš if isinstance(lcore, str): 84840b1e01SJuraj Linkeš self._lcore_list.extend(expand_range(lcore)) 85840b1e01SJuraj Linkeš else: 86840b1e01SJuraj Linkeš self._lcore_list.append(int(lcore)) 87840b1e01SJuraj Linkeš 88840b1e01SJuraj Linkeš # the input lcores may not be sorted 89840b1e01SJuraj Linkeš self._lcore_list.sort() 90840b1e01SJuraj Linkeš self._lcore_str = f'{",".join(self._get_consecutive_lcores_range(self._lcore_list))}' 91840b1e01SJuraj Linkeš 92840b1e01SJuraj Linkeš @property 93840b1e01SJuraj Linkeš def lcore_list(self) -> list[int]: 946ef07151SJuraj Linkeš """The logical core IDs.""" 95840b1e01SJuraj Linkeš return self._lcore_list 96840b1e01SJuraj Linkeš 97840b1e01SJuraj Linkeš def _get_consecutive_lcores_range(self, lcore_ids_list: list[int]) -> list[str]: 98840b1e01SJuraj Linkeš formatted_core_list = [] 99840b1e01SJuraj Linkeš segment = lcore_ids_list[:1] 100840b1e01SJuraj Linkeš for lcore_id in lcore_ids_list[1:]: 101840b1e01SJuraj Linkeš if lcore_id - segment[-1] == 1: 102840b1e01SJuraj Linkeš segment.append(lcore_id) 103840b1e01SJuraj Linkeš else: 104840b1e01SJuraj Linkeš formatted_core_list.append( 105840b1e01SJuraj Linkeš f"{segment[0]}-{segment[-1]}" if len(segment) > 1 else f"{segment[0]}" 106840b1e01SJuraj Linkeš ) 107840b1e01SJuraj Linkeš current_core_index = lcore_ids_list.index(lcore_id) 108840b1e01SJuraj Linkeš formatted_core_list.extend( 109840b1e01SJuraj Linkeš self._get_consecutive_lcores_range(lcore_ids_list[current_core_index:]) 110840b1e01SJuraj Linkeš ) 111840b1e01SJuraj Linkeš segment.clear() 112840b1e01SJuraj Linkeš break 113840b1e01SJuraj Linkeš if len(segment) > 0: 114840b1e01SJuraj Linkeš formatted_core_list.append( 115840b1e01SJuraj Linkeš f"{segment[0]}-{segment[-1]}" if len(segment) > 1 else f"{segment[0]}" 116840b1e01SJuraj Linkeš ) 117840b1e01SJuraj Linkeš return formatted_core_list 118840b1e01SJuraj Linkeš 119840b1e01SJuraj Linkeš def __str__(self) -> str: 1206ef07151SJuraj Linkeš """The consecutive ranges of logical core IDs.""" 121840b1e01SJuraj Linkeš return self._lcore_str 122840b1e01SJuraj Linkeš 123840b1e01SJuraj Linkeš 124840b1e01SJuraj Linkeš@dataclasses.dataclass(slots=True, frozen=True) 125840b1e01SJuraj Linkešclass LogicalCoreCount(object): 1266ef07151SJuraj Linkeš """Define the number of logical cores per physical cores per sockets.""" 127840b1e01SJuraj Linkeš 1286ef07151SJuraj Linkeš #: Use this many logical cores per each physical core. 129840b1e01SJuraj Linkeš lcores_per_core: int = 1 1306ef07151SJuraj Linkeš #: Use this many physical cores per each socket. 131840b1e01SJuraj Linkeš cores_per_socket: int = 2 1326ef07151SJuraj Linkeš #: Use this many sockets. 133840b1e01SJuraj Linkeš socket_count: int = 1 1346ef07151SJuraj Linkeš #: Use exactly these sockets. This takes precedence over `socket_count`, 1356ef07151SJuraj Linkeš #: so when `sockets` is not :data:`None`, `socket_count` is ignored. 136840b1e01SJuraj Linkeš sockets: list[int] | None = None 137840b1e01SJuraj Linkeš 138840b1e01SJuraj Linkeš 139840b1e01SJuraj Linkešclass LogicalCoreFilter(ABC): 1406ef07151SJuraj Linkeš """Common filtering class. 1416ef07151SJuraj Linkeš 1426ef07151SJuraj Linkeš Each filter needs to be implemented in a subclass. This base class sorts the list of cores 1436ef07151SJuraj Linkeš and defines the filtering method, which must be implemented by subclasses. 144840b1e01SJuraj Linkeš """ 145840b1e01SJuraj Linkeš 146840b1e01SJuraj Linkeš _filter_specifier: LogicalCoreCount | LogicalCoreList 147840b1e01SJuraj Linkeš _lcores_to_filter: list[LogicalCore] 148840b1e01SJuraj Linkeš 149840b1e01SJuraj Linkeš def __init__( 150840b1e01SJuraj Linkeš self, 151840b1e01SJuraj Linkeš lcore_list: list[LogicalCore], 152840b1e01SJuraj Linkeš filter_specifier: LogicalCoreCount | LogicalCoreList, 153840b1e01SJuraj Linkeš ascending: bool = True, 154840b1e01SJuraj Linkeš ): 1556ef07151SJuraj Linkeš """Filter according to the input filter specifier. 1566ef07151SJuraj Linkeš 1576ef07151SJuraj Linkeš The input `lcore_list` is copied and sorted by physical core before filtering. 1586ef07151SJuraj Linkeš The list is copied so that the original is left intact. 1596ef07151SJuraj Linkeš 1606ef07151SJuraj Linkeš Args: 1616ef07151SJuraj Linkeš lcore_list: The logical CPU cores to filter. 1626ef07151SJuraj Linkeš filter_specifier: Filter cores from `lcore_list` according to this filter. 1636ef07151SJuraj Linkeš ascending: Sort cores in ascending order (lowest to highest IDs). If data:`False`, 1646ef07151SJuraj Linkeš sort in descending order. 1656ef07151SJuraj Linkeš """ 166840b1e01SJuraj Linkeš self._filter_specifier = filter_specifier 167840b1e01SJuraj Linkeš 168840b1e01SJuraj Linkeš # sorting by core is needed in case hyperthreading is enabled 169840b1e01SJuraj Linkeš self._lcores_to_filter = sorted(lcore_list, key=lambda x: x.core, reverse=not ascending) 170840b1e01SJuraj Linkeš self.filter() 171840b1e01SJuraj Linkeš 172840b1e01SJuraj Linkeš @abstractmethod 173840b1e01SJuraj Linkeš def filter(self) -> list[LogicalCore]: 1746ef07151SJuraj Linkeš r"""Filter the cores. 1756ef07151SJuraj Linkeš 1766ef07151SJuraj Linkeš Use `self._filter_specifier` to filter `self._lcores_to_filter` and return 1776ef07151SJuraj Linkeš the filtered :class:`LogicalCore`\s. 1786ef07151SJuraj Linkeš `self._lcores_to_filter` is a sorted copy of the original list, so it may be modified. 1796ef07151SJuraj Linkeš 1806ef07151SJuraj Linkeš Returns: 1816ef07151SJuraj Linkeš The filtered cores. 182840b1e01SJuraj Linkeš """ 183840b1e01SJuraj Linkeš 184840b1e01SJuraj Linkeš 185840b1e01SJuraj Linkešclass LogicalCoreCountFilter(LogicalCoreFilter): 1866ef07151SJuraj Linkeš """Filter cores by specified counts. 1876ef07151SJuraj Linkeš 188840b1e01SJuraj Linkeš Filter the input list of LogicalCores according to specified rules: 1896ef07151SJuraj Linkeš 1906ef07151SJuraj Linkeš * The input `filter_specifier` is :class:`LogicalCoreCount`, 1916ef07151SJuraj Linkeš * Use cores from the specified number of sockets or from the specified socket ids, 1926ef07151SJuraj Linkeš * If `sockets` is specified, it takes precedence over `socket_count`, 1936ef07151SJuraj Linkeš * From each of those sockets, use only `cores_per_socket` of cores, 1946ef07151SJuraj Linkeš * And for each core, use `lcores_per_core` of logical cores. Hypertheading 195840b1e01SJuraj Linkeš must be enabled for this to take effect. 196840b1e01SJuraj Linkeš """ 197840b1e01SJuraj Linkeš 198840b1e01SJuraj Linkeš _filter_specifier: LogicalCoreCount 199840b1e01SJuraj Linkeš 200840b1e01SJuraj Linkeš def filter(self) -> list[LogicalCore]: 2016ef07151SJuraj Linkeš """Filter the cores according to :class:`LogicalCoreCount`. 2026ef07151SJuraj Linkeš 2036ef07151SJuraj Linkeš Start by filtering the allowed sockets. The cores matching the allowed sockets are returned. 2046ef07151SJuraj Linkeš The cores of each socket are stored in separate lists. 2056ef07151SJuraj Linkeš 2066ef07151SJuraj Linkeš Then filter the allowed physical cores from those lists of cores per socket. When filtering 2076ef07151SJuraj Linkeš physical cores, store the desired number of logical cores per physical core which then 2086ef07151SJuraj Linkeš together constitute the final filtered list. 2096ef07151SJuraj Linkeš 2106ef07151SJuraj Linkeš Returns: 2116ef07151SJuraj Linkeš The filtered cores. 2126ef07151SJuraj Linkeš """ 213840b1e01SJuraj Linkeš sockets_to_filter = self._filter_sockets(self._lcores_to_filter) 214840b1e01SJuraj Linkeš filtered_lcores = [] 215840b1e01SJuraj Linkeš for socket_to_filter in sockets_to_filter: 216840b1e01SJuraj Linkeš filtered_lcores.extend(self._filter_cores_from_socket(socket_to_filter)) 217840b1e01SJuraj Linkeš return filtered_lcores 218840b1e01SJuraj Linkeš 219840b1e01SJuraj Linkeš def _filter_sockets( 220840b1e01SJuraj Linkeš self, lcores_to_filter: Iterable[LogicalCore] 221840b1e01SJuraj Linkeš ) -> ValuesView[list[LogicalCore]]: 2226ef07151SJuraj Linkeš """Filter a list of cores per each allowed socket. 2236ef07151SJuraj Linkeš 2246ef07151SJuraj Linkeš The sockets may be specified in two ways, either a number or a specific list of sockets. 2256ef07151SJuraj Linkeš In case of a specific list, we just need to return the cores from those sockets. 2266ef07151SJuraj Linkeš If filtering a number of cores, we need to go through all cores and note which sockets 2276ef07151SJuraj Linkeš appear and only filter from the first n that appear. 2286ef07151SJuraj Linkeš 2296ef07151SJuraj Linkeš Args: 2306ef07151SJuraj Linkeš lcores_to_filter: The cores to filter. These must be sorted by the physical core. 2316ef07151SJuraj Linkeš 2326ef07151SJuraj Linkeš Returns: 2336ef07151SJuraj Linkeš A list of lists of logical CPU cores. Each list contains cores from one socket. 234840b1e01SJuraj Linkeš """ 235840b1e01SJuraj Linkeš allowed_sockets: set[int] = set() 236840b1e01SJuraj Linkeš socket_count = self._filter_specifier.socket_count 237840b1e01SJuraj Linkeš if self._filter_specifier.sockets: 2386ef07151SJuraj Linkeš # when sockets in filter is specified, the sockets are already set 239840b1e01SJuraj Linkeš socket_count = len(self._filter_specifier.sockets) 240840b1e01SJuraj Linkeš allowed_sockets = set(self._filter_specifier.sockets) 241840b1e01SJuraj Linkeš 2426ef07151SJuraj Linkeš # filter socket_count sockets from all sockets by checking the socket of each CPU 243840b1e01SJuraj Linkeš filtered_lcores: dict[int, list[LogicalCore]] = {} 244840b1e01SJuraj Linkeš for lcore in lcores_to_filter: 245840b1e01SJuraj Linkeš if not self._filter_specifier.sockets: 2466ef07151SJuraj Linkeš # this is when sockets is not set, so we do the actual filtering 2476ef07151SJuraj Linkeš # when it is set, allowed_sockets is already defined and can't be changed 248840b1e01SJuraj Linkeš if len(allowed_sockets) < socket_count: 2496ef07151SJuraj Linkeš # allowed_sockets is a set, so adding an existing socket won't re-add it 250840b1e01SJuraj Linkeš allowed_sockets.add(lcore.socket) 251840b1e01SJuraj Linkeš if lcore.socket in allowed_sockets: 2526ef07151SJuraj Linkeš # separate lcores into sockets; this makes it easier in further processing 253840b1e01SJuraj Linkeš if lcore.socket in filtered_lcores: 254840b1e01SJuraj Linkeš filtered_lcores[lcore.socket].append(lcore) 255840b1e01SJuraj Linkeš else: 256840b1e01SJuraj Linkeš filtered_lcores[lcore.socket] = [lcore] 257840b1e01SJuraj Linkeš 258840b1e01SJuraj Linkeš if len(allowed_sockets) < socket_count: 259840b1e01SJuraj Linkeš raise ValueError( 260840b1e01SJuraj Linkeš f"The actual number of sockets from which to use cores " 261840b1e01SJuraj Linkeš f"({len(allowed_sockets)}) is lower than required ({socket_count})." 262840b1e01SJuraj Linkeš ) 263840b1e01SJuraj Linkeš 264840b1e01SJuraj Linkeš return filtered_lcores.values() 265840b1e01SJuraj Linkeš 266840b1e01SJuraj Linkeš def _filter_cores_from_socket( 267840b1e01SJuraj Linkeš self, lcores_to_filter: Iterable[LogicalCore] 268840b1e01SJuraj Linkeš ) -> list[LogicalCore]: 2696ef07151SJuraj Linkeš """Filter a list of cores from the given socket. 270840b1e01SJuraj Linkeš 2716ef07151SJuraj Linkeš Go through the cores and note how many logical cores per physical core have been filtered. 2726ef07151SJuraj Linkeš 2736ef07151SJuraj Linkeš Returns: 2746ef07151SJuraj Linkeš The filtered logical CPU cores. 2756ef07151SJuraj Linkeš """ 276840b1e01SJuraj Linkeš # no need to use ordered dict, from Python3.7 the dict 277840b1e01SJuraj Linkeš # insertion order is preserved (LIFO). 278840b1e01SJuraj Linkeš lcore_count_per_core_map: dict[int, int] = {} 279840b1e01SJuraj Linkeš filtered_lcores = [] 280840b1e01SJuraj Linkeš for lcore in lcores_to_filter: 281840b1e01SJuraj Linkeš if lcore.core in lcore_count_per_core_map: 282840b1e01SJuraj Linkeš current_core_lcore_count = lcore_count_per_core_map[lcore.core] 283840b1e01SJuraj Linkeš if self._filter_specifier.lcores_per_core > current_core_lcore_count: 284840b1e01SJuraj Linkeš # only add lcores of the given core 285840b1e01SJuraj Linkeš lcore_count_per_core_map[lcore.core] += 1 286840b1e01SJuraj Linkeš filtered_lcores.append(lcore) 287840b1e01SJuraj Linkeš else: 288840b1e01SJuraj Linkeš # we have enough lcores per this core 289840b1e01SJuraj Linkeš continue 290840b1e01SJuraj Linkeš elif self._filter_specifier.cores_per_socket > len(lcore_count_per_core_map): 291840b1e01SJuraj Linkeš # only add cores if we need more 292840b1e01SJuraj Linkeš lcore_count_per_core_map[lcore.core] = 1 293840b1e01SJuraj Linkeš filtered_lcores.append(lcore) 294840b1e01SJuraj Linkeš else: 295840b1e01SJuraj Linkeš # we have enough cores 296840b1e01SJuraj Linkeš break 297840b1e01SJuraj Linkeš 298840b1e01SJuraj Linkeš cores_per_socket = len(lcore_count_per_core_map) 299840b1e01SJuraj Linkeš if cores_per_socket < self._filter_specifier.cores_per_socket: 300840b1e01SJuraj Linkeš raise ValueError( 301840b1e01SJuraj Linkeš f"The actual number of cores per socket ({cores_per_socket}) " 302840b1e01SJuraj Linkeš f"is lower than required ({self._filter_specifier.cores_per_socket})." 303840b1e01SJuraj Linkeš ) 304840b1e01SJuraj Linkeš 305840b1e01SJuraj Linkeš lcores_per_core = lcore_count_per_core_map[filtered_lcores[-1].core] 306840b1e01SJuraj Linkeš if lcores_per_core < self._filter_specifier.lcores_per_core: 307840b1e01SJuraj Linkeš raise ValueError( 308840b1e01SJuraj Linkeš f"The actual number of logical cores per core ({lcores_per_core}) " 309840b1e01SJuraj Linkeš f"is lower than required ({self._filter_specifier.lcores_per_core})." 310840b1e01SJuraj Linkeš ) 311840b1e01SJuraj Linkeš 312840b1e01SJuraj Linkeš return filtered_lcores 313840b1e01SJuraj Linkeš 314840b1e01SJuraj Linkeš 315840b1e01SJuraj Linkešclass LogicalCoreListFilter(LogicalCoreFilter): 3166ef07151SJuraj Linkeš """Filter the logical CPU cores by logical CPU core IDs. 3176ef07151SJuraj Linkeš 3186ef07151SJuraj Linkeš This is a simple filter that looks at logical CPU IDs and only filter those that match. 3196ef07151SJuraj Linkeš 3206ef07151SJuraj Linkeš The input filter is :class:`LogicalCoreList`. An empty LogicalCoreList won't filter anything. 321840b1e01SJuraj Linkeš """ 322840b1e01SJuraj Linkeš 323840b1e01SJuraj Linkeš _filter_specifier: LogicalCoreList 324840b1e01SJuraj Linkeš 325840b1e01SJuraj Linkeš def filter(self) -> list[LogicalCore]: 3266ef07151SJuraj Linkeš """Filter based on logical CPU core ID. 3276ef07151SJuraj Linkeš 3286ef07151SJuraj Linkeš Return: 3296ef07151SJuraj Linkeš The filtered logical CPU cores. 3306ef07151SJuraj Linkeš """ 331840b1e01SJuraj Linkeš if not len(self._filter_specifier.lcore_list): 332840b1e01SJuraj Linkeš return self._lcores_to_filter 333840b1e01SJuraj Linkeš 334840b1e01SJuraj Linkeš filtered_lcores = [] 335840b1e01SJuraj Linkeš for core in self._lcores_to_filter: 336840b1e01SJuraj Linkeš if core.lcore in self._filter_specifier.lcore_list: 337840b1e01SJuraj Linkeš filtered_lcores.append(core) 338840b1e01SJuraj Linkeš 339840b1e01SJuraj Linkeš if len(filtered_lcores) != len(self._filter_specifier.lcore_list): 340840b1e01SJuraj Linkeš raise ValueError( 341840b1e01SJuraj Linkeš f"Not all logical cores from {self._filter_specifier.lcore_list} " 342840b1e01SJuraj Linkeš f"were found among {self._lcores_to_filter}" 343840b1e01SJuraj Linkeš ) 344840b1e01SJuraj Linkeš 345840b1e01SJuraj Linkeš return filtered_lcores 346840b1e01SJuraj Linkeš 347840b1e01SJuraj Linkeš 348840b1e01SJuraj Linkešdef lcore_filter( 349840b1e01SJuraj Linkeš core_list: list[LogicalCore], 350840b1e01SJuraj Linkeš filter_specifier: LogicalCoreCount | LogicalCoreList, 351840b1e01SJuraj Linkeš ascending: bool, 352840b1e01SJuraj Linkeš) -> LogicalCoreFilter: 3536ef07151SJuraj Linkeš """Factory for providing the filter that corresponds to `filter_specifier`. 3546ef07151SJuraj Linkeš 3556ef07151SJuraj Linkeš Args: 3566ef07151SJuraj Linkeš core_list: The logical CPU cores to filter. 3576ef07151SJuraj Linkeš filter_specifier: The filter to use. 3586ef07151SJuraj Linkeš ascending: Sort cores in ascending order (lowest to highest IDs). If :data:`False`, 3596ef07151SJuraj Linkeš sort in descending order. 3606ef07151SJuraj Linkeš 3616ef07151SJuraj Linkeš Returns: 3626ef07151SJuraj Linkeš The filter that corresponds to `filter_specifier`. 3636ef07151SJuraj Linkeš """ 364840b1e01SJuraj Linkeš if isinstance(filter_specifier, LogicalCoreList): 365840b1e01SJuraj Linkeš return LogicalCoreListFilter(core_list, filter_specifier, ascending) 366840b1e01SJuraj Linkeš elif isinstance(filter_specifier, LogicalCoreCount): 367840b1e01SJuraj Linkeš return LogicalCoreCountFilter(core_list, filter_specifier, ascending) 368840b1e01SJuraj Linkeš else: 369840b1e01SJuraj Linkeš raise ValueError(f"Unsupported filter r{filter_specifier}") 370