|
@@ -11,7 +11,6 @@ const DEFAULT_MIN_TDP_PERCENT = 10;
|
|
|
const DEFAULT_MAX_TDP_PERCENT = 100;
|
|
const DEFAULT_MAX_TDP_PERCENT = 100;
|
|
|
const DEFAULT_TDP_STEP_PERCENT = 10;
|
|
const DEFAULT_TDP_STEP_PERCENT = 10;
|
|
|
const DEFAULT_CHECK_INTERVAL_MS = 2000;
|
|
const DEFAULT_CHECK_INTERVAL_MS = 2000;
|
|
|
-const DEFAULT_UPP_PATH = 'upp';
|
|
|
|
|
|
|
|
|
|
function loadConfig(configPath) {
|
|
function loadConfig(configPath) {
|
|
|
if (!fs.existsSync(configPath)) {
|
|
if (!fs.existsSync(configPath)) {
|
|
@@ -29,7 +28,6 @@ function mergeConfig(config) {
|
|
|
maxTdpPercent: config.maxTdpPercent ?? DEFAULT_MAX_TDP_PERCENT,
|
|
maxTdpPercent: config.maxTdpPercent ?? DEFAULT_MAX_TDP_PERCENT,
|
|
|
tdpStepPercent: config.tdpStepPercent ?? DEFAULT_TDP_STEP_PERCENT,
|
|
tdpStepPercent: config.tdpStepPercent ?? DEFAULT_TDP_STEP_PERCENT,
|
|
|
checkIntervalMs: config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS,
|
|
checkIntervalMs: config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS,
|
|
|
- uppPath: config.uppPath ?? DEFAULT_UPP_PATH,
|
|
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -129,32 +127,20 @@ function getDeviceName(cardNumber) {
|
|
|
return `card${cardNumber}`;
|
|
return `card${cardNumber}`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function getCardPath(cardNumber) {
|
|
|
|
|
- return `/sys/class/drm/card${cardNumber}/device/pp_table`;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function setTdpForCard(cardNumber, power, config) {
|
|
|
|
|
- const cardPath = getCardPath(cardNumber);
|
|
|
|
|
- const cmd = `${config.uppPath} -p ${cardPath} set --write ` +
|
|
|
|
|
- `SmallPowerLimit1=${power} ` +
|
|
|
|
|
- `SmallPowerLimit2=${power} ` +
|
|
|
|
|
- `BoostPowerLimit=${power} ` +
|
|
|
|
|
- `smcPPTable/SocketPowerLimitAc0=${power} ` +
|
|
|
|
|
- `smcPPTable/SocketPowerLimitDc=${power}`;
|
|
|
|
|
- execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function getMaxTdp(cardNumber, config) {
|
|
|
|
|
|
|
+function getTdpLimit(cardNumber) {
|
|
|
try {
|
|
try {
|
|
|
- const cardPath = getCardPath(cardNumber);
|
|
|
|
|
- const out = execSync(
|
|
|
|
|
- `${config.uppPath} -p ${cardPath} get SmallPowerLimit1`,
|
|
|
|
|
- { encoding: 'utf-8', stdio: 'pipe' }
|
|
|
|
|
- ).trim();
|
|
|
|
|
- const match = out.match(/(\d+(?:\.\d+)?)/);
|
|
|
|
|
- if (match) return parseFloat(match[1]);
|
|
|
|
|
|
|
+ const out = execSync(`amd-smi static --json -g ${cardNumber}`, { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
|
|
|
+ const data = JSON.parse(out);
|
|
|
|
|
+ const gpuData = data.gpu_data[0];
|
|
|
|
|
+ if (gpuData && gpuData.limit && gpuData.limit.ppt0 && gpuData.limit.ppt0.max_power_limit) {
|
|
|
|
|
+ return gpuData.limit.ppt0.max_power_limit.value;
|
|
|
|
|
+ }
|
|
|
} catch { /* ignore */ }
|
|
} catch { /* ignore */ }
|
|
|
- return 120;
|
|
|
|
|
|
|
+ return 100;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function setTdpForCard(cardNumber, power) {
|
|
|
|
|
+ execSync(`amd-smi set -g ${cardNumber} -o ppt0 ${power}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function parseArgs(argv) {
|
|
function parseArgs(argv) {
|
|
@@ -198,7 +184,7 @@ function log(message) {
|
|
|
console.log(`[${timestamp}] ${message}`);
|
|
console.log(`[${timestamp}] ${message}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async function main() {
|
|
|
|
|
|
|
+function main() {
|
|
|
const configPath = parseArgs(process.argv);
|
|
const configPath = parseArgs(process.argv);
|
|
|
const rawConfig = loadConfig(configPath);
|
|
const rawConfig = loadConfig(configPath);
|
|
|
const config = mergeConfig(rawConfig || {});
|
|
const config = mergeConfig(rawConfig || {});
|
|
@@ -210,7 +196,6 @@ async function main() {
|
|
|
log(`TDP range: ${config.minTdpPercent}% - ${config.maxTdpPercent}%`);
|
|
log(`TDP range: ${config.minTdpPercent}% - ${config.maxTdpPercent}%`);
|
|
|
log(`TDP step: ${config.tdpStepPercent}%`);
|
|
log(`TDP step: ${config.tdpStepPercent}%`);
|
|
|
log(`Check interval: ${config.checkIntervalMs}ms`);
|
|
log(`Check interval: ${config.checkIntervalMs}ms`);
|
|
|
- log(`Upp path: ${config.uppPath}`);
|
|
|
|
|
log(`Config: ${configPath}`);
|
|
log(`Config: ${configPath}`);
|
|
|
log(`Monitoring ${cardIndices.length} GPU(s)`);
|
|
log(`Monitoring ${cardIndices.length} GPU(s)`);
|
|
|
log('Press Ctrl+C to stop');
|
|
log('Press Ctrl+C to stop');
|
|
@@ -219,7 +204,7 @@ async function main() {
|
|
|
const gpuStates = cardIndices.map(cardNumber => ({
|
|
const gpuStates = cardIndices.map(cardNumber => ({
|
|
|
cardNumber,
|
|
cardNumber,
|
|
|
deviceName: getDeviceName(cardNumber),
|
|
deviceName: getDeviceName(cardNumber),
|
|
|
- maxTdp: getMaxTdp(cardNumber, config),
|
|
|
|
|
|
|
+ maxTdp: getTdpLimit(cardNumber),
|
|
|
currentTdpPercent: null,
|
|
currentTdpPercent: null,
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
@@ -274,10 +259,9 @@ async function main() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (shouldApply) {
|
|
if (shouldApply) {
|
|
|
- const clampedPercent = Math.max(config.minTdpPercent, Math.min(config.maxTdpPercent, gpuState.currentTdpPercent));
|
|
|
|
|
- const power = wattsFromPercent(clampedPercent, gpuState.maxTdp);
|
|
|
|
|
- setTdpForCard(gpuState.cardNumber, power, config);
|
|
|
|
|
- log(`GPU ${gpuState.cardNumber} [${gpuState.deviceName}]: ${temp.toFixed(1)}°C | TDP: ${clampedPercent}% (${power}W)`);
|
|
|
|
|
|
|
+ const power = wattsFromPercent(gpuState.currentTdpPercent, gpuState.maxTdp);
|
|
|
|
|
+ setTdpForCard(gpuState.cardNumber, power);
|
|
|
|
|
+ log(`GPU ${gpuState.cardNumber} [${gpuState.deviceName}]: ${temp.toFixed(1)}°C | TDP: ${gpuState.currentTdpPercent}% (${power}W)`);
|
|
|
}
|
|
}
|
|
|
} else if (gpuState.currentTdpPercent !== null && iteration % 10 === 0) {
|
|
} else if (gpuState.currentTdpPercent !== null && iteration % 10 === 0) {
|
|
|
const currentWatts = wattsFromPercent(gpuState.currentTdpPercent, gpuState.maxTdp);
|
|
const currentWatts = wattsFromPercent(gpuState.currentTdpPercent, gpuState.maxTdp);
|