hexagon: add configurable HTP DCVS/HMX power mode (#30423)
* hexagon: add configurable HTP DCVS/HMX power mode * hexagon: address PR review feedback (drop CLI flag, align QAIRT power modes, comment/log cleanup) * hexagon: add --hex-power-mode flag to run.py Hooks GGML_HEXAGON_POWER_MODE into the unified run.py runner, following the existing --hex-nhvx pattern. The env var must be set before the process touches the Hexagon backend registry (see comment in ggml_hexagon_init), so this is exposed as a runner flag rather than a common/arg.cpp CLI option. * hexagon: keep HTP awake for DCVS power profiles * Apply suggestion from @max-krasnyansky --------- Co-authored-by: Max Krasnyansky <maxk@qti.qualcomm.com>
This commit is contained in:
1 parent
ace20f679e
commit
ec0bfac179
6 files changed
+180
-15
No files matched your search
@@ -330,6 +330,31 @@ on 4 physical NPUs, or `--devices 'HTP0[0-1:0],HTP1[0-1:1]'` on 2 physical NPUs
|
||||
- `GGML_HEXAGON_DMA64=0` (default: enabled on v81+)
|
||||
Disables 64-bit DMA for model weights. Set to `1` to enable it explicitly on a supported architecture.
|
||||
|
||||
- `GGML_HEXAGON_POWER_MODE=default`
|
||||
Controls the DCVS/HMX power mode requested from the HTP DSP for the session. Modes are listed
|
||||
from lowest to highest power. All modes except `burst` leave DCVS enabled and keep the HTP awake.
|
||||
|
||||
- `low_power_saver`
|
||||
- `power_saver`
|
||||
- `high_power_saver`
|
||||
- `low_balanced`
|
||||
- `balanced`
|
||||
- `high_performance`
|
||||
- `sustained_high_performance`
|
||||
- `burst` (maximum clocks, disables DCVS; may thermal-throttle on sustained workloads)
|
||||
- `default` (currently aliases to `burst`)
|
||||
|
||||
Unknown values are logged as a warning and fall back to `burst`.
|
||||
|
||||
This must be set in the environment before the process initializes the backend registry, so
|
||||
there is no `llama-*` CLI flag for it. The `run.py` runner exposes it as `--hex-power-mode`.
|
||||
|
||||
Examples:
|
||||
|
||||
`GGML_HEXAGON_POWER_MODE=balanced llama-completion ...`
|
||||
`./scripts/snapdragon/run.py --hex-power-mode sustained_high_performance ...`
|
||||
|
||||
|
||||
- `GGML_HEXAGON_VERBOSE=1`
|
||||
Enables verbose logging of Ops from the backend. Example output:
|
||||
|
||||
|
||||
@@ -108,6 +108,41 @@ static int opt_verbose = 0;
|
||||
static int opt_profile = 0; // profiling mode (0-disabled, 1-basic, 2-pmu)
|
||||
static bool opt_hostbuf = false;
|
||||
static bool opt_dma64 = false;
|
||||
static int opt_power_mode = HTP_POWER_MODE_BURST; // DCVS/HMX power profile requested at session start
|
||||
|
||||
static const std::pair<const char *, int> htp_power_mode_names[] = {
|
||||
{ "low_power_saver", HTP_POWER_MODE_LOW_POWER_SAVER },
|
||||
{ "power_saver", HTP_POWER_MODE_POWER_SAVER },
|
||||
{ "high_power_saver", HTP_POWER_MODE_HIGH_POWER_SAVER },
|
||||
{ "low_balanced", HTP_POWER_MODE_LOW_BALANCED },
|
||||
{ "balanced", HTP_POWER_MODE_BALANCED },
|
||||
{ "high_performance", HTP_POWER_MODE_HIGH_PERFORMANCE },
|
||||
{ "sustained_high_performance", HTP_POWER_MODE_SUSTAINED_HIGH_PERFORMANCE },
|
||||
{ "burst", HTP_POWER_MODE_BURST },
|
||||
{ "default", HTP_POWER_MODE_BURST }, // flip this to change the default mode
|
||||
};
|
||||
|
||||
static int htp_power_mode_from_str(const char * s) {
|
||||
for (auto & e : htp_power_mode_names) {
|
||||
#if defined(_MSC_VER)
|
||||
if (_stricmp(s, e.first) == 0) {
|
||||
#else
|
||||
if (strcasecmp(s, e.first) == 0) {
|
||||
#endif
|
||||
return e.second;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char * htp_power_mode_to_str(int mode) {
|
||||
for (auto & e : htp_power_mode_names) {
|
||||
if (e.second == mode) {
|
||||
return e.first;
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static size_t opt_mbuf_dyn = 512ul * 1024 * 1024; // max dynamic (compute) buffer size
|
||||
static size_t opt_mbuf_static = 1ul * 1024 * 1024 * 1024; // max static (weight/KV) buffer size
|
||||
@@ -4956,12 +4991,13 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
|
||||
this->op_batch = new ggml_hexagon_opbatch(this, opt_opbatch, this->max_vmem);
|
||||
|
||||
// Start dspqueue/opbatch processing
|
||||
err = htp_iface_start(this->handle, this->session_id, this->queue_id, this->n_threads, opt_nhmx, this->max_vmem);
|
||||
err = htp_iface_start(this->handle, this->session_id, this->queue_id, this->n_threads, opt_nhmx, this->max_vmem, (uint32_t) opt_power_mode);
|
||||
if (err != 0) {
|
||||
GGML_LOG_ERROR("ggml-hex: %s failed to start session: 0x%08x\n", this->c_name(), (unsigned) err);
|
||||
throw std::runtime_error("ggml-hex: iface start failed (see log for details)");
|
||||
}
|
||||
this->valid_iface = true;
|
||||
GGML_LOG_INFO("ggml-hex: %s power mode: %s\n", this->c_name(), htp_power_mode_to_str(opt_power_mode));
|
||||
|
||||
if (opt_profile) {
|
||||
htp_iface_pmu_conf pmu_conf{};
|
||||
@@ -9959,6 +9995,7 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
const char * str_optrace = getenv("GGML_HEXAGON_OPTRACE");
|
||||
const char * str_hostbuf = getenv("GGML_HEXAGON_HOSTBUF");
|
||||
const char * str_dma64 = getenv("GGML_HEXAGON_DMA64");
|
||||
const char * str_power_mode = getenv("GGML_HEXAGON_POWER_MODE");
|
||||
|
||||
// Init Arch first since it affects other defaults
|
||||
if (!str_arch) {
|
||||
@@ -9999,6 +10036,14 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
opt_opfusion = str_opfusion ? atoi(str_opfusion) : opt_opfusion;
|
||||
opt_profile = str_profile ? atoi(str_profile) : 0;
|
||||
opt_etm = str_etm ? atoi(str_etm) : 0;
|
||||
if (str_power_mode) {
|
||||
int m = htp_power_mode_from_str(str_power_mode);
|
||||
if (m < 0) {
|
||||
GGML_LOG_WARN("ggml-hex: unknown GGML_HEXAGON_POWER_MODE '%s', falling back to 'burst'\n", str_power_mode);
|
||||
} else {
|
||||
opt_power_mode = m;
|
||||
}
|
||||
}
|
||||
opt_nhvx = str_nhvx ? strtoul(str_nhvx, NULL, 0) : opt_nhvx;
|
||||
opt_nhmx = str_nhmx ? atoi(str_nhmx) : opt_nhmx;
|
||||
opt_mm_select = str_mm_select ? atoi(str_mm_select) : opt_mm_select;
|
||||
|
||||
@@ -197,6 +197,20 @@ enum htp_profiler_mode {
|
||||
HTP_PROF_TRACE = 3,
|
||||
};
|
||||
|
||||
// DCVS/HMX power profile requested for the session.
|
||||
enum htp_power_mode {
|
||||
HTP_POWER_MODE_LOW_POWER_SAVER = 0,
|
||||
HTP_POWER_MODE_POWER_SAVER = 1,
|
||||
HTP_POWER_MODE_HIGH_POWER_SAVER = 2,
|
||||
HTP_POWER_MODE_LOW_BALANCED = 3,
|
||||
HTP_POWER_MODE_BALANCED = 4,
|
||||
HTP_POWER_MODE_HIGH_PERFORMANCE = 5,
|
||||
HTP_POWER_MODE_SUSTAINED_HIGH_PERFORMANCE = 6,
|
||||
HTP_POWER_MODE_BURST = 7,
|
||||
|
||||
HTP_POWER_MODE_COUNT
|
||||
};
|
||||
|
||||
enum htp_trace_event_id {
|
||||
HTP_TRACE_EVT_DMA = 0,
|
||||
HTP_TRACE_EVT_L2FLUSH = 1,
|
||||
|
||||
@@ -11,7 +11,7 @@ struct htp_iface_pmu_conf {
|
||||
};
|
||||
|
||||
interface htp_iface : remote_handle64 {
|
||||
AEEResult start(in uint32 sess_id, in uint64 dsp_queue_id, in uint32 n_hvx, in uint32 n_hmx, in uint64 max_vmem);
|
||||
AEEResult start(in uint32 sess_id, in uint64 dsp_queue_id, in uint32 n_hvx, in uint32 n_hmx, in uint64 max_vmem, in uint32 power_mode);
|
||||
AEEResult stop();
|
||||
AEEResult mmap(in uint32 fd, in uint64 size);
|
||||
AEEResult munmap(in uint32 fd);
|
||||
|
||||
@@ -363,12 +363,91 @@ static void htp_main_thread(void * context);
|
||||
static void htp_packet_callback(dspqueue_t queue, int error, void * context);
|
||||
static void htp_error_callback(dspqueue_t queue, int error, void * context);
|
||||
|
||||
AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp_queue_id, uint32_t n_hvx, uint32_t n_hmx, uint64_t max_vmem) {
|
||||
// Non-BURST modes leave DCVS enabled and keep the HTP awake.
|
||||
// BURST pins all corners to MAX and disables DCVS.
|
||||
// Corner constants are from HAP_power.h (HAP_dcvs_voltage_corner_t / HAP_dcvs_exp_vcorner_t).
|
||||
struct htp_power_profile {
|
||||
boolean dcvs_enable;
|
||||
uint32_t bus_min_corner;
|
||||
uint32_t bus_max_corner;
|
||||
uint32_t bus_target_corner;
|
||||
uint32_t core_min_corner;
|
||||
uint32_t core_max_corner;
|
||||
uint32_t core_target_corner;
|
||||
boolean sleep_disable;
|
||||
uint32_t hmx_min_corner; // only used when __HVX_ARCH__ >= 75
|
||||
uint32_t hmx_max_corner;
|
||||
uint32_t hmx_target_corner;
|
||||
uint32_t hmx_perf_mode;
|
||||
};
|
||||
|
||||
#define HTP_POWER_PROFILE_ENTRY(min_c, max_c, hmx_c) \
|
||||
{ \
|
||||
.dcvs_enable = TRUE, \
|
||||
.bus_min_corner = (min_c), \
|
||||
.bus_max_corner = (max_c), \
|
||||
.bus_target_corner = (max_c), \
|
||||
.core_min_corner = (min_c), \
|
||||
.core_max_corner = (max_c), \
|
||||
.core_target_corner = (max_c), \
|
||||
.sleep_disable = TRUE, \
|
||||
.hmx_min_corner = (hmx_c), \
|
||||
.hmx_max_corner = (hmx_c), \
|
||||
.hmx_target_corner = (hmx_c), \
|
||||
.hmx_perf_mode = HAP_CLK_PERF_HIGH, \
|
||||
}
|
||||
|
||||
static const struct htp_power_profile htp_power_profiles[HTP_POWER_MODE_COUNT] = {
|
||||
[HTP_POWER_MODE_LOW_POWER_SAVER] =
|
||||
HTP_POWER_PROFILE_ENTRY(HAP_DCVS_VCORNER_SVS2, HAP_DCVS_VCORNER_SVS2, HAP_DCVS_EXP_VCORNER_MIN_SVS),
|
||||
[HTP_POWER_MODE_POWER_SAVER] =
|
||||
HTP_POWER_PROFILE_ENTRY(HAP_DCVS_VCORNER_SVS, HAP_DCVS_VCORNER_SVS_PLUS, HAP_DCVS_EXP_VCORNER_SVS),
|
||||
[HTP_POWER_MODE_HIGH_POWER_SAVER] =
|
||||
HTP_POWER_PROFILE_ENTRY(HAP_DCVS_VCORNER_SVS_PLUS, HAP_DCVS_VCORNER_NOM, HAP_DCVS_EXP_VCORNER_SVS_L2),
|
||||
[HTP_POWER_MODE_LOW_BALANCED] =
|
||||
HTP_POWER_PROFILE_ENTRY(HAP_DCVS_VCORNER_NOM, HAP_DCVS_VCORNER_NOM_PLUS, HAP_DCVS_EXP_VCORNER_NOM),
|
||||
[HTP_POWER_MODE_BALANCED] =
|
||||
HTP_POWER_PROFILE_ENTRY(HAP_DCVS_VCORNER_NOM_PLUS, HAP_DCVS_VCORNER_TURBO, HAP_DCVS_EXP_VCORNER_NOM_L1),
|
||||
[HTP_POWER_MODE_HIGH_PERFORMANCE] =
|
||||
HTP_POWER_PROFILE_ENTRY(HAP_DCVS_VCORNER_TURBO, HAP_DCVS_VCORNER_TURBO_PLUS, HAP_DCVS_EXP_VCORNER_TUR_L2),
|
||||
[HTP_POWER_MODE_SUSTAINED_HIGH_PERFORMANCE] =
|
||||
HTP_POWER_PROFILE_ENTRY(HAP_DCVS_VCORNER_NOM_PLUS, HAP_DCVS_VCORNER_TURBO_PLUS, HAP_DCVS_EXP_VCORNER_TUR_L2),
|
||||
[HTP_POWER_MODE_BURST] = {
|
||||
.dcvs_enable = FALSE,
|
||||
.bus_min_corner = HAP_DCVS_VCORNER_MAX,
|
||||
.bus_max_corner = HAP_DCVS_VCORNER_MAX,
|
||||
.bus_target_corner = HAP_DCVS_VCORNER_MAX,
|
||||
.core_min_corner = HAP_DCVS_VCORNER_MAX,
|
||||
.core_max_corner = HAP_DCVS_VCORNER_MAX,
|
||||
.core_target_corner = HAP_DCVS_VCORNER_MAX,
|
||||
.sleep_disable = TRUE,
|
||||
.hmx_min_corner = HAP_DCVS_EXP_VCORNER_MAX,
|
||||
.hmx_max_corner = HAP_DCVS_EXP_VCORNER_MAX,
|
||||
.hmx_target_corner = HAP_DCVS_EXP_VCORNER_MAX,
|
||||
.hmx_perf_mode = HAP_CLK_PERF_HIGH,
|
||||
},
|
||||
};
|
||||
|
||||
#undef HTP_POWER_PROFILE_ENTRY
|
||||
|
||||
AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp_queue_id, uint32_t n_hvx, uint32_t n_hmx, uint64_t max_vmem, uint32_t power_mode) {
|
||||
struct htp_handle * h = (struct htp_handle *) handle;
|
||||
if (!h) {
|
||||
return AEE_EBADPARM;
|
||||
}
|
||||
|
||||
if (power_mode >= HTP_POWER_MODE_COUNT) {
|
||||
FARF(ERROR, "invalid power-mode %u, falling back to burst", (unsigned) power_mode);
|
||||
power_mode = HTP_POWER_MODE_BURST;
|
||||
}
|
||||
const struct htp_power_profile * pwr = &htp_power_profiles[power_mode];
|
||||
FARF(ALWAYS, "power-mode %u : dcvs-enable %d bus[%u,%u,%u] core[%u,%u,%u] sleep-disable %d hmx[%u,%u,%u]",
|
||||
(unsigned) power_mode, (int) pwr->dcvs_enable,
|
||||
(unsigned) pwr->bus_min_corner, (unsigned) pwr->bus_max_corner, (unsigned) pwr->bus_target_corner,
|
||||
(unsigned) pwr->core_min_corner, (unsigned) pwr->core_max_corner, (unsigned) pwr->core_target_corner,
|
||||
(int) pwr->sleep_disable,
|
||||
(unsigned) pwr->hmx_min_corner, (unsigned) pwr->hmx_max_corner, (unsigned) pwr->hmx_target_corner);
|
||||
|
||||
if (h->ctx) {
|
||||
FARF(ERROR, "Queue already open");
|
||||
return AEE_EITEMBUSY;
|
||||
@@ -499,17 +578,17 @@ AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp
|
||||
|
||||
request.type = HAP_power_set_DCVS_v3;
|
||||
request.dcvs_v3.set_dcvs_enable = TRUE;
|
||||
request.dcvs_v3.dcvs_enable = FALSE;
|
||||
request.dcvs_v3.dcvs_enable = pwr->dcvs_enable;
|
||||
request.dcvs_v3.set_bus_params = TRUE;
|
||||
request.dcvs_v3.bus_params.min_corner = HAP_DCVS_VCORNER_MAX;
|
||||
request.dcvs_v3.bus_params.max_corner = HAP_DCVS_VCORNER_MAX;
|
||||
request.dcvs_v3.bus_params.target_corner = HAP_DCVS_VCORNER_MAX;
|
||||
request.dcvs_v3.bus_params.min_corner = pwr->bus_min_corner;
|
||||
request.dcvs_v3.bus_params.max_corner = pwr->bus_max_corner;
|
||||
request.dcvs_v3.bus_params.target_corner = pwr->bus_target_corner;
|
||||
request.dcvs_v3.set_core_params = TRUE;
|
||||
request.dcvs_v3.core_params.min_corner = HAP_DCVS_VCORNER_MAX;
|
||||
request.dcvs_v3.core_params.max_corner = HAP_DCVS_VCORNER_MAX;
|
||||
request.dcvs_v3.core_params.target_corner = HAP_DCVS_VCORNER_MAX;
|
||||
request.dcvs_v3.core_params.min_corner = pwr->core_min_corner;
|
||||
request.dcvs_v3.core_params.max_corner = pwr->core_max_corner;
|
||||
request.dcvs_v3.core_params.target_corner = pwr->core_target_corner;
|
||||
request.dcvs_v3.set_sleep_disable = TRUE;
|
||||
request.dcvs_v3.sleep_disable = TRUE;
|
||||
request.dcvs_v3.sleep_disable = pwr->sleep_disable;
|
||||
|
||||
#if (__HEXAGON_ARCH__ >= 79)
|
||||
HAP_set_dcvs_v3_protected_bus_corners(&request, 1);
|
||||
@@ -537,10 +616,10 @@ AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp
|
||||
request.hmx_v2.set_power = TRUE;
|
||||
request.hmx_v2.power_up = TRUE;
|
||||
request.hmx_v2.set_clock = TRUE;
|
||||
request.hmx_v2.target_corner = HAP_DCVS_EXP_VCORNER_MAX;
|
||||
request.hmx_v2.min_corner = HAP_DCVS_EXP_VCORNER_MAX;
|
||||
request.hmx_v2.max_corner = HAP_DCVS_EXP_VCORNER_MAX;
|
||||
request.hmx_v2.perf_mode = HAP_CLK_PERF_HIGH;
|
||||
request.hmx_v2.target_corner = pwr->hmx_target_corner;
|
||||
request.hmx_v2.min_corner = pwr->hmx_min_corner;
|
||||
request.hmx_v2.max_corner = pwr->hmx_max_corner;
|
||||
request.hmx_v2.perf_mode = pwr->hmx_perf_mode;
|
||||
FARF(ALWAYS, "Setting HMX clock\n");
|
||||
err = HAP_power_set((void *) ctx, &request);
|
||||
if (err != AEE_SUCCESS) {
|
||||
|
||||
@@ -159,6 +159,7 @@ def main():
|
||||
parser.add_argument("--hex-profile", help="Enable NPU/Hexagon profiling and performance metrics print (GGML_HEXAGON_PROFILE)")
|
||||
parser.add_argument("--hex-nhvx", help="Number of HVX units to use (GGML_HEXAGON_NHVX)")
|
||||
parser.add_argument("--hex-nhmx", help="Number of HMX units to use. 0 disables HMX power-up (GGML_HEXAGON_NHMX)")
|
||||
parser.add_argument("--hex-power-mode", help="HTP DCVS/HMX power mode: low_power_saver, power_saver, high_power_saver, low_balanced, balanced, high_performance, sustained_high_performance, burst (GGML_HEXAGON_POWER_MODE)")
|
||||
parser.add_argument("--hex-hostbuf", help="Enable host buffers (GGML_HEXAGON_HOSTBUF)")
|
||||
parser.add_argument("--hex-dma64", nargs="?", const="1", help="Enable (1) or disable (0) 64-bit DMA for model weights (GGML_HEXAGON_DMA64)")
|
||||
parser.add_argument("--hex-opbatch", help="Maximum number of operations to batch into a single HTP execution (GGML_HEXAGON_OPBATCH)")
|
||||
@@ -302,6 +303,7 @@ def main():
|
||||
# Other Hexagon environment variables
|
||||
set_env("GGML_HEXAGON_NHVX", args.hex_nhvx)
|
||||
set_env("GGML_HEXAGON_NHMX", args.hex_nhmx)
|
||||
set_env("GGML_HEXAGON_POWER_MODE", args.hex_power_mode)
|
||||
set_env("GGML_HEXAGON_HOSTBUF", args.hex_hostbuf)
|
||||
set_env("GGML_HEXAGON_DMA64", args.hex_dma64)
|
||||
set_env("GGML_HEXAGON_OPBATCH", args.hex_opbatch)
|
||||
|
||||
Reference in new issue
Block a user