Files
wehub-resource-sync ec436095dd
Book-CI / test (macos-latest) (push) Has been cancelled
Book-CI / test (ubuntu-latest) (push) Has been cancelled
Book-CI / test (windows-latest) (push) Has been cancelled
Release Fake Tag / publish (push) Has been cancelled
Deploy / deploy (macos-latest) (push) Has been cancelled
Deploy / deploy (ubuntu-latest) (push) Has been cancelled
Deploy / deploy (windows-latest) (push) Has been cancelled
Release to PyPI / Build & publish sglang-kt (push) Has been cancelled
Release to PyPI / Build kt-kernel (Python 3.11) (push) Has been cancelled
Release to PyPI / Build kt-kernel (Python 3.12) (push) Has been cancelled
Release to PyPI / Publish kt-kernel to PyPI (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:30:03 +08:00

87 lines
2.7 KiB
C++

/**
* @Description : FP8 E4M3 dequantization for AVX2 (LUT-based)
* @Author : Claude
* @Date : 2026-03-18
* @Version : 1.0.0
* @Copyright (c) 2024 by KVCache.AI, All Rights Reserved.
*
* AVX512 uses _mm512_permutex2var_epi8 (VBMI) for FP8→BF16 LUT conversion.
* AVX2 uses a precomputed 256-entry FP8→FP32 lookup table + _mm256_i32gather_ps.
*
* FP8 E4M3 format: sign(1) + exponent(4) + mantissa(3)
* Reference: examples/test_fp8_moe.py:103-116
**/
#ifndef CPUINFER_OPERATOR_AVX2_FP8_DEQUANT_H
#define CPUINFER_OPERATOR_AVX2_FP8_DEQUANT_H
#include <immintrin.h>
#include <cmath>
#include <cstdint>
namespace avx2 {
// Precomputed FP8 E4M3 → FP32 lookup table (256 entries)
// Initialized once at program startup via init_fp8_lut()
struct FP8LUT {
alignas(32) float table[256];
bool initialized = false;
void init() {
if (initialized) return;
for (int i = 0; i < 256; i++) {
int sign = (i >> 7) & 1;
int exp = (i >> 3) & 0xF; // 4-bit exponent (bits 3-6)
int man = i & 0x7; // 3-bit mantissa (bits 0-2)
float val;
if (exp == 0 && man == 0) {
val = 0.0f; // zero
} else if (exp == 0) {
val = std::ldexp((float)man / 8.0f, -6); // subnormal: 2^(-6) * (0.man)
} else if (exp == 15 && man == 7) {
val = 0.0f; // Only 0x7F is NaN in E4M3. Treat as 0 to avoid propagation.
// E4M3 has no Inf. exp=15 with man=0-6 are valid finite values (256-448).
} else {
val = std::ldexp(1.0f + (float)man / 8.0f, exp - 7); // normal: 2^(exp-7) * (1.man)
}
table[i] = sign ? -val : val;
}
initialized = true;
}
};
// Global LUT instance
inline FP8LUT& get_fp8_lut() {
static FP8LUT lut;
return lut;
}
// Ensure LUT is initialized (call once at startup)
inline void ensure_fp8_lut_initialized() {
get_fp8_lut().init();
}
// ============================================================================
// AVX2 FP8→FP32 dequantization: 8 FP8 bytes → 8 FP32 values
// Uses _mm256_i32gather_ps for parallel LUT lookups
// ============================================================================
static inline __m256 fp8x8_to_fp32x8(const uint8_t* src) {
const float* lut = get_fp8_lut().table;
// Load 8 bytes, zero-extend to 32-bit indices
__m128i bytes = _mm_loadl_epi64((const __m128i*)src);
__m256i indices = _mm256_cvtepu8_epi32(bytes);
// Gather 8 floats from LUT (scale=4 because float is 4 bytes)
return _mm256_i32gather_ps(lut, indices, 4);
}
// Scalar fallback for non-aligned or tail elements
static inline float fp8_to_fp32_scalar(uint8_t val) {
return get_fp8_lut().table[val];
}
} // namespace avx2
#endif // CPUINFER_OPERATOR_AVX2_FP8_DEQUANT_H