@@ -60,6 +60,12 @@ static const char* const VLM_DEFAULT_PROMPT = "Describe the image.";
6060
6161#define MAX_PATHS 16
6262
63+ /* QAIRT prefill chunk size: the engine pads input_ids up to the next multiple
64+ * of this before running prefill, so the real prefill work (and thus the honest
65+ * tokens/sec) is over the padded length, not the raw prompt length. llama_cpp
66+ * has no such padding and is left untouched. See qcom-ai-hub/geniex#1194. */
67+ #define QAIRT_PREFILL_CHUNK 128
68+
6369typedef struct {
6470 const char * plugin ;
6571 const char * device ;
@@ -136,6 +142,19 @@ typedef struct {
136142 char err [256 ];
137143} run_result_t ;
138144
145+ /* Adjust the reported prefill metrics for the engine's real prefill work.
146+ * QAIRT pads the prompt to a QAIRT_PREFILL_CHUNK multiple before prefill, so
147+ * prompt_tokens/prefill_tps should reflect that padded length (#1194); QAIRT's
148+ * prompt_time equals ttft, so recomputing the rate over the padded count keeps
149+ * rate == prompt_tokens / prompt_time consistent. llama_cpp does no such
150+ * padding, so its metrics are left as the SDK reported them. */
151+ static void normalize_prefill_metrics (run_result_t * r , const char * plugin ) {
152+ if (!plugin || strcmp (plugin , "qairt" ) != 0 || r -> prompt_tokens <= 0 ) return ;
153+ int64_t padded = ((r -> prompt_tokens + QAIRT_PREFILL_CHUNK - 1 ) / QAIRT_PREFILL_CHUNK ) * QAIRT_PREFILL_CHUNK ;
154+ r -> prompt_tokens = padded ;
155+ r -> prefill_tps = r -> prompt_time_us > 0 ? (double )padded / ((double )r -> prompt_time_us / 1e6 ) : 0.0 ;
156+ }
157+
139158static void die (int32_t code , const char * what ) {
140159 const char * msg = geniex_get_error_message ((geniex_ErrorCode )code );
141160 fprintf (stderr , "ERROR: %s: %s (code=%d)\n" , what , msg ? msg : "?" , code );
@@ -206,6 +225,9 @@ static void usage(const char* argv0) {
206225 " only way to bench plugins that don't support\n"
207226 " input_ids (today: qairt). With this flag, reported\n"
208227 " `pp` is the tokenizer's count, NOT --n-prompt.\n"
228+ " For qairt, `pp` and prefill tok/s are reported over\n"
229+ " the padded length ceil(pp/128)*128, matching the\n"
230+ " engine's 128-token prefill chunking (#1194).\n"
209231 " --no-reset-between-runs\n"
210232 " keep KV cache across measured runs (default is\n"
211233 " to call geniex_llm_reset() before every run so\n"
@@ -1054,6 +1076,7 @@ static void run_llm(const options_t* o, const char* device_id, int32_t ngl, run_
10541076 r -> decode_tps = gout .profile_data .decoding_speed ;
10551077 r -> stop_reason = gout .profile_data .stop_reason ;
10561078 r -> status = 0 ;
1079+ normalize_prefill_metrics (r , o -> plugin );
10571080 }
10581081
10591082 if (!is_warmup && o -> accuracy && gout .full_text ) {
@@ -1138,6 +1161,7 @@ static void run_vlm(const options_t* o, const char* device_id, int32_t ngl, run_
11381161 r -> decode_tps = gout .profile_data .decoding_speed ;
11391162 r -> stop_reason = gout .profile_data .stop_reason ;
11401163 r -> status = 0 ;
1164+ normalize_prefill_metrics (r , o -> plugin );
11411165 }
11421166
11431167 if (!is_warmup && o -> accuracy && gout .full_text ) {
0 commit comments