llm-checker 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +418 -0
- package/analyzer/compatibility.js +584 -0
- package/analyzer/performance.js +505 -0
- package/bin/CLAUDE.md +12 -0
- package/bin/enhanced_cli.js +3118 -0
- package/bin/test-deterministic.js +41 -0
- package/package.json +96 -0
- package/src/CLAUDE.md +12 -0
- package/src/ai/intelligent-selector.js +615 -0
- package/src/ai/model-selector.js +312 -0
- package/src/ai/multi-objective-selector.js +820 -0
- package/src/commands/check.js +58 -0
- package/src/data/CLAUDE.md +11 -0
- package/src/data/model-database.js +637 -0
- package/src/data/sync-manager.js +279 -0
- package/src/hardware/CLAUDE.md +12 -0
- package/src/hardware/backends/CLAUDE.md +11 -0
- package/src/hardware/backends/apple-silicon.js +318 -0
- package/src/hardware/backends/cpu-detector.js +490 -0
- package/src/hardware/backends/cuda-detector.js +417 -0
- package/src/hardware/backends/intel-detector.js +436 -0
- package/src/hardware/backends/rocm-detector.js +440 -0
- package/src/hardware/detector.js +573 -0
- package/src/hardware/pc-optimizer.js +635 -0
- package/src/hardware/specs.js +286 -0
- package/src/hardware/unified-detector.js +442 -0
- package/src/index.js +2289 -0
- package/src/models/CLAUDE.md +17 -0
- package/src/models/ai-check-selector.js +806 -0
- package/src/models/catalog.json +426 -0
- package/src/models/deterministic-selector.js +1145 -0
- package/src/models/expanded_database.js +1142 -0
- package/src/models/intelligent-selector.js +532 -0
- package/src/models/requirements.js +310 -0
- package/src/models/scoring-config.js +57 -0
- package/src/models/scoring-engine.js +715 -0
- package/src/ollama/.cache/README.md +33 -0
- package/src/ollama/CLAUDE.md +24 -0
- package/src/ollama/client.js +438 -0
- package/src/ollama/enhanced-client.js +113 -0
- package/src/ollama/enhanced-scraper.js +634 -0
- package/src/ollama/manager.js +357 -0
- package/src/ollama/native-scraper.js +776 -0
- package/src/plugins/CLAUDE.md +11 -0
- package/src/plugins/examples/custom_model_plugin.js +87 -0
- package/src/plugins/index.js +295 -0
- package/src/utils/CLAUDE.md +11 -0
- package/src/utils/config.js +359 -0
- package/src/utils/formatter.js +315 -0
- package/src/utils/logger.js +272 -0
- package/src/utils/model-classifier.js +167 -0
- package/src/utils/verbose-progress.js +266 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CPU Detector
|
|
3
|
+
* Detects CPU capabilities for LLM inference
|
|
4
|
+
* Focuses on AVX2, AVX512, AMX, and other SIMD extensions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
|
|
11
|
+
class CPUDetector {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.cache = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Detect CPU capabilities
|
|
18
|
+
*/
|
|
19
|
+
detect() {
|
|
20
|
+
if (this.cache) {
|
|
21
|
+
return this.cache;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const info = this.getCPUInfo();
|
|
26
|
+
this.cache = info;
|
|
27
|
+
return info;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return this.getFallbackInfo();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get detailed CPU information
|
|
35
|
+
*/
|
|
36
|
+
getCPUInfo() {
|
|
37
|
+
const cpus = os.cpus();
|
|
38
|
+
const cpu = cpus[0] || {};
|
|
39
|
+
|
|
40
|
+
const result = {
|
|
41
|
+
brand: cpu.model || 'Unknown CPU',
|
|
42
|
+
vendor: this.detectVendor(cpu.model),
|
|
43
|
+
cores: {
|
|
44
|
+
physical: this.getPhysicalCores(),
|
|
45
|
+
logical: cpus.length,
|
|
46
|
+
performance: 0,
|
|
47
|
+
efficiency: 0
|
|
48
|
+
},
|
|
49
|
+
frequency: {
|
|
50
|
+
base: cpu.speed || 0,
|
|
51
|
+
max: this.getMaxFrequency()
|
|
52
|
+
},
|
|
53
|
+
cache: this.getCacheInfo(),
|
|
54
|
+
capabilities: this.getCapabilities(),
|
|
55
|
+
architecture: process.arch,
|
|
56
|
+
backend: 'cpu',
|
|
57
|
+
speedCoefficient: 0
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Detect P-core/E-core for hybrid CPUs (Intel 12th+ gen, Apple Silicon)
|
|
61
|
+
const hybrid = this.detectHybridCores(result.brand);
|
|
62
|
+
result.cores.performance = hybrid.performance;
|
|
63
|
+
result.cores.efficiency = hybrid.efficiency;
|
|
64
|
+
|
|
65
|
+
// Calculate speed coefficient
|
|
66
|
+
result.speedCoefficient = this.calculateSpeedCoefficient(result);
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Detect CPU vendor
|
|
73
|
+
*/
|
|
74
|
+
detectVendor(brand) {
|
|
75
|
+
const brandLower = (brand || '').toLowerCase();
|
|
76
|
+
|
|
77
|
+
if (brandLower.includes('intel')) return 'Intel';
|
|
78
|
+
if (brandLower.includes('amd')) return 'AMD';
|
|
79
|
+
if (brandLower.includes('apple')) return 'Apple';
|
|
80
|
+
if (brandLower.includes('arm')) return 'ARM';
|
|
81
|
+
if (brandLower.includes('qualcomm')) return 'Qualcomm';
|
|
82
|
+
|
|
83
|
+
return 'Unknown';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get physical core count
|
|
88
|
+
*/
|
|
89
|
+
getPhysicalCores() {
|
|
90
|
+
try {
|
|
91
|
+
if (process.platform === 'darwin') {
|
|
92
|
+
return parseInt(execSync('sysctl -n hw.physicalcpu', { encoding: 'utf8', timeout: 5000 }).trim());
|
|
93
|
+
} else if (process.platform === 'linux') {
|
|
94
|
+
const cpuInfo = fs.readFileSync('/proc/cpuinfo', 'utf8');
|
|
95
|
+
const coreIds = new Set();
|
|
96
|
+
const matches = cpuInfo.matchAll(/core id\s*:\s*(\d+)/g);
|
|
97
|
+
for (const match of matches) {
|
|
98
|
+
coreIds.add(match[1]);
|
|
99
|
+
}
|
|
100
|
+
return coreIds.size || os.cpus().length;
|
|
101
|
+
} else if (process.platform === 'win32') {
|
|
102
|
+
const wmic = execSync('wmic cpu get NumberOfCores', { encoding: 'utf8', timeout: 5000 });
|
|
103
|
+
const match = wmic.match(/\d+/);
|
|
104
|
+
return match ? parseInt(match[0]) : os.cpus().length;
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {
|
|
107
|
+
return os.cpus().length;
|
|
108
|
+
}
|
|
109
|
+
return os.cpus().length;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get maximum CPU frequency
|
|
114
|
+
*/
|
|
115
|
+
getMaxFrequency() {
|
|
116
|
+
try {
|
|
117
|
+
if (process.platform === 'darwin') {
|
|
118
|
+
// macOS doesn't expose max frequency easily
|
|
119
|
+
const cpus = os.cpus();
|
|
120
|
+
return cpus.length > 0 ? cpus[0].speed : 0;
|
|
121
|
+
} else if (process.platform === 'linux') {
|
|
122
|
+
const maxFreq = fs.readFileSync(
|
|
123
|
+
'/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq',
|
|
124
|
+
'utf8'
|
|
125
|
+
);
|
|
126
|
+
return Math.round(parseInt(maxFreq) / 1000); // kHz to MHz
|
|
127
|
+
} else if (process.platform === 'win32') {
|
|
128
|
+
const wmic = execSync('wmic cpu get MaxClockSpeed', { encoding: 'utf8', timeout: 5000 });
|
|
129
|
+
const match = wmic.match(/\d+/);
|
|
130
|
+
return match ? parseInt(match[0]) : 0;
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {
|
|
133
|
+
return os.cpus()[0]?.speed || 0;
|
|
134
|
+
}
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get CPU cache information
|
|
140
|
+
*/
|
|
141
|
+
getCacheInfo() {
|
|
142
|
+
const cache = {
|
|
143
|
+
l1d: 0,
|
|
144
|
+
l1i: 0,
|
|
145
|
+
l2: 0,
|
|
146
|
+
l3: 0
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
if (process.platform === 'darwin') {
|
|
151
|
+
cache.l1d = parseInt(execSync('sysctl -n hw.l1dcachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 || 0;
|
|
152
|
+
cache.l1i = parseInt(execSync('sysctl -n hw.l1icachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 || 0;
|
|
153
|
+
cache.l2 = parseInt(execSync('sysctl -n hw.l2cachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 / 1024 || 0;
|
|
154
|
+
cache.l3 = parseInt(execSync('sysctl -n hw.l3cachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 / 1024 || 0;
|
|
155
|
+
} else if (process.platform === 'linux') {
|
|
156
|
+
// Parse from /sys/devices/system/cpu/cpu0/cache/
|
|
157
|
+
const cachePath = '/sys/devices/system/cpu/cpu0/cache';
|
|
158
|
+
if (fs.existsSync(cachePath)) {
|
|
159
|
+
const indexes = fs.readdirSync(cachePath).filter(f => f.startsWith('index'));
|
|
160
|
+
for (const idx of indexes) {
|
|
161
|
+
try {
|
|
162
|
+
const level = fs.readFileSync(`${cachePath}/${idx}/level`, 'utf8').trim();
|
|
163
|
+
const type = fs.readFileSync(`${cachePath}/${idx}/type`, 'utf8').trim();
|
|
164
|
+
const size = fs.readFileSync(`${cachePath}/${idx}/size`, 'utf8').trim();
|
|
165
|
+
|
|
166
|
+
const sizeKB = parseInt(size.replace(/[KMG]/g, ''));
|
|
167
|
+
const multiplier = size.includes('M') ? 1024 : size.includes('G') ? 1024 * 1024 : 1;
|
|
168
|
+
const finalSize = sizeKB * multiplier;
|
|
169
|
+
|
|
170
|
+
if (level === '1' && type === 'Data') cache.l1d = finalSize;
|
|
171
|
+
else if (level === '1' && type === 'Instruction') cache.l1i = finalSize;
|
|
172
|
+
else if (level === '2') cache.l2 = finalSize / 1024; // MB
|
|
173
|
+
else if (level === '3') cache.l3 = finalSize / 1024; // MB
|
|
174
|
+
} catch (e) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch (e) {
|
|
181
|
+
// Return zeros
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return cache;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get CPU SIMD and acceleration capabilities
|
|
189
|
+
*/
|
|
190
|
+
getCapabilities() {
|
|
191
|
+
const caps = {
|
|
192
|
+
sse: false,
|
|
193
|
+
sse2: false,
|
|
194
|
+
sse3: false,
|
|
195
|
+
ssse3: false,
|
|
196
|
+
sse4_1: false,
|
|
197
|
+
sse4_2: false,
|
|
198
|
+
avx: false,
|
|
199
|
+
avx2: false,
|
|
200
|
+
avx512: false,
|
|
201
|
+
avx512_vnni: false,
|
|
202
|
+
amx: false, // Intel Advanced Matrix Extensions
|
|
203
|
+
fma: false,
|
|
204
|
+
f16c: false,
|
|
205
|
+
neon: false, // ARM NEON
|
|
206
|
+
sve: false, // ARM SVE
|
|
207
|
+
dotprod: false, // ARM dot product
|
|
208
|
+
bestSimd: 'none'
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
if (process.platform === 'darwin') {
|
|
213
|
+
// For Apple Silicon (ARM64), use ARM features
|
|
214
|
+
if (process.arch === 'arm64') {
|
|
215
|
+
caps.neon = true; // All Apple Silicon has NEON
|
|
216
|
+
caps.dotprod = true;
|
|
217
|
+
caps.f16c = true;
|
|
218
|
+
// Apple Silicon has excellent FP16 support
|
|
219
|
+
} else {
|
|
220
|
+
// Intel Mac - check via sysctl
|
|
221
|
+
try {
|
|
222
|
+
const features = execSync('sysctl -n machdep.cpu.features', {
|
|
223
|
+
encoding: 'utf8',
|
|
224
|
+
timeout: 5000
|
|
225
|
+
}).toLowerCase();
|
|
226
|
+
|
|
227
|
+
const leafFeatures = execSync('sysctl -n machdep.cpu.leaf7_features', {
|
|
228
|
+
encoding: 'utf8',
|
|
229
|
+
timeout: 5000
|
|
230
|
+
}).toLowerCase();
|
|
231
|
+
|
|
232
|
+
caps.sse = features.includes('sse');
|
|
233
|
+
caps.sse2 = features.includes('sse2');
|
|
234
|
+
caps.sse3 = features.includes('sse3');
|
|
235
|
+
caps.ssse3 = features.includes('ssse3');
|
|
236
|
+
caps.sse4_1 = features.includes('sse4.1');
|
|
237
|
+
caps.sse4_2 = features.includes('sse4.2');
|
|
238
|
+
caps.avx = features.includes('avx1.0') || features.includes('avx ');
|
|
239
|
+
caps.avx2 = leafFeatures.includes('avx2');
|
|
240
|
+
caps.fma = features.includes('fma');
|
|
241
|
+
caps.f16c = leafFeatures.includes('f16c');
|
|
242
|
+
|
|
243
|
+
// Check for AVX-512 on Intel Macs
|
|
244
|
+
caps.avx512 = leafFeatures.includes('avx512');
|
|
245
|
+
} catch (e) {
|
|
246
|
+
// Intel Mac sysctl failed, assume basic SSE
|
|
247
|
+
caps.sse = caps.sse2 = true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
} else if (process.platform === 'linux') {
|
|
252
|
+
// Linux - check /proc/cpuinfo
|
|
253
|
+
const cpuInfo = fs.readFileSync('/proc/cpuinfo', 'utf8').toLowerCase();
|
|
254
|
+
const flags = cpuInfo.match(/flags\s*:\s*(.+)/)?.[1] || '';
|
|
255
|
+
|
|
256
|
+
caps.sse = flags.includes('sse ') || flags.includes(' sse');
|
|
257
|
+
caps.sse2 = flags.includes('sse2');
|
|
258
|
+
caps.sse3 = flags.includes('sse3');
|
|
259
|
+
caps.ssse3 = flags.includes('ssse3');
|
|
260
|
+
caps.sse4_1 = flags.includes('sse4_1');
|
|
261
|
+
caps.sse4_2 = flags.includes('sse4_2');
|
|
262
|
+
caps.avx = flags.includes('avx ') || flags.includes(' avx');
|
|
263
|
+
caps.avx2 = flags.includes('avx2');
|
|
264
|
+
caps.avx512 = flags.includes('avx512f');
|
|
265
|
+
caps.avx512_vnni = flags.includes('avx512_vnni');
|
|
266
|
+
caps.amx = flags.includes('amx_tile') || flags.includes('amx_int8');
|
|
267
|
+
caps.fma = flags.includes('fma');
|
|
268
|
+
caps.f16c = flags.includes('f16c');
|
|
269
|
+
|
|
270
|
+
// ARM features
|
|
271
|
+
if (process.arch === 'arm64') {
|
|
272
|
+
caps.neon = flags.includes('asimd') || flags.includes('neon');
|
|
273
|
+
caps.sve = flags.includes('sve');
|
|
274
|
+
caps.dotprod = flags.includes('asimddp');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
} else if (process.platform === 'win32') {
|
|
278
|
+
// Windows - use WMIC or assume based on CPU model
|
|
279
|
+
const cpuName = os.cpus()[0]?.model?.toLowerCase() || '';
|
|
280
|
+
|
|
281
|
+
// All modern x64 CPUs have SSE through SSE4.2
|
|
282
|
+
if (process.arch === 'x64') {
|
|
283
|
+
caps.sse = caps.sse2 = caps.sse3 = caps.ssse3 = true;
|
|
284
|
+
caps.sse4_1 = caps.sse4_2 = true;
|
|
285
|
+
|
|
286
|
+
// Most post-2013 CPUs have AVX/AVX2
|
|
287
|
+
if (cpuName.includes('ryzen') || cpuName.includes('core')) {
|
|
288
|
+
caps.avx = true;
|
|
289
|
+
// Ryzen and Intel 4th gen+ have AVX2
|
|
290
|
+
if (cpuName.includes('ryzen') ||
|
|
291
|
+
cpuName.match(/core.*i[3579].*[4-9]\d{3}|1[0-4]\d{3}/)) {
|
|
292
|
+
caps.avx2 = true;
|
|
293
|
+
caps.fma = true;
|
|
294
|
+
caps.f16c = true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Intel 12th gen+ and Xeon Scalable have AVX-512
|
|
299
|
+
if (cpuName.includes('xeon') ||
|
|
300
|
+
cpuName.match(/12\d{3}|13\d{3}|14\d{3}/)) {
|
|
301
|
+
caps.avx512 = true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (e) {
|
|
306
|
+
// Assume modern x64 CPU has basic SIMD
|
|
307
|
+
if (process.arch === 'x64') {
|
|
308
|
+
caps.sse = caps.sse2 = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Determine best SIMD level
|
|
313
|
+
if (caps.amx) caps.bestSimd = 'AMX';
|
|
314
|
+
else if (caps.avx512) caps.bestSimd = 'AVX512';
|
|
315
|
+
else if (caps.avx2) caps.bestSimd = 'AVX2';
|
|
316
|
+
else if (caps.avx) caps.bestSimd = 'AVX';
|
|
317
|
+
else if (caps.neon) caps.bestSimd = 'NEON';
|
|
318
|
+
else if (caps.sse4_2) caps.bestSimd = 'SSE4.2';
|
|
319
|
+
else if (caps.sse2) caps.bestSimd = 'SSE2';
|
|
320
|
+
|
|
321
|
+
return caps;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Detect hybrid core configuration
|
|
326
|
+
*/
|
|
327
|
+
detectHybridCores(brand) {
|
|
328
|
+
const brandLower = (brand || '').toLowerCase();
|
|
329
|
+
const logicalCores = os.cpus().length;
|
|
330
|
+
|
|
331
|
+
// Intel 12th+ gen hybrid
|
|
332
|
+
if (brandLower.includes('intel') && brandLower.match(/12\d{3}|13\d{3}|14\d{3}/)) {
|
|
333
|
+
// Estimate P/E core split (varies by SKU)
|
|
334
|
+
if (brandLower.includes('i9')) {
|
|
335
|
+
return { performance: 8, efficiency: logicalCores - 16 };
|
|
336
|
+
} else if (brandLower.includes('i7')) {
|
|
337
|
+
return { performance: 8, efficiency: Math.max(0, logicalCores - 16) };
|
|
338
|
+
} else if (brandLower.includes('i5')) {
|
|
339
|
+
return { performance: 6, efficiency: Math.max(0, logicalCores - 12) };
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Apple Silicon (handled by apple-silicon.js, but fallback here)
|
|
344
|
+
if (brandLower.includes('apple') || brandLower.includes('m1') ||
|
|
345
|
+
brandLower.includes('m2') || brandLower.includes('m3') ||
|
|
346
|
+
brandLower.includes('m4')) {
|
|
347
|
+
const pCores = Math.ceil(logicalCores * 0.5);
|
|
348
|
+
return { performance: pCores, efficiency: logicalCores - pCores };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Non-hybrid
|
|
352
|
+
return { performance: logicalCores, efficiency: 0 };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Calculate speed coefficient for LLM inference
|
|
357
|
+
*/
|
|
358
|
+
calculateSpeedCoefficient(info) {
|
|
359
|
+
let base = 30; // Base tokens/sec per B params
|
|
360
|
+
|
|
361
|
+
// Adjust based on SIMD capabilities
|
|
362
|
+
switch (info.capabilities.bestSimd) {
|
|
363
|
+
case 'AMX':
|
|
364
|
+
base = 100; // Intel AMX is excellent for matrix ops
|
|
365
|
+
break;
|
|
366
|
+
case 'AVX512':
|
|
367
|
+
base = 70;
|
|
368
|
+
break;
|
|
369
|
+
case 'AVX2':
|
|
370
|
+
base = 50;
|
|
371
|
+
break;
|
|
372
|
+
case 'NEON':
|
|
373
|
+
base = 45; // ARM NEON is quite good
|
|
374
|
+
break;
|
|
375
|
+
case 'AVX':
|
|
376
|
+
base = 35;
|
|
377
|
+
break;
|
|
378
|
+
default:
|
|
379
|
+
base = 25;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Adjust for core count (more cores = more parallel processing)
|
|
383
|
+
const coreBonus = Math.min(20, info.cores.physical * 1.5);
|
|
384
|
+
base += coreBonus;
|
|
385
|
+
|
|
386
|
+
// Adjust for frequency
|
|
387
|
+
const freqBonus = Math.min(15, (info.frequency.max / 1000) * 3);
|
|
388
|
+
base += freqBonus;
|
|
389
|
+
|
|
390
|
+
// Adjust for L3 cache (important for LLM)
|
|
391
|
+
if (info.cache.l3 >= 64) base += 15;
|
|
392
|
+
else if (info.cache.l3 >= 32) base += 10;
|
|
393
|
+
else if (info.cache.l3 >= 16) base += 5;
|
|
394
|
+
|
|
395
|
+
return Math.round(base);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Fallback info when detection fails
|
|
400
|
+
*/
|
|
401
|
+
getFallbackInfo() {
|
|
402
|
+
const cpus = os.cpus();
|
|
403
|
+
const cpu = cpus[0] || {};
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
brand: cpu.model || 'Unknown CPU',
|
|
407
|
+
vendor: 'Unknown',
|
|
408
|
+
cores: {
|
|
409
|
+
physical: cpus.length,
|
|
410
|
+
logical: cpus.length,
|
|
411
|
+
performance: cpus.length,
|
|
412
|
+
efficiency: 0
|
|
413
|
+
},
|
|
414
|
+
frequency: {
|
|
415
|
+
base: cpu.speed || 0,
|
|
416
|
+
max: cpu.speed || 0
|
|
417
|
+
},
|
|
418
|
+
cache: { l1d: 0, l1i: 0, l2: 0, l3: 0 },
|
|
419
|
+
capabilities: {
|
|
420
|
+
sse2: process.arch === 'x64',
|
|
421
|
+
avx: false,
|
|
422
|
+
avx2: false,
|
|
423
|
+
avx512: false,
|
|
424
|
+
neon: process.arch === 'arm64',
|
|
425
|
+
bestSimd: process.arch === 'arm64' ? 'NEON' : 'SSE2'
|
|
426
|
+
},
|
|
427
|
+
architecture: process.arch,
|
|
428
|
+
backend: 'cpu',
|
|
429
|
+
speedCoefficient: 30
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Get hardware fingerprint
|
|
435
|
+
*/
|
|
436
|
+
getFingerprint() {
|
|
437
|
+
const info = this.detect();
|
|
438
|
+
const brandSlug = info.brand.toLowerCase()
|
|
439
|
+
.replace(/\(r\)|\(tm\)|@|cpu|processor/gi, '')
|
|
440
|
+
.replace(/\s+/g, '-')
|
|
441
|
+
.replace(/-+/g, '-')
|
|
442
|
+
.trim();
|
|
443
|
+
|
|
444
|
+
return `cpu-${brandSlug}-${info.cores.logical}c-${info.capabilities.bestSimd.toLowerCase()}`;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Estimate inference speed
|
|
449
|
+
*/
|
|
450
|
+
estimateTokensPerSecond(paramsB, quantization = 'Q4_K_M') {
|
|
451
|
+
const info = this.detect();
|
|
452
|
+
|
|
453
|
+
// CPU quantization benefits
|
|
454
|
+
const quantMult = {
|
|
455
|
+
'FP16': 0.8, // FP16 slower on CPU
|
|
456
|
+
'Q8_0': 1.2,
|
|
457
|
+
'Q6_K': 1.5,
|
|
458
|
+
'Q5_K_M': 1.8,
|
|
459
|
+
'Q5_0': 1.8,
|
|
460
|
+
'Q4_K_M': 2.2,
|
|
461
|
+
'Q4_0': 2.5,
|
|
462
|
+
'Q3_K_M': 2.8,
|
|
463
|
+
'Q2_K': 3.2,
|
|
464
|
+
'IQ4_XS': 2.3,
|
|
465
|
+
'IQ3_XXS': 2.9
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const mult = quantMult[quantization] || 2.0;
|
|
469
|
+
const baseSpeed = info.speedCoefficient / paramsB * mult;
|
|
470
|
+
|
|
471
|
+
return Math.round(baseSpeed);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Get recommended thread count for inference
|
|
476
|
+
*/
|
|
477
|
+
getRecommendedThreads() {
|
|
478
|
+
const info = this.detect();
|
|
479
|
+
|
|
480
|
+
// Use performance cores if hybrid, otherwise physical cores
|
|
481
|
+
// Leave 1-2 cores for system
|
|
482
|
+
const available = info.cores.performance > 0
|
|
483
|
+
? info.cores.performance
|
|
484
|
+
: info.cores.physical;
|
|
485
|
+
|
|
486
|
+
return Math.max(1, available - 1);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
module.exports = CPUDetector;
|