Per-thread LUT cache with multiple slots to avoid rebuilding when alternating codebooks
| 521 | |
| 522 | // Per-thread LUT cache with multiple slots to avoid rebuilding when alternating codebooks |
| 523 | struct LUTCache { |
| 524 | unsigned char luts[kLUTCacheSlots][kLUTSize]; |
| 525 | const float* cached_codes[kLUTCacheSlots] = {}; |
| 526 | // Store fingerprint to detect pointer reuse (ABA problem): |
| 527 | // when a tensor is freed and a new one reuses the same address, |
| 528 | // the pointer matches but the codebook content may differ. |
| 529 | float cached_fingerprints[kLUTCacheSlots][4] = {}; |
| 530 | int next_slot = 0; |
| 531 | |
| 532 | static void compute_fingerprint(const float* code, float* fp) { |
| 533 | fp[0] = code[0]; |
| 534 | fp[1] = code[1]; |
| 535 | fp[2] = code[127]; |
| 536 | fp[3] = code[255]; |
| 537 | } |
| 538 | |
| 539 | const unsigned char* get_lut(const float* code) { |
| 540 | float fp[4]; |
| 541 | compute_fingerprint(code, fp); |
| 542 | for (int i = 0; i < kLUTCacheSlots; ++i) { |
| 543 | if (cached_codes[i] == code && cached_fingerprints[i][0] == fp[0] && cached_fingerprints[i][1] == fp[1] && |
| 544 | cached_fingerprints[i][2] == fp[2] && cached_fingerprints[i][3] == fp[3]) { |
| 545 | return luts[i]; |
| 546 | } |
| 547 | } |
| 548 | // Cache miss: build and store in next slot (round-robin) |
| 549 | int slot = next_slot; |
| 550 | next_slot = (next_slot + 1) % kLUTCacheSlots; |
| 551 | build_quantize_lut(code, luts[slot]); |
| 552 | cached_codes[slot] = code; |
| 553 | for (int j = 0; j < 4; ++j) |
| 554 | cached_fingerprints[slot][j] = fp[j]; |
| 555 | return luts[slot]; |
| 556 | } |
| 557 | }; |
| 558 | |
| 559 | // Single global LUT cache (protected by mutex for thread safety during build) |
| 560 | static LUTCache g_lut_cache; |
nothing calls this directly
no outgoing calls
no test coverage detected