feat(scheduler-allocation): add resreq fingerprinting for the cache
Adds rusage_hash to xce_primary_resreq_t (computed once by parse_resreq_primary() via a new hash_rusages() helper, hashing rusage content rather than raw struct bytes since resname is a non-interned strdup'd pointer), the xce_resreq_fingerprint_t value type, and resreq_fp on xce_primary_schedobj_t, populated once at real job-ingest time in core_build_primary_schedobj(). Task 1.2 (scheduler-allocation spec). Requirement 7.1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
This commit is contained in:
parent
d5d2c1caf5
commit
5a6706761d
9 changed files with 550 additions and 4 deletions
|
|
@ -304,6 +304,90 @@ cleanup_parser:
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Req 7.1 (scheduler-allocation spec): stable resource-usage fingerprint
|
||||
* over a primary resreq's parsed `rusages` list.
|
||||
*
|
||||
* Style: FNV-1a 64-bit, consistent with this spec's cache-key hash
|
||||
* (requirements.md "New types (Req 7)", cache_key_hash_ -- FNV-1a 32-bit
|
||||
* over xce_cache_key_t, a later task's own scope). No existing hash-update
|
||||
* helper for this purpose exists anywhere in this codebase; this is new,
|
||||
* self-contained, file-local code.
|
||||
*
|
||||
* Design choice -- ORDER-DEPENDENT: each rusage_value_t is folded into the
|
||||
* hash in xlist storage order (the order its `rusage[...]` clause was
|
||||
* parsed in), rather than sorted/combined commutatively. This is simpler
|
||||
* (no auxiliary sort, no need for a commutative combine step) and is
|
||||
* sufficient for Req 7.1's actual requirement: parsing the *same* resreq
|
||||
* string twice always produces the same xlist order, so the fingerprint
|
||||
* is stable for identical input, which is all the unsatisfiable-resreq
|
||||
* cache needs (a false miss on a reordered-but-equivalent resreq costs one
|
||||
* redundant, still-correct allocation attempt; it can never cause a false
|
||||
* hit).
|
||||
*
|
||||
* Field selection -- deliberately per-field, NOT a raw memcmp/byte-hash of
|
||||
* `sizeof(rusage_value_t)`: `rusage_value_t.res` is a `char*` (owned,
|
||||
* strdup'd per parse -- see kill_rusage_value()'s plain `FREEUP(val->res)`,
|
||||
* not an xstring-interned/refcounted pointer). Hashing the raw struct
|
||||
* bytes would hash the POINTER VALUE (heap address), which differs across
|
||||
* separate parses of identical content and would make the "fingerprint"
|
||||
* unstable by construction. Each field is hashed individually: the
|
||||
* resource name's string CONTENT (not its pointer), then the raw bytes of
|
||||
* `value`/`per_flag`/`duration`/`decay_flag` (safe to hash as raw bytes --
|
||||
* none of those are pointers). */
|
||||
static uint64_t hash_rusages(xlist_t* rusages)
|
||||
{
|
||||
uint64_t h = 14695981039346656037ULL; /* FNV-1a 64-bit offset basis */
|
||||
int i;
|
||||
|
||||
if (rusages == NULL) {
|
||||
return h;
|
||||
}
|
||||
|
||||
for (i = 0; i < rusages->count; i++) {
|
||||
rusage_value_t* v = (rusage_value_t*)xlist_get(rusages, i);
|
||||
const unsigned char* p;
|
||||
size_t n;
|
||||
size_t j;
|
||||
|
||||
if (v == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Resource name: hash the string content, not the pointer. */
|
||||
p = (v->res != NULL) ? (const unsigned char*)v->res
|
||||
: (const unsigned char*)"";
|
||||
n = (v->res != NULL) ? strlen(v->res) : 0;
|
||||
for (j = 0; j < n; j++) {
|
||||
h = (h ^ (uint64_t)p[j]) * 1099511628211ULL;
|
||||
}
|
||||
/* Field separator -- keeps a name's trailing bytes from aliasing
|
||||
* with the numeric fields that follow (e.g. "ab"+1 vs "a"+"b1"). */
|
||||
h = (h ^ 0xFFu) * 1099511628211ULL;
|
||||
|
||||
p = (const unsigned char*)&v->value;
|
||||
for (j = 0; j < sizeof(v->value); j++) {
|
||||
h = (h ^ (uint64_t)p[j]) * 1099511628211ULL;
|
||||
}
|
||||
|
||||
p = (const unsigned char*)&v->per_flag;
|
||||
for (j = 0; j < sizeof(v->per_flag); j++) {
|
||||
h = (h ^ (uint64_t)p[j]) * 1099511628211ULL;
|
||||
}
|
||||
|
||||
p = (const unsigned char*)&v->duration;
|
||||
for (j = 0; j < sizeof(v->duration); j++) {
|
||||
h = (h ^ (uint64_t)p[j]) * 1099511628211ULL;
|
||||
}
|
||||
|
||||
p = (const unsigned char*)&v->decay_flag;
|
||||
for (j = 0; j < sizeof(v->decay_flag); j++) {
|
||||
h = (h ^ (uint64_t)p[j]) * 1099511628211ULL;
|
||||
}
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
int parse_resreq_primary(xce_primary_resreq_t* primary, char* resreqString,
|
||||
var resmgr, char** errmsg)
|
||||
{
|
||||
|
|
@ -347,6 +431,10 @@ int parse_resreq_primary(xce_primary_resreq_t* primary, char* resreqString,
|
|||
primary->minTasks = 1;
|
||||
primary->maxTasks = 1;
|
||||
|
||||
/* Req 7.1: compute once, at parse time -- stable for the life of this
|
||||
* resreq, reused by every later cache lookup instead of recomputing. */
|
||||
primary->rusage_hash = hash_rusages(&(primary->rusages));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue