|
15 | 15 | # See the License for the specific language governing permissions and |
16 | 16 | # limitations under the License. |
17 | 17 |
|
| 18 | +import importlib |
| 19 | +import subprocess |
18 | 20 | import time |
19 | 21 |
|
| 22 | +import cpuinfo |
| 23 | +import psutil |
| 24 | + |
20 | 25 | from neural_compressor.common.utils import TuningLogger, logger |
21 | 26 |
|
22 | 27 | __all__ = [ |
|
26 | 31 | "set_tensorboard", |
27 | 32 | "dump_elapsed_time", |
28 | 33 | "log_quant_execution", |
| 34 | + "singleton", |
| 35 | + "LazyImport", |
| 36 | + "CpuInfo", |
29 | 37 | ] |
30 | 38 |
|
31 | 39 |
|
| 40 | +def singleton(cls): |
| 41 | + """Singleton decorator.""" |
| 42 | + |
| 43 | + instances = {} |
| 44 | + |
| 45 | + def _singleton(*args, **kw): |
| 46 | + """Create a singleton object.""" |
| 47 | + if cls not in instances: |
| 48 | + instances[cls] = cls(*args, **kw) |
| 49 | + return instances[cls] |
| 50 | + |
| 51 | + return _singleton |
| 52 | + |
| 53 | + |
| 54 | +class LazyImport(object): |
| 55 | + """Lazy import python module till use.""" |
| 56 | + |
| 57 | + def __init__(self, module_name): |
| 58 | + """Init LazyImport object. |
| 59 | +
|
| 60 | + Args: |
| 61 | + module_name (string): The name of module imported later |
| 62 | + """ |
| 63 | + self.module_name = module_name |
| 64 | + self.module = None |
| 65 | + |
| 66 | + def __getattr__(self, name): |
| 67 | + """Get the attributes of the module by name.""" |
| 68 | + try: |
| 69 | + self.module = importlib.import_module(self.module_name) |
| 70 | + mod = getattr(self.module, name) |
| 71 | + except: |
| 72 | + spec = importlib.util.find_spec(str(self.module_name + "." + name)) |
| 73 | + mod = importlib.util.module_from_spec(spec) |
| 74 | + spec.loader.exec_module(mod) |
| 75 | + return mod |
| 76 | + |
| 77 | + def __call__(self, *args, **kwargs): |
| 78 | + """Call the function in that module.""" |
| 79 | + function_name = self.module_name.split(".")[-1] |
| 80 | + module_name = self.module_name.split(f".{function_name}")[0] |
| 81 | + self.module = importlib.import_module(module_name) |
| 82 | + function = getattr(self.module, function_name) |
| 83 | + return function(*args, **kwargs) |
| 84 | + |
| 85 | + |
| 86 | +@singleton |
| 87 | +class CpuInfo(object): |
| 88 | + """CPU info collection.""" |
| 89 | + |
| 90 | + def __init__(self): |
| 91 | + """Get whether the cpu numerical format is bf16, the number of sockets, cores and cores per socket.""" |
| 92 | + self._bf16 = False |
| 93 | + self._vnni = False |
| 94 | + info = cpuinfo.get_cpu_info() |
| 95 | + if "arch" in info and "X86" in info["arch"]: |
| 96 | + cpuid = cpuinfo.CPUID() |
| 97 | + max_extension_support = cpuid.get_max_extension_support() |
| 98 | + if max_extension_support >= 7: |
| 99 | + ecx = cpuid._run_asm( |
| 100 | + b"\x31\xC9", # xor ecx, ecx |
| 101 | + b"\xB8\x07\x00\x00\x00" b"\x0f\xa2" b"\x89\xC8" b"\xC3", # mov eax, 7 # cpuid # mov ax, cx # ret |
| 102 | + ) |
| 103 | + self._vnni = bool(ecx & (1 << 11)) |
| 104 | + eax = cpuid._run_asm( |
| 105 | + b"\xB9\x01\x00\x00\x00", # mov ecx, 1 |
| 106 | + b"\xB8\x07\x00\x00\x00" b"\x0f\xa2" b"\xC3", # mov eax, 7 # cpuid # ret |
| 107 | + ) |
| 108 | + self._bf16 = bool(eax & (1 << 5)) |
| 109 | + # TODO: The implementation will be refined in the future. |
| 110 | + # https://github.com/intel/neural-compressor/tree/detect_sockets |
| 111 | + if "arch" in info and "ARM" in info["arch"]: # pragma: no cover |
| 112 | + self._sockets = 1 |
| 113 | + else: |
| 114 | + self._sockets = self.get_number_of_sockets() |
| 115 | + self._cores = psutil.cpu_count(logical=False) |
| 116 | + self._cores_per_socket = int(self._cores / self._sockets) |
| 117 | + |
| 118 | + @property |
| 119 | + def bf16(self): |
| 120 | + """Get whether it is bf16.""" |
| 121 | + return self._bf16 |
| 122 | + |
| 123 | + @property |
| 124 | + def vnni(self): |
| 125 | + """Get whether it is vnni.""" |
| 126 | + return self._vnni |
| 127 | + |
| 128 | + @property |
| 129 | + def cores_per_socket(self): |
| 130 | + """Get the cores per socket.""" |
| 131 | + return self._cores_per_socket |
| 132 | + |
| 133 | + def get_number_of_sockets(self) -> int: |
| 134 | + """Get number of sockets in platform.""" |
| 135 | + cmd = "cat /proc/cpuinfo | grep 'physical id' | sort -u | wc -l" |
| 136 | + if psutil.WINDOWS: |
| 137 | + cmd = r'wmic cpu get DeviceID | C:\Windows\System32\find.exe /C "CPU"' |
| 138 | + elif psutil.MACOS: # pragma: no cover |
| 139 | + cmd = "sysctl -n machdep.cpu.core_count" |
| 140 | + |
| 141 | + with subprocess.Popen( |
| 142 | + args=cmd, |
| 143 | + shell=True, |
| 144 | + stdout=subprocess.PIPE, |
| 145 | + stderr=subprocess.STDOUT, |
| 146 | + universal_newlines=False, |
| 147 | + ) as proc: |
| 148 | + proc.wait() |
| 149 | + if proc.stdout: |
| 150 | + for line in proc.stdout: |
| 151 | + return int(line.decode("utf-8", errors="ignore").strip()) |
| 152 | + return 0 |
| 153 | + |
| 154 | + |
32 | 155 | def dump_elapsed_time(customized_msg=""): |
33 | 156 | """Get the elapsed time for decorated functions. |
34 | 157 |
|
|
0 commit comments