Skip to content

Commit 3b150d6

Browse files
authored
Port more helper classes from 2.x (#1701)
Signed-off-by: yiliu30 <[email protected]>
1 parent f21afbb commit 3b150d6

File tree

4 files changed

+169
-1
lines changed

4 files changed

+169
-1
lines changed

neural_compressor/common/utils/utility.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@
1515
# See the License for the specific language governing permissions and
1616
# limitations under the License.
1717

18+
import importlib
19+
import subprocess
1820
import time
1921

22+
import cpuinfo
23+
import psutil
24+
2025
from neural_compressor.common.utils import TuningLogger, logger
2126

2227
__all__ = [
@@ -26,9 +31,127 @@
2631
"set_tensorboard",
2732
"dump_elapsed_time",
2833
"log_quant_execution",
34+
"singleton",
35+
"LazyImport",
36+
"CpuInfo",
2937
]
3038

3139

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+
32155
def dump_elapsed_time(customized_msg=""):
33156
"""Get the elapsed time for decorated functions.
34157

requirements_ort.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ numpy
22
onnx
33
onnxruntime
44
onnxruntime-extensions
5+
psutil
6+
py-cpuinfo
57
pydantic

requirements_pt.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
auto-round
22
intel_extension_for_pytorch
33
peft
4+
psutil
45
py-cpuinfo
56
pydantic
67
torch

test/3x/common/test_utility.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@
1010
import unittest
1111

1212
from neural_compressor.common import options
13-
from neural_compressor.common.utils import set_random_seed, set_resume_from, set_tensorboard, set_workspace
13+
from neural_compressor.common.utils import (
14+
CpuInfo,
15+
LazyImport,
16+
set_random_seed,
17+
set_resume_from,
18+
set_tensorboard,
19+
set_workspace,
20+
singleton,
21+
)
1422

1523

1624
class TestOptions(unittest.TestCase):
@@ -55,5 +63,39 @@ def test_set_tensorboard(self):
5563
set_tensorboard(tensorboard)
5664

5765

66+
class TestCPUInfo(unittest.TestCase):
67+
def test_cpu_info(self):
68+
cpu_info = CpuInfo()
69+
assert cpu_info.cores_per_socket > 0, "CPU count should be greater than 0"
70+
assert isinstance(cpu_info.bf16, bool), "bf16 should be a boolean"
71+
assert isinstance(cpu_info.vnni, bool), "avx512 should be a boolean"
72+
73+
74+
class TestLazyImport(unittest.TestCase):
75+
def test_lazy_import(self):
76+
# Test import
77+
pydantic = LazyImport("pydantic")
78+
assert pydantic.__name__ == "pydantic", "pydantic should be imported"
79+
80+
def test_lazy_import_error(self):
81+
# Test import error
82+
with self.assertRaises(ImportError):
83+
non_existent_module = LazyImport("non_existent_module")
84+
non_existent_module.non_existent_function()
85+
86+
87+
class TestSingletonDecorator:
88+
def test_singleton_decorator(self):
89+
@singleton
90+
class TestSingleton:
91+
def __init__(self):
92+
self.value = 0
93+
94+
instance = TestSingleton()
95+
instance.value = 1
96+
instance2 = TestSingleton()
97+
assert instance2.value == 1, "Singleton should return the same instance"
98+
99+
58100
if __name__ == "__main__":
59101
unittest.main()

0 commit comments

Comments
 (0)