File size: 5,571 Bytes
49b8205 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | """xeon_runtime.py — Ativação do runtime Intel Xeon (AVX512 + AMX + IPEX + OneDNN).
Adaptado do projeto BiGRU_T_version (PowerMachine), com simplificações para
rodar em CPU-only e degradar graciosamente quando IPEX/AMX não estiverem
disponíveis. A ativação é SEMPRE realizada (mesmo que parcial), conforme
requisito do usuário: "ativar xeon_runtime.py".
Otimizações aplicadas:
1. OMP_NUM_THREADS / MKL_NUM_THREADS = núcleos físicos
2. KMP_AFFINITY=granularity=fine,compact
3. MKL_ENABLE_INSTRUCTIONS=AVX512 (se suportado)
4. ONEDNN_MAX_CPU_ISA=AMX_INT8 (se suportado)
5. DNNL_PRIMITIVE_CACHE_CAPACITY=1024
6. MKL_DYNAMIC=FALSE
7. IPEX (intel_extension_for_pytorch) — se disponível, ipex.optimize(model)
8. torch.set_float32_matmul_precision("high")
9. torch.backends.cudnn.benchmark = True (no-op em CPU)
Uso:
from cnn_bigru.utils.xeon_runtime import optimize_xeon_environment
N_CORES = optimize_xeon_environment() # chamar ANTES de importar torch
import torch
"""
from __future__ import annotations
import logging
import os
import platform
from typing import Any, Optional
logger = logging.getLogger(__name__)
_NUCLEOS_ALOCADOS: Optional[int] = None
_IPEX_AVAILABLE: Optional[bool] = None
_AMX_CAPABLE: Optional[bool] = None
_INIT_DONE: bool = False
def _detect_physical_cores() -> int:
"""Detecta núcleos físicos respeitando cgroup limits."""
try:
import psutil # type: ignore
n_phys = psutil.cpu_count(logical=False) or 1
except ImportError:
try:
with open("/proc/cpuinfo", "r") as f:
cores = set()
for line in f:
if line.startswith("core id"):
cores.add(line.strip())
n_phys = len(cores) or 1
except OSError:
n_phys = 1
try:
n_affine = len(os.sched_getaffinity(0))
n_logical = os.cpu_count() or 1
if n_affine < n_logical:
n_phys = max(1, n_affine // 2)
else:
n_phys = min(n_phys, n_affine)
except (AttributeError, OSError):
pass
return max(1, n_phys)
def _read_cpu_flags() -> str:
try:
with open("/proc/cpuinfo", "r") as f:
for line in f:
if line.startswith("flags"):
return line
except OSError:
pass
return ""
def _detect_amx() -> bool:
flags = _read_cpu_flags()
return "amx_int8" in flags and "amx_bf16" in flags
def _detect_avx512() -> bool:
flags = _read_cpu_flags()
return "avx512f" in flags
def optimize_xeon_environment(force_threads: Optional[int] = None) -> int:
"""Ativa todas as otimizações de CPU. Retorna o número de núcleos alocados.
Deve ser chamada UMA VEZ, antes de importar torch, para que as variáveis
de ambiente tenham efeito. Chamadas subsequentes são no-op (idempotente).
"""
global _NUCLEOS_ALOCADOS, _IPEX_AVAILABLE, _AMX_CAPABLE, _INIT_DONE
if _INIT_DONE:
return _NUCLEOS_ALOCADOS or 1
n_cores = force_threads or _detect_physical_cores()
_NUCLEOS_ALOCADOS = n_cores
# Threads
os.environ.setdefault("OMP_NUM_THREADS", str(n_cores))
os.environ.setdefault("MKL_NUM_THREADS", str(n_cores))
os.environ.setdefault("OPENBLAS_NUM_THREADS", str(n_cores))
os.environ.setdefault("NUMEXPR_NUM_THREADS", str(n_cores))
os.environ["KMP_AFFINITY"] = "granularity=fine,compact"
os.environ["MKL_DYNAMIC"] = "FALSE"
# ISA detection
if _detect_avx512():
os.environ["MKL_ENABLE_INSTRUCTIONS"] = "AVX512"
logger.info("AVX512 detectado e ativado para MKL")
if _detect_amx():
os.environ["ONEDNN_MAX_CPU_ISA"] = "AMX_INT8"
os.environ["DNNL_PRIMITIVE_CACHE_CAPACITY"] = "1024"
_AMX_CAPABLE = True
logger.info("AMX_INT8 detectado e ativado para OneDNN")
else:
_AMX_CAPABLE = False
# Tokenizers parallelism
os.environ.setdefault("TOKENIZERS_PARALLELISM", "true")
_INIT_DONE = True
logger.info(
"Xeon runtime ativado: cores=%d, avx512=%s, amx=%s, ipex=%s",
n_cores,
_detect_avx512(),
bool(_AMX_CAPABLE),
_check_ipex_available(),
)
return n_cores
def _check_ipex_available() -> bool:
global _IPEX_AVAILABLE
if _IPEX_AVAILABLE is not None:
return _IPEX_AVAILABLE
try:
import intel_extension_for_pytorch # noqa: F401
_IPEX_AVAILABLE = True
except ImportError:
_IPEX_AVAILABLE = False
return _IPEX_AVAILABLE
def optimize_model_ipex(model: Any) -> Any:
"""Aplica ipex.optimize() no modelo, se IPEX estiver disponível."""
if not _check_ipex_available():
logger.info("IPEX indisponível — pulando ipex.optimize()")
return model
try:
import intel_extension_for_pytorch as ipex # type: ignore
model = ipex.optimize(model)
logger.info("Modelo otimizado com IPEX")
except Exception as e:
logger.warning("Falha ao aplicar ipex.optimize(): %s", e)
return model
def get_runtime_info() -> dict:
"""Retorna informações sobre o runtime ativado."""
return {
"nucleos_alocados": _NUCLEOS_ALOCADOS,
"avx512": _detect_avx512(),
"amx_capable": bool(_AMX_CAPABLE),
"ipex_available": _check_ipex_available(),
"platform": platform.platform(),
"init_done": _INIT_DONE,
}
__all__ = [
"optimize_xeon_environment",
"optimize_model_ipex",
"get_runtime_info",
]
|