ENH make gpu power limits configurable

This commit is contained in:
Nathan Dwarshuis 2023-09-28 23:25:11 -04:00
parent b5920374a7
commit 1ab23d039b
3 changed files with 100 additions and 12 deletions

View File

@ -138,6 +138,23 @@ let FileSystem =
, default.geometry = FSGeo::{=} , default.geometry = FSGeo::{=}
} }
let GPUPower =
{-
Controls how the GPU will be queries so it can go to sleep when 'idle'.
freq_limit: only query the GPU every 'cycle_seconds' when frequency is below
this value; this should be a sensible value representing and
'idle' gpu state.
cycle_seconds: the number of seconds to query the GPU when below the idle
frequency limit; note that this must be long enough to let
the GPU go to sleep when it actually is ready to sleep, but
not so long that the next query will miss too much activity
in the case the GPU doesn't shut off
-}
{ Type = { freq_limit : Natural, cycle_seconds : Natural }
, default = { freq_limit = 250, cycle_seconds = 10 }
}
let Graphics = let Graphics =
{- {-
Defines Graphics module configuration Defines Graphics module configuration
@ -158,8 +175,9 @@ let Graphics =
, show_mem_util : Bool , show_mem_util : Bool
, show_vid_util : Bool , show_vid_util : Bool
, geometry : GfxGeo.Type , geometry : GfxGeo.Type
, power : GPUPower.Type
} }
, default.geometry = GfxGeo::{=} , default = { geometry = GfxGeo::{=}, power = GPUPower::{=} }
} }
let Memory = let Memory =

View File

@ -20,29 +20,43 @@ return function(update_freq, config, common, width, point)
i_o.assert_exe_exists(NVIDIA_EXE) i_o.assert_exe_exists(NVIDIA_EXE)
-- vars to process the nv settings glob local mk_query_cmd = function(props)
-- return string.format(
'%s --query-gpu=%s --format=csv,noheader,nounits',
NVIDIA_EXE,
pure.collapse(props, ',')
)
end
local NV_QUERY = NVIDIA_EXE.. -- TODO also use encoder for video util?
' --query-gpu=memory.used,memory.total,temperature.gpu,clocks.gr,clocks.mem,utilization.gpu,utilization.decoder'.. -- TODO add video clock speed?
' --format=csv,noheader,nounits' local query_stats = mk_query_cmd(
{
'memory.used',
'temperature.gpu',
'clocks.gr',
'clocks.mem',
'utilization.gpu',
'utilization.decoder'
}
)
local NV_REGEX = '(%d+), (%d+), (%d+), (%d+), (%d+), (%d+), (%d+)' local NV_REGEX = '(%d+), (%d+), (%d+), (%d+), (%d+), (%d+)'
local mod_state = { local mod_state = {
error = false, error = false,
total_memory = 0,
gpu_frequency = 0, gpu_frequency = 0,
memory_frequency = 0, memory_frequency = 0,
used_memory = 0, used_memory = 0,
total_memory = 0,
temp_reading = 0, temp_reading = 0,
gpu_utilization = 0, gpu_utilization = 0,
vid_utilization = 0 vid_utilization = 0
} }
local sleep_token = 0 local sleep_token = 0
local sleep_limit = 10 local sleep_limit = config.power.cycle_seconds
local gpu_idle_freq_limit = 250 local gpu_idle_freq_limit = config.power.freq_limit
-- TODO ensure this file exists -- TODO ensure this file exists
local runtime_status_file = config.dev_power..'/runtime_status' local runtime_status_file = config.dev_power..'/runtime_status'
@ -74,12 +88,11 @@ return function(update_freq, config, common, width, point)
sleep_token = 0 sleep_token = 0
end end
if is_active and want_nvidia_query and sleep_token == 0 then if is_active and want_nvidia_query and sleep_token == 0 then
local nvidia_settings_glob = i_o.execute_cmd(NV_QUERY) local nvidia_settings_glob = i_o.execute_cmd(query_stats)
if nvidia_settings_glob == nil then if nvidia_settings_glob == nil then
mod_state.error = 'Error' mod_state.error = 'Error'
else else
mod_state.used_memory, mod_state.used_memory,
mod_state.total_memory,
mod_state.temp_reading, mod_state.temp_reading,
mod_state.gpu_frequency, mod_state.gpu_frequency,
mod_state.memory_frequency, mod_state.memory_frequency,
@ -89,6 +102,11 @@ return function(update_freq, config, common, width, point)
mod_state.gpu_frequency = tonumber(mod_state.gpu_frequency) mod_state.gpu_frequency = tonumber(mod_state.gpu_frequency)
mod_state.error = false mod_state.error = false
end end
-- query total memory if we need it (do this here so we don't turn on
-- the gpu every time we start conky)
if mod_state.total_memory == 0 and config.show_mem_util then
mod_state.total_memory = tonumber(i_o.execute_cmd(mk_query_cmd({'memory.total'})))
end
elseif is_active then elseif is_active then
mod_state.error = false mod_state.error = false
else else

View File

@ -4,6 +4,7 @@ local err = require 'err'
local __math_floor = math.floor local __math_floor = math.floor
local __table_insert = table.insert local __table_insert = table.insert
local __string_format = string.format
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- zippy functions -- zippy functions
@ -50,9 +51,52 @@ M.reduce = function(f, init, seq)
end end
end end
M.member = function(x, seq)
return M.reduce(
function(acc, next) return acc or next == x end,
false,
M.values(seq)
)
end
M.collapse = function(seq, delim)
if #seq == 0 then
return ''
elseif #seq == 1 then
return seq[1]
else
local init, rest = M.head(seq)
return M.reduce(
function(acc, next) return __string_format('%s%s%s', acc, delim, next) end,
init,
rest
)
end
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- mappy functions -- mappy functions
M.keys = function(seq)
local r = {}
local n = 1
for k, _ in pairs(seq) do
r[n] = k
n = n + 1
end
return r
end
M.values = function(seq)
local r = {}
local n = 1
for _, v in pairs(seq) do
r[n] = v
n = n + 1
end
return r
end
M.map = function(f, seq, ...) M.map = function(f, seq, ...)
local r = {} local r = {}
for i = 1, #seq do for i = 1, #seq do
@ -144,6 +188,14 @@ M.length = function(tbl)
return n return n
end end
M.head = function(seq)
local r = {}
for i = 2, #seq do
r[i - 1] = seq[i]
end
return seq[1], r
end
-- a stupid but composable function -- a stupid but composable function
M.get = function(key, tbl) M.get = function(key, tbl)
return tbl[key] return tbl[key]