diff --git a/src/hardware/backends/rocm-detector.js b/src/hardware/backends/rocm-detector.js index c801e29..8fa7a74 100644 --- a/src/hardware/backends/rocm-detector.js +++ b/src/hardware/backends/rocm-detector.js @@ -19,6 +19,8 @@ class ROCmDetector { // AMD PCI device IDs for model name resolution static AMD_DEVICE_IDS = { + // RDNA 3.5 / Strix Halo integrated graphics + '1586': { name: 'AMD Radeon 8060S (Strix Halo)', vram: 16, integrated: true }, // RDNA 4 / Radeon AI PRO '7551': { name: 'AMD Radeon AI PRO R9700', vram: 32 }, '7590': { name: 'AMD Radeon RX 9060 XT', vram: 16 }, @@ -812,19 +814,27 @@ class ROCmDetector { // Try to get VRAM from sysfs for this specific device const sysfsVram = this._getVRAMFromSysfsForDevice(deviceId); + const memoryProfile = this.resolveGpuMemoryProfile(name, sysfsVram || vram); result.gpus.push({ index: idx, name: name, + type: memoryProfile.type, memory: { - total: sysfsVram || vram, - free: sysfsVram || vram, - used: 0 + total: memoryProfile.total, + free: memoryProfile.total, + used: 0, + dedicated: memoryProfile.dedicated, + shared: memoryProfile.shared }, + dedicatedMemory: memoryProfile.dedicated, + sharedMemory: memoryProfile.shared, + unifiedMemory: memoryProfile.type === 'integrated' ? memoryProfile.shared : 0, capabilities: this.getGPUCapabilities(name), - speedCoefficient: this.calculateSpeedCoefficient(name, sysfsVram || vram) + speedCoefficient: this.calculateSpeedCoefficient(name, memoryProfile.total) }); - result.totalVRAM += sysfsVram || vram; + result.totalVRAM += memoryProfile.type === 'integrated' ? memoryProfile.dedicated : memoryProfile.total; + result.totalSharedMemory += memoryProfile.type === 'integrated' ? memoryProfile.shared : 0; idx++; } @@ -876,14 +886,27 @@ class ROCmDetector { } } + const memoryProfile = this.resolveGpuMemoryProfile(name, vram); + result.gpus.push({ index: idx, name: name, - memory: { total: vram, free: vram, used: 0 }, + type: memoryProfile.type, + memory: { + total: memoryProfile.total, + free: memoryProfile.total, + used: 0, + dedicated: memoryProfile.dedicated, + shared: memoryProfile.shared + }, + dedicatedMemory: memoryProfile.dedicated, + sharedMemory: memoryProfile.shared, + unifiedMemory: memoryProfile.type === 'integrated' ? memoryProfile.shared : 0, capabilities: this.getGPUCapabilities(name), - speedCoefficient: this.calculateSpeedCoefficient(name, vram) + speedCoefficient: this.calculateSpeedCoefficient(name, memoryProfile.total) }); - result.totalVRAM += vram; + result.totalVRAM += memoryProfile.type === 'integrated' ? memoryProfile.dedicated : memoryProfile.total; + result.totalSharedMemory += memoryProfile.type === 'integrated' ? memoryProfile.shared : 0; idx++; } catch (e) { continue; diff --git a/src/hardware/detector.js b/src/hardware/detector.js index 8970eb5..5a58bad 100644 --- a/src/hardware/detector.js +++ b/src/hardware/detector.js @@ -155,6 +155,7 @@ class HardwareDetector { const hasGenericModel = !originalModel || modelLower === 'unknown' || modelLower.includes('nvidia corporation device') || + /(?:amd|ati|advanced micro devices).*\bdevice\s+[0-9a-f]{4}\b/i.test(originalModel) || /^device\s+[0-9a-f]{4}$/i.test(originalModel); if (hasGenericModel && gpu.deviceId) { @@ -514,6 +515,11 @@ class HardwareDetector { return (modelLower.includes('intel') && !modelLower.includes('arc')) || (modelLower.includes('amd') && modelLower.includes('graphics') && !modelLower.includes(' rx ')) || (modelLower.includes('radeon') && modelLower.includes('graphics') && !modelLower.includes('rx')) || + modelLower.includes('radeon 8060s') || + modelLower.includes('strix halo') || + modelLower.includes('gfx1150') || + modelLower.includes('gfx1151') || + modelLower.includes('gfx1152') || modelLower.includes('iris') || modelLower.includes('uhd') || modelLower.includes('hd graphics') || @@ -560,6 +566,7 @@ class HardwareDetector { '1b80': 'NVIDIA GeForce GTX 1080', // AMD RDNA 3 / RDNA 2 + '1586': 'AMD Radeon 8060S (Strix Halo)', '744c': 'AMD Radeon RX 7900 XTX', '7448': 'AMD Radeon RX 7900 XT', '7460': 'AMD Radeon RX 7900 GRE', @@ -626,6 +633,8 @@ class HardwareDetector { if (modelLower.includes('rtx 3060')) return 12; // AMD RX 7000 series + if (modelLower.includes('radeon 8060s') || modelLower.includes('strix halo') || + modelLower.includes('gfx1150') || modelLower.includes('gfx1151') || modelLower.includes('gfx1152')) return 16; if (modelLower.includes('rx 7900')) return 24; if (modelLower.includes('rx 7800')) return 16; if (modelLower.includes('rx 7700')) return 12; diff --git a/src/hardware/unified-detector.js b/src/hardware/unified-detector.js index cd75b4a..735bf52 100644 --- a/src/hardware/unified-detector.js +++ b/src/hardware/unified-detector.js @@ -346,7 +346,6 @@ class UnifiedDetector { // a small aperture as VRAM and a much larger shared pool for model-fit // decisions, so avoid treating the aperture as dedicated VRAM. if ( - ['rocm', 'intel'].includes(primary?.type) && summary.hasIntegratedGPU && !summary.hasDedicatedGPU && summary.integratedSharedMemory > 0 diff --git a/tests/run-all-tests.js b/tests/run-all-tests.js index 9d6154f..970d2ab 100644 --- a/tests/run-all-tests.js +++ b/tests/run-all-tests.js @@ -8,6 +8,7 @@ const path = require('path'); const TESTS = [ { name: 'AMD GPU detection', file: 'amd-gpu-detection.test.js', category: 'Hardware' }, + { name: 'AMD Strix Halo unified memory', file: 'strix-halo-unified-memory.test.js', category: 'Hardware' }, { name: 'CUDA Jetson detection', file: 'cuda-jetson-detection.test.js', category: 'Hardware' }, { name: 'Hardware simulation scoring', file: 'hardware-simulation-tests.js', category: 'Hardware' }, { name: 'Hardware detector regression', file: 'hardware-detector-regression.js', category: 'Hardware' }, diff --git a/tests/strix-halo-unified-memory.test.js b/tests/strix-halo-unified-memory.test.js new file mode 100644 index 0000000..750f25b --- /dev/null +++ b/tests/strix-halo-unified-memory.test.js @@ -0,0 +1,139 @@ +const assert = require('assert'); +const childProcess = require('child_process'); + +const rocmModulePath = require.resolve('../src/hardware/backends/rocm-detector'); +const originalExecSync = childProcess.execSync; + +function loadLspciFixtureDetector() { + childProcess.execSync = (command) => { + if (String(command).startsWith('lspci -nn')) { + return [ + '65:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Device 1586 [1002:1586]' + ].join('\n'); + } + throw new Error(`fixture command unavailable: ${command}`); + }; + delete require.cache[rocmModulePath]; + const ROCmDetector = require(rocmModulePath); + const detector = new ROCmDetector(); + detector.isAvailable = true; + detector.detectionMethod = 'lspci'; + detector._getVRAMFromSysfsForDevice = () => 1; + detector.getIntegratedMemoryProfile = () => ({ dedicated: 1, shared: 118 }); + return { detector, ROCmDetector }; +} + +function restoreLspciFixture() { + childProcess.execSync = originalExecSync; + delete require.cache[rocmModulePath]; +} + +function testRocmDeviceIdAndUnifiedMemory() { + const { detector, ROCmDetector } = loadLspciFixtureDetector(); + try { + assert.match(ROCmDetector.AMD_DEVICE_IDS['1586'].name, /Radeon 8060S|Strix Halo/i); + const result = detector.detect(); + assert.ok(result); + assert.strictEqual(result.gpus.length, 1); + + const gpu = result.gpus[0]; + assert.match(gpu.name, /Radeon 8060S|Strix Halo/i); + assert.strictEqual(gpu.type, 'integrated'); + assert.strictEqual(gpu.memory.dedicated, 1); + assert.strictEqual(gpu.memory.shared, 118); + assert.strictEqual(gpu.unifiedMemory, 118); + assert.strictEqual(result.totalVRAM, 1); + assert.strictEqual(result.totalSharedMemory, 118); + assert.ok(gpu.memory.total > 0, 'usable accelerator memory must be positive'); + } finally { + restoreLspciFixture(); + } +} + +function testSystemInformationFixtureAndCommandClassification() { + const HardwareDetector = require('../src/hardware/detector'); + const UnifiedDetector = require('../src/hardware/unified-detector'); + const LLMChecker = require('../src/index'); + const DeterministicModelSelector = require('../src/models/deterministic-selector'); + + const detector = new HardwareDetector(); + const gpu = detector.processGPUInfo( + { + controllers: [{ + model: 'Advanced Micro Devices, Inc. [AMD/ATI] Device 1586', + vendor: 'Advanced Micro Devices, Inc. [AMD/ATI]', + deviceId: '1002:1586', + vram: 1024, + vramDynamic: true + }], + displays: [] + }, + { total: 124 * 1024 ** 3 } + ); + + assert.match(gpu.model, /Radeon 8060S|Strix Halo/i); + assert.strictEqual(gpu.dedicated, false); + assert.strictEqual(gpu.dedicatedMemory, 1); + assert.ok(gpu.sharedMemory > 0); + assert.ok(gpu.vram > 0); + + const unified = new UnifiedDetector(); + const summary = unified.buildSummary({ + cpu: { brand: 'AMD Ryzen AI MAX+ 395', speedCoefficient: 160 }, + primary: { + type: 'rocm', + name: 'AMD ROCm', + info: { + gpus: [{ + name: gpu.model, + type: 'integrated', + memory: { total: gpu.sharedMemory }, + dedicatedMemory: gpu.dedicatedMemory, + sharedMemory: gpu.sharedMemory, + unifiedMemory: gpu.sharedMemory + }], + totalVRAM: gpu.dedicatedMemory, + totalSharedMemory: gpu.sharedMemory, + isMultiGPU: false, + speedCoefficient: 160 + } + } + }); + + assert.strictEqual(summary.hasIntegratedGPU, true); + assert.strictEqual(summary.hasDedicatedGPU, false); + assert.ok(summary.effectiveMemory > 0); + assert.notStrictEqual(summary.hardwareTier, 'ultra_low'); + + unified.cache = { summary }; + assert.ok(unified.getMaxModelSize() > 0, 'maximum model size must be positive'); + + const hardware = { + cpu: { brand: 'AMD Ryzen AI MAX+ 395' }, + memory: { total: 124 }, + gpu, + summary + }; + const checkTier = new LLMChecker({ verbose: false }).getHardwareTier(hardware); + const selectorTier = new DeterministicModelSelector().mapHardwareTier(hardware); + assert.strictEqual(checkTier, summary.hardwareTier); + assert.strictEqual(selectorTier, summary.hardwareTier); +} + +function run() { + testRocmDeviceIdAndUnifiedMemory(); + testSystemInformationFixtureAndCommandClassification(); + console.log('strix-halo-unified-memory.test.js: OK'); +} + +if (require.main === module) { + try { + run(); + } catch (error) { + console.error('strix-halo-unified-memory.test.js: FAILED'); + console.error(error); + process.exit(1); + } +} + +module.exports = { run };