PowerMachine commited on
Commit
b4c4338
·
verified ·
1 Parent(s): b714048

V6.5-7ds cleanup: remove legacy scripts/xeon_runtime.py

Browse files
Files changed (1) hide show
  1. scripts/xeon_runtime.py +0 -472
scripts/xeon_runtime.py DELETED
@@ -1,472 +0,0 @@
1
- """xeon_runtime.py — Runtime Xeon OTIMIZADO para BiGRU_T_version V6+.
2
-
3
- Aprimoramento do xeon_setup.py com:
4
- 1. Configuração completa do ambiente AVX512/AMX/MKL/OMP/OneDNN
5
- 2. Verificação do IPEX (Intel Extension for PyTorch) + aplicação de ipex.optimize
6
- 3. Benchmark FP16 com autocast para medir TFLOPS reais da CPU
7
- 4. Aplicação automática de ipex.optimize(model, dtype=...) para modelos
8
- 5. Configuração de threads com detecção dinâmica de núcleos físicos
9
- 6. Diagnóstico completo de CPU ISA (AVX512 sub-features, AMX, BF16, FP16, VNNI)
10
- 7. API one-call: `apply_all(model=None)` para configuração total
11
- 8. Monkey-patch opcional para torch.matmul com autocast FP16 persistente
12
-
13
- DEVE ser importado ANTES de `import torch` para que as variáveis de ambiente
14
- tenham efeito sobre o backend OneDNN/MKL.
15
-
16
- Uso típico:
17
- from xeon_runtime import apply_all, benchmark_fp16, optimize_model
18
- apply_all() # configura ambiente + verifica IPEX + imprime diagnóstico
19
- import torch
20
- apply_all.post_torch() # configura threads do torch
21
- tf = benchmark_fp16() # mede TFLOPS FP16 reais
22
- model = ...
23
- model = optimize_model(model, dtype=torch.bfloat16) # aplica ipex.optimize
24
- """
25
- from __future__ import annotations
26
-
27
- import os
28
- import sys
29
- import time
30
- import logging
31
- import subprocess
32
- from typing import Optional, Tuple
33
-
34
- logger = logging.getLogger(__name__)
35
-
36
- # ==============================================================================
37
- # 1. CONFIGURAÇÃO DE AMBIENTE (Executar antes de qualquer import de IA/HPC)
38
- # ==============================================================================
39
-
40
- def _detect_physical_cores() -> int:
41
- """Detecta núcleos físicos da VM (com fallback seguro)."""
42
- # Tentativa 1: lscpu
43
- try:
44
- out = subprocess.check_output(["lscpu", "-p=Core"], stderr=subprocess.DEVNULL).decode()
45
- cores = set(line for line in out.splitlines() if line and not line.startswith("#"))
46
- if cores:
47
- return max(1, min(len(cores), 4)) # limita a 4 para não oversubscrever
48
- except Exception:
49
- pass
50
- # Tentativa 2: /proc/cpuinfo
51
- try:
52
- with open("/proc/cpuinfo") as f:
53
- cores = sum(1 for line in f if line.startswith("processor"))
54
- return max(1, min(cores, 4))
55
- except Exception:
56
- return 2
57
-
58
-
59
- NUM_CORES = str(_detect_physical_cores())
60
-
61
-
62
- def _detect_cpu_isa() -> dict:
63
- """Detecta sub-features AVX512/AMX/BF16/FP16/VNNI disponíveis na CPU."""
64
- flags = ""
65
- try:
66
- with open("/proc/cpuinfo") as f:
67
- for line in f:
68
- if line.startswith("flags"):
69
- flags = line.lower()
70
- break
71
- except Exception:
72
- pass
73
- return {
74
- "avx512f": "avx512f" in flags,
75
- "avx512bw": "avx512bw" in flags,
76
- "avx512cd": "avx512cd" in flags,
77
- "avx512dq": "avx512dq" in flags,
78
- "avx512vl": "avx512vl" in flags,
79
- "avx512_vnni": "avx512_vnni" in flags,
80
- "avx512_bf16": "avx512_bf16" in flags,
81
- "avx512_fp16": "avx512_fp16" in flags,
82
- "amx_tile": "amx_tile" in flags,
83
- "amx_int8": "amx_int8" in flags,
84
- "amx_bf16": "amx_bf16" in flags,
85
- }
86
-
87
-
88
- def _pick_max_isa(isa_flags: dict) -> str:
89
- """Escolhe a maior ISA suportada (para ONEDNN_MAX_CPU_ISA)."""
90
- if isa_flags["amx_int8"] or isa_flags["amx_bf16"]:
91
- return "AMX_INT8" # máximo para OneDNN
92
- if isa_flags["avx512f"]:
93
- return "AVX512"
94
- if "avx2" in str(isa_flags):
95
- return "AVX2"
96
- return "SSE"
97
-
98
-
99
- def configure_environment(num_cores: Optional[str] = None) -> dict:
100
- """Configura todas as variáveis de ambiente Xeon/Intel.
101
-
102
- DEVE ser chamado ANTES de `import torch`.
103
- Retorna dict com a configuração aplicada (para logging).
104
- """
105
- cores = num_cores or NUM_CORES
106
- isa_flags = _detect_cpu_isa()
107
- max_isa = _pick_max_isa(isa_flags)
108
-
109
- # Ativa explicitamente o suporte a AVX-512 e extensões de vetorização Intel
110
- os.environ["MKL_ENABLE_INSTRUCTIONS"] = "AVX512"
111
-
112
- # Configura o gerenciador de threads openmp/mkl para os núcleos físicos da VM
113
- os.environ["MKL_NUM_THREADS"] = cores
114
- os.environ["OMP_NUM_THREADS"] = cores
115
- os.environ["OMP_DYNAMIC"] = "FALSE"
116
- os.environ["MKL_DYNAMIC"] = "FALSE"
117
-
118
- # Otimizações específicas do PyTorch para CPUs Intel (OneDNN)
119
- os.environ["DNNL_PRIMITIVE_CACHE_CAPACITY"] = "1024"
120
- # AMX (Matrix Extensions) — só ativa em Xeon 4ª gen+ (Sapphire Rapids+)
121
- os.environ["ONEDNN_MAX_CPU_ISA"] = max_isa
122
-
123
- # Evita buffering de stdout (logs aparecem em tempo real)
124
- os.environ["PYTHONUNBUFFERED"] = "1"
125
-
126
- # Tokenizers paralelismo (HuggingFace)
127
- os.environ["TOKENIZERS_PARALLELISM"] = "true"
128
-
129
- # KMP_AFFINITY para melhor distribuição de threads
130
- os.environ["KMP_AFFINITY"] = "granularity=fine,compact,1,0"
131
- os.environ["KMP_BLOCKTIME"] = "1"
132
-
133
- return {
134
- "NUM_CORES": cores,
135
- "MKL_ENABLE_INSTRUCTIONS": os.environ["MKL_ENABLE_INSTRUCTIONS"],
136
- "MKL_NUM_THREADS": os.environ["MKL_NUM_THREADS"],
137
- "OMP_NUM_THREADS": os.environ["OMP_NUM_THREADS"],
138
- "OMP_DYNAMIC": os.environ["OMP_DYNAMIC"],
139
- "MKL_DYNAMIC": os.environ["MKL_DYNAMIC"],
140
- "DNNL_PRIMITIVE_CACHE_CAPACITY": os.environ["DNNL_PRIMITIVE_CACHE_CAPACITY"],
141
- "ONEDNN_MAX_CPU_ISA": os.environ["ONEDNN_MAX_CPU_ISA"],
142
- "PYTHONUNBUFFERED": os.environ["PYTHONUNBUFFERED"],
143
- "TOKENIZERS_PARALLELISM": os.environ["TOKENIZERS_PARALLELISM"],
144
- "KMP_AFFINITY": os.environ["KMP_AFFINITY"],
145
- "KMP_BLOCKTIME": os.environ["KMP_BLOCKTIME"],
146
- "ISA_FLAGS": isa_flags,
147
- }
148
-
149
-
150
- # ==============================================================================
151
- # 2. CONFIGURAÇÃO DO PYTORCH (após import) + IPEX
152
- # ==============================================================================
153
-
154
- def configure_torch() -> "torch":
155
- """Configura threads do PyTorch. Chamar APÓS `import torch`."""
156
- import torch
157
- torch.set_num_threads(int(NUM_CORES))
158
- torch.set_num_interop_threads(1)
159
- # OneDNN (MKL-DNN) — habilitado por padrão em CPU, mas garantimos
160
- try:
161
- torch.backends.mkldnn.enabled = True
162
- except Exception:
163
- pass
164
- # Desativa profiling mode do JIT para melhor desempenho
165
- try:
166
- torch._C._jit_set_profiling_mode(False)
167
- torch._C._jit_set_profiling_executor(False)
168
- except Exception:
169
- pass
170
- return torch
171
-
172
-
173
- def check_ipex() -> Tuple[bool, Optional[str]]:
174
- """Verifica se IPEX (Intel Extension for PyTorch) está disponível."""
175
- try:
176
- import intel_extension_for_pytorch as ipex
177
- return True, getattr(ipex, "__version__", "unknown")
178
- except ImportError:
179
- return False, None
180
- except Exception:
181
- # IPEX pode falhar ao importar se versão do torch não bater
182
- return False, None
183
-
184
-
185
- def optimize_model(model, dtype=None, level="O1"):
186
- """Aplica ipex.optimize ao modelo (se IPEX disponível).
187
-
188
- Args:
189
- model: nn.Module a ser otimizado
190
- dtype: torch.float16, torch.bfloat16, ou None (default = float32)
191
- level: "O0" (sem otim), "O1" (conv-bn folding + fusão), "O3" (agressivo)
192
-
193
- Returns:
194
- model otimizado (in-place + return). Se IPEX não disponível, retorna model inalterado.
195
- """
196
- ipex_ok, _ = check_ipex()
197
- if not ipex_ok:
198
- logger.warning("IPEX não disponível — model retornado sem otimização.")
199
- return model
200
- try:
201
- import intel_extension_for_pytorch as ipex
202
- if dtype is not None:
203
- try:
204
- # Tentativa: ipex.optimize com dtype (IPEX 2.x)
205
- model = ipex.optimize(model, dtype=dtype, level=level)
206
- except TypeError:
207
- # Fallback: ipex.optimize sem dtype
208
- model = ipex.optimize(model, level=level)
209
- else:
210
- model = ipex.optimize(model, level=level)
211
- logger.info(f"ipex.optimize aplicado (level={level}, dtype={dtype}).")
212
- return model
213
- except Exception as e:
214
- logger.warning(f"Falha ao aplicar ipex.optimize: {e}")
215
- return model
216
-
217
-
218
- # ==============================================================================
219
- # 3. BENCHMARK FP16 (com autocast) — medir TFLOPS reais da CPU Xeon
220
- # ==============================================================================
221
-
222
- def benchmark_fp16(N: int = 4000, warmup: int = 2) -> dict:
223
- """Executa benchmark FP16 com autocast em matmul NxN.
224
-
225
- Retorna dict com:
226
- - duration_s: tempo real (segundos)
227
- - tflops: TeraFLOPS estimados (2*N^3 / duration / 1e12)
228
- - dtype_in: dtype de entrada (torch.float16)
229
- - dtype_out: dtype de saída (após autocast)
230
- - ipex_active: bool
231
- - isa: str ("AVX512", "AMX", etc.)
232
- """
233
- import torch
234
- ipex_ok, _ = check_ipex()
235
-
236
- # Matrizes em FP16 nativo
237
- A = torch.randn(N, N, dtype=torch.float16)
238
- B = torch.randn(N, N, dtype=torch.float16)
239
-
240
- # Warm-up
241
- with torch.cpu.amp.autocast(enabled=True, dtype=torch.float16):
242
- for _ in range(warmup):
243
- _ = torch.matmul(A[:N // 4, :N // 4], B[:N // 4, :N // 4])
244
-
245
- # Medição
246
- start = time.perf_counter()
247
- with torch.cpu.amp.autocast(enabled=True, dtype=torch.float16):
248
- C = torch.matmul(A, B)
249
- # Sincroniza (CPU é síncrono por padrão, mas garantimos)
250
- _ = C.sum().item()
251
- end = time.perf_counter()
252
-
253
- duration = end - start
254
- flops = 2.0 * (N ** 3) / duration if duration > 0 else 0.0
255
- tflops = flops / 1e12
256
-
257
- return {
258
- "matrix_size": N,
259
- "duration_s": duration,
260
- "tflops": tflops,
261
- "gops_per_sec": flops / 1e9,
262
- "dtype_in": "torch.float16",
263
- "dtype_out": str(C.dtype),
264
- "ipex_active": ipex_ok,
265
- "isa": get_cpu_isa(),
266
- "torch_threads": torch.get_num_threads(),
267
- }
268
-
269
-
270
- def benchmark_bf16(N: int = 4000, warmup: int = 2) -> dict:
271
- """Executa benchmark BF16 (advantage: mesma range que FP32, melhor para treino)."""
272
- import torch
273
- ipex_ok, _ = check_ipex()
274
-
275
- A = torch.randn(N, N, dtype=torch.bfloat16)
276
- B = torch.randn(N, N, dtype=torch.bfloat16)
277
-
278
- with torch.cpu.amp.autocast(enabled=True, dtype=torch.bfloat16):
279
- for _ in range(warmup):
280
- _ = torch.matmul(A[:N // 4, :N // 4], B[:N // 4, :N // 4])
281
-
282
- start = time.perf_counter()
283
- with torch.cpu.amp.autocast(enabled=True, dtype=torch.bfloat16):
284
- C = torch.matmul(A, B)
285
- _ = C.sum().item()
286
- end = time.perf_counter()
287
-
288
- duration = end - start
289
- flops = 2.0 * (N ** 3) / duration if duration > 0 else 0.0
290
- return {
291
- "matrix_size": N,
292
- "duration_s": duration,
293
- "tflops": flops / 1e12,
294
- "gops_per_sec": flops / 1e9,
295
- "dtype_in": "torch.bfloat16",
296
- "dtype_out": str(C.dtype),
297
- "ipex_active": ipex_ok,
298
- "isa": get_cpu_isa(),
299
- "torch_threads": torch.get_num_threads(),
300
- }
301
-
302
-
303
- # ==============================================================================
304
- # 4. DIAGNÓSTICO E PRINT
305
- # ==============================================================================
306
-
307
- def get_cpu_isa() -> str:
308
- """Retorna a maior ISA suportada pela CPU (string curta)."""
309
- isa_flags = _detect_cpu_isa()
310
- if isa_flags["amx_int8"] or isa_flags["amx_bf16"]:
311
- return "AMX"
312
- if isa_flags["avx512f"]:
313
- return "AVX512"
314
- if "avx2" in str(isa_flags):
315
- return "AVX2"
316
- return "SSE"
317
-
318
-
319
- def print_xeon_summary(benchmark: bool = False, bench_N: int = 4000):
320
- """Imprime resumo das configurações Xeon aplicadas.
321
-
322
- Args:
323
- benchmark: se True, executa benchmark FP16 + BF16 ao final
324
- bench_N: tamanho da matriz para benchmark
325
- """
326
- ipex_ok, ipex_ver = check_ipex()
327
- isa = get_cpu_isa()
328
- isa_flags = _detect_cpu_isa()
329
-
330
- print("=" * 72)
331
- print("CONFIGURAÇÃO XEON APLICADA (xeon_runtime.py)")
332
- print("=" * 72)
333
- print(f" Python: {sys.version.split()[0]}")
334
- print(f" NUM_CORES: {NUM_CORES}")
335
- print(f" MKL_ENABLE_INSTR: {os.environ.get('MKL_ENABLE_INSTRUCTIONS')}")
336
- print(f" ONEDNN_MAX_CPU_ISA: {os.environ.get('ONEDNN_MAX_CPU_ISA')}")
337
- print(f" OMP_NUM_THREADS: {os.environ.get('OMP_NUM_THREADS')}")
338
- print(f" MKL_NUM_THREADS: {os.environ.get('MKL_NUM_THREADS')}")
339
- print(f" KMP_AFFINITY: {os.environ.get('KMP_AFFINITY')}")
340
- print(f" KMP_BLOCKTIME: {os.environ.get('KMP_BLOCKTIME')}")
341
- print(f" CPU ISA detected: {isa}")
342
- print(f" AVX512 sub-features:")
343
- for k, v in isa_flags.items():
344
- marker = "✓" if v else " "
345
- print(f" {marker} {k}")
346
- print(f" IPEX ativo: {ipex_ok}")
347
- if ipex_ok:
348
- print(f" IPEX version: {ipex_ver}")
349
- try:
350
- import torch
351
- print(f" Torch: {torch.__version__}")
352
- print(f" Torch threads: {torch.get_num_threads()}")
353
- print(f" Torch interop: {torch.get_num_interop_threads()}")
354
- print(f" OneDNN enabled: {torch.backends.mkldnn.enabled}")
355
- print(f" CPU bf16 support: {torch.backends.mkldnn.is_available()}")
356
- except ImportError:
357
- print(" Torch: (ainda não importado)")
358
- print("=" * 72)
359
-
360
- if benchmark:
361
- print("\n--- BENCHMARK FP16 (matmul autocast) ---")
362
- try:
363
- res_fp16 = benchmark_fp16(N=bench_N)
364
- print(f" Matriz: {res_fp16['matrix_size']}x{res_fp16['matrix_size']}")
365
- print(f" Tempo: {res_fp16['duration_s']:.4f}s")
366
- print(f" TFLOPS FP16: {res_fp16['tflops']:.4f}")
367
- print(f" GOPS/s: {res_fp16['gops_per_sec']:.1f}")
368
- print(f" Output dtype: {res_fp16['dtype_out']}")
369
- except Exception as e:
370
- print(f" [ERRO] FP16 benchmark falhou: {e}")
371
-
372
- print("\n--- BENCHMARK BF16 (matmul autocast) ---")
373
- try:
374
- res_bf16 = benchmark_bf16(N=bench_N)
375
- print(f" Matriz: {res_bf16['matrix_size']}x{res_bf16['matrix_size']}")
376
- print(f" Tempo: {res_bf16['duration_s']:.4f}s")
377
- print(f" TFLOPS BF16: {res_bf16['tflops']:.4f}")
378
- print(f" GOPS/s: {res_bf16['gops_per_sec']:.1f}")
379
- print(f" Output dtype: {res_bf16['dtype_out']}")
380
- except Exception as e:
381
- print(f" [ERRO] BF16 benchmark falhou: {e}")
382
- print("=" * 72)
383
-
384
-
385
- # ==============================================================================
386
- # 5. API ONE-CALL: apply_all() — configura tudo numa chamada
387
- # ==============================================================================
388
-
389
- def apply_all(model=None, dtype=None, level: str = "O1", benchmark: bool = False,
390
- bench_N: int = 4000, verbose: bool = True) -> dict:
391
- """Aplica TODA a configuração Xeon em uma chamada.
392
-
393
- Fluxo:
394
- 1. configure_environment() — env vars (ANTES de import torch)
395
- 2. import torch + configure_torch()
396
- 3. Verifica IPEX
397
- 4. (Opcional) ipex.optimize(model, dtype, level)
398
- 5. (Opcional) benchmark FP16/BF16
399
- 6. Print resumo (se verbose)
400
-
401
- Uso:
402
- from xeon_runtime import apply_all
403
- apply_all() # antes de import torch
404
- import torch
405
-
406
- Ou:
407
- from xeon_runtime import apply_all
408
- apply_all() # env vars
409
- import torch
410
- apply_all.post_torch() # threads
411
- model = ...
412
- model = apply_all.optimize_model(model, dtype=torch.bfloat16)
413
-
414
- Returns:
415
- dict com configuração aplicada + benchmarks (se solicitado)
416
- """
417
- env = configure_environment()
418
- import torch
419
- configure_torch()
420
- ipex_ok, ipex_ver = check_ipex()
421
-
422
- result = {
423
- "env": env,
424
- "ipex_active": ipex_ok,
425
- "ipex_version": ipex_ver,
426
- "torch_version": torch.__version__,
427
- "torch_threads": torch.get_num_threads(),
428
- "onednn_enabled": torch.backends.mkldnn.enabled,
429
- "isa": get_cpu_isa(),
430
- }
431
-
432
- if model is not None:
433
- model = optimize_model(model, dtype=dtype, level=level)
434
- result["model_optimized"] = True
435
-
436
- if benchmark:
437
- try:
438
- result["fp16_benchmark"] = benchmark_fp16(N=bench_N)
439
- except Exception as e:
440
- result["fp16_benchmark_error"] = str(e)
441
- try:
442
- result["bf16_benchmark"] = benchmark_bf16(N=bench_N)
443
- except Exception as e:
444
- result["bf16_benchmark_error"] = str(e)
445
-
446
- if verbose:
447
- print_xeon_summary(benchmark=benchmark, bench_N=bench_N)
448
-
449
- return result
450
-
451
-
452
- def post_torch():
453
- """Configura threads do torch (deve ser chamado APÓS import torch)."""
454
- return configure_torch()
455
-
456
-
457
- # ==============================================================================
458
- # 6. MAIN — para executar standalone como diagnóstico
459
- # ==============================================================================
460
-
461
- if __name__ == "__main__":
462
- print("\n>>> Aplicando configuração Xeon + benchmark FP16/BF16...\n")
463
- result = apply_all(benchmark=True, bench_N=4000)
464
- print("\n>>> Resumo final:")
465
- for k, v in result.items():
466
- if k in ("env", "fp16_benchmark", "bf16_benchmark"):
467
- print(f" {k}:")
468
- if isinstance(v, dict):
469
- for k2, v2 in v.items():
470
- print(f" {k2}: {v2}")
471
- else:
472
- print(f" {k}: {v}")