Skip to content

Commit 75756fa

Browse files
committed
remove SSE4.2 support
1 parent 9ba4fca commit 75756fa

11 files changed

Lines changed: 140 additions & 945 deletions

File tree

cpp/deglib/include/config.h

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,13 @@
1616

1717
// Compile methods with this attribute for AVX2 F16C functions on GCC/Clang.
1818
// Covers AVX2, F16C, and FMA intrinsics used in fp16_ip.h and fp32_ip.h.
19+
// F16C is assumed available on all CPUs supporting AVX2.
1920
#if defined(DEGLIB_X86) && (defined(__GNUC__) || defined(__clang__))
2021
#define DEGLIB_TARGET_AVX2 __attribute__((target("avx2,f16c,fma")))
2122
#else
2223
#define DEGLIB_TARGET_AVX2
2324
#endif
2425

25-
// Compile methods with this attribute for F16C functions on GCC/Clang.
26-
// Includes FMA for _mm_fmadd_ps / _mm256_fmadd_ps used in SSE/AVX2 paths.
27-
#if defined(DEGLIB_X86) && (defined(__GNUC__) || defined(__clang__))
28-
#define DEGLIB_TARGET_F16C __attribute__((target("f16c,avx,fma")))
29-
#else
30-
#define DEGLIB_TARGET_F16C
31-
#endif
32-
3326
// Architecture intrinsic headers
3427
#if defined(DEGLIB_X86)
3528
#ifdef _MSC_VER
@@ -47,8 +40,11 @@
4740
// Runtime CPU feature detection
4841
// ---------------------------------------------------------------------------
4942
// Uses CPUID (via __cpuidex on MSVC, __cpuid_count on GCC/Clang) to detect
50-
// SSE4.2, F16C, AVX, AVX2, and AVX-512F at runtime. Results are cached in a
51-
// function-local static so the CPUID query runs only once.
43+
// AVX2 and AVX-512F at runtime. Results are cached in a function-local static
44+
// so the CPUID query runs only once.
45+
//
46+
// AVX2 is the minimum x86 baseline requirement. F16C is always available on
47+
// CPUs that support AVX2, so it is not tracked separately.
5248
//
5349
// The distance headers compile all SIMD code paths unconditionally (the
5450
// intrinsic headers are always included below). At runtime, select_dist()
@@ -75,22 +71,13 @@ namespace deglib::cpu {
7571

7672
// Cached hardware feature flags, populated on first call via a function-local static.
7773
struct CpuFeatures {
78-
bool sse42{false};
79-
bool f16c{false};
80-
bool avx{false};
8174
bool avx2{false};
8275
bool avx512f{false};
8376

8477
CpuFeatures() {
8578
#if defined(DEGLIB_X86)
8679
int cpu_info[4] = {0};
8780

88-
// Leaf 1: feature flags in ECX and EDX
89-
cpuid(1, 0, cpu_info);
90-
sse42 = (cpu_info[2] & (1 << 20)) != 0;
91-
f16c = (cpu_info[2] & (1 << 29)) != 0;
92-
avx = (cpu_info[2] & (1 << 28)) != 0;
93-
9481
// Leaf 7, subleaf 0: extended feature flags in EBX and ECX
9582
cpuid(7, 0, cpu_info);
9683
avx2 = (cpu_info[1] & (1 << 5)) != 0;
@@ -109,9 +96,6 @@ namespace deglib::cpu {
10996
// Runtime CPU feature detection — safe to call from any translation unit.
11097
// These checks are performed once (cached) and have zero cost per call thereafter.
11198

112-
inline bool has_sse42() { return detail::features().sse42; }
113-
inline bool has_f16c() { return detail::features().f16c; }
114-
inline bool has_avx() { return detail::features().avx; }
11599
inline bool has_avx2() { return detail::features().avx2; }
116100
inline bool has_avx512() { return detail::features().avx512f; }
117101

cpp/deglib/include/distance/fp16.h

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ namespace deglib::distances {
3333

3434
#if defined(DEGLIB_X86) && (defined(__GNUC__) || defined(__clang__))
3535

36-
DEGLIB_TARGET_F16C inline uint16_t float_to_fp16_gcc(float f) {
36+
DEGLIB_TARGET_AVX2 inline uint16_t float_to_fp16_gcc(float f) {
3737
return _cvtss_sh(f, 0);
3838
}
3939

40-
DEGLIB_TARGET_F16C inline float fp16_to_float_gcc(uint16_t h) {
40+
DEGLIB_TARGET_AVX2 inline float fp16_to_float_gcc(uint16_t h) {
4141
return _cvtsh_ss(h);
4242
}
4343

44-
DEGLIB_TARGET_F16C inline void floats_to_fp16_gcc(const float* floats, uint16_t* fp16_vals, size_t count) {
44+
DEGLIB_TARGET_AVX2 inline void floats_to_fp16_gcc(const float* floats, uint16_t* fp16_vals, size_t count) {
4545
size_t i = 0;
4646
// Process 8 floats per step with _mm256_cvtps_ph
4747
for (; i + 8 <= count; i += 8) {
@@ -61,7 +61,7 @@ namespace deglib::distances {
6161
}
6262
}
6363

64-
DEGLIB_TARGET_F16C inline void fp16_to_floats_gcc(const uint16_t* fp16_vals, float* floats, size_t count) {
64+
DEGLIB_TARGET_AVX2 inline void fp16_to_floats_gcc(const uint16_t* fp16_vals, float* floats, size_t count) {
6565
size_t i = 0;
6666
// Process 8 uint16_t per step with _mm256_cvtph_ps
6767
for (; i + 8 <= count; i += 8) {
@@ -243,15 +243,15 @@ namespace deglib::distances {
243243
// ---------------------------------------------------------------------------
244244
// Public API: float_to_fp16, fp16_to_float, floats_to_fp16, fp16_to_floats
245245
// ---------------------------------------------------------------------------
246-
// Runtime dispatch via deglib::cpu::has_f16c().
247-
// On GCC/Clang: uses DEGLIB_TARGET_F16C-attributed intrinsics.
246+
// Runtime dispatch via deglib::cpu::has_avx2().
247+
// On GCC/Clang: uses DEGLIB_TARGET_AVX2-attributed intrinsics.
248248
// On MSVC: uses _mm_cvtps_ph / _mm_cvtph_ps (SSE intrinsics).
249249
// Fallback: scalar IEEE 754 Round-to-Nearest-Even.
250250
// ---------------------------------------------------------------------------
251251

252252
inline uint16_t float_to_fp16(float f) {
253253
#if defined(DEGLIB_X86)
254-
if (deglib::cpu::has_f16c()) {
254+
if (deglib::cpu::has_avx2()) {
255255
#if defined(__GNUC__) || defined(__clang__)
256256
return float_to_fp16_gcc(f);
257257
#elif defined(_MSC_VER)
@@ -264,7 +264,7 @@ namespace deglib::distances {
264264

265265
inline float fp16_to_float(uint16_t h) {
266266
#if defined(DEGLIB_X86)
267-
if (deglib::cpu::has_f16c()) {
267+
if (deglib::cpu::has_avx2()) {
268268
#if defined(__GNUC__) || defined(__clang__)
269269
return fp16_to_float_gcc(h);
270270
#elif defined(_MSC_VER)
@@ -277,7 +277,7 @@ namespace deglib::distances {
277277

278278
inline void floats_to_fp16(const float* floats, uint16_t* fp16_vals, size_t count) {
279279
#if defined(DEGLIB_X86)
280-
if (deglib::cpu::has_f16c()) {
280+
if (deglib::cpu::has_avx2()) {
281281
#if defined(__GNUC__) || defined(__clang__)
282282
floats_to_fp16_gcc(floats, fp16_vals, count);
283283
return;
@@ -295,7 +295,7 @@ namespace deglib::distances {
295295

296296
inline void fp16_to_floats(const uint16_t* fp16_vals, float* floats, size_t count) {
297297
#if defined(DEGLIB_X86)
298-
if (deglib::cpu::has_f16c()) {
298+
if (deglib::cpu::has_avx2()) {
299299
#if defined(__GNUC__) || defined(__clang__)
300300
fp16_to_floats_gcc(fp16_vals, floats, count);
301301
return;
@@ -311,22 +311,6 @@ namespace deglib::distances {
311311
}
312312
}
313313

314-
// Naive scalar inner product for FP16 vectors (used for testing and fallback).
315-
// Computes the raw dot product (without 1.f -) using std::fma for precision.
316-
inline float fp16_ip_naive(const void* pVect1v, const void* pVect2v, const void* qty_ptr) {
317-
const uint16_t* a = static_cast<const uint16_t*>(pVect1v);
318-
const uint16_t* b = static_cast<const uint16_t*>(pVect2v);
319-
size_t size = *((size_t*)qty_ptr);
320-
321-
float result = 0.0f;
322-
for (size_t i = 0; i < size; ++i) {
323-
float fa = fp16_to_float(a[i]);
324-
float fb = fp16_to_float(b[i]);
325-
result = std::fma(fa, fb, result);
326-
}
327-
return result;
328-
}
329-
330314
} // namespace fp16
331315

332316
} // end namespace deglib::distances

cpp/deglib/include/distance/fp16_ip.h

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,17 @@ namespace deglib::distances::fp16_ip {
2222
}
2323

2424
inline static float dot(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
25-
return deglib::distances::fp16::fp16_ip_naive(pVect1v, pVect2v, qty_ptr);
25+
const uint16_t* a = static_cast<const uint16_t*>(pVect1v);
26+
const uint16_t* b = static_cast<const uint16_t*>(pVect2v);
27+
size_t size = *((size_t*)qty_ptr);
28+
29+
float result = 0.0f;
30+
for (size_t i = 0; i < size; ++i) {
31+
float fa = deglib::distances::fp16::fp16_to_float(a[i]);
32+
float fb = deglib::distances::fp16::fp16_to_float(b[i]);
33+
result = std::fma(fa, fb, result);
34+
}
35+
return result;
2636
}
2737
};
2838

@@ -121,33 +131,33 @@ namespace deglib::distances::fp16_ip {
121131

122132
// -------------------------------------------------------------------
123133
// InnerProductFP16_8Ext — processes 8 FP16 values (16 bytes) per iteration.
124-
// Uses SSE F16C (_mm_cvtph_ps) to convert 8 half-precision values to
125-
// 8 single-precision floats, then accumulates with _mm256_fmadd_ps
126-
// via two SSE loads combined into an AVX2 register.
134+
// Uses AVX2 F16C (_mm256_cvtph_ps) to convert 8 half-precision values
135+
// to 8 single-precision floats, then accumulates with _mm256_fmadd_ps.
127136
// -------------------------------------------------------------------
128137

129-
class InnerProductFP16_8Ext_SSE {
138+
class InnerProductFP16_8Ext_AVX2 {
130139
public:
131-
DEGLIB_TARGET_F16C inline static float compare(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
140+
DEGLIB_TARGET_AVX2 inline static float compare(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
132141
return 1.f - dot(pVect1v, pVect2v, qty_ptr);
133142
}
134143

135-
DEGLIB_TARGET_F16C inline static float dot(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
144+
DEGLIB_TARGET_AVX2 inline static float dot(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
136145
const uint16_t *a = static_cast<const uint16_t *>(pVect1v);
137146
const uint16_t *b = static_cast<const uint16_t *>(pVect2v);
138147
size_t size = *((size_t *) qty_ptr);
139148

140149
const uint16_t *last = a + size;
141150

142-
__m128 sum128 = _mm_setzero_ps();
151+
__m256 sum256 = _mm256_setzero_ps();
143152
while (a < last) {
144-
__m128 va = _mm_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i *>(a)));
145-
__m128 vb = _mm_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i *>(b)));
146-
sum128 = _mm_fmadd_ps(va, vb, sum128);
153+
__m256 va = _mm256_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i *>(a)));
154+
__m256 vb = _mm256_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i *>(b)));
155+
sum256 = _mm256_fmadd_ps(va, vb, sum256);
147156
a += 8;
148157
b += 8;
149158
}
150159

160+
__m128 sum128 = _mm_add_ps(_mm256_extractf128_ps(sum256, 0), _mm256_extractf128_ps(sum256, 1));
151161
alignas(32) float f[4];
152162
_mm_store_ps(f, sum128);
153163
return f[0] + f[1] + f[2] + f[3];
@@ -191,13 +201,13 @@ namespace deglib::distances::fp16_ip {
191201
}
192202
};
193203

194-
class InnerProductFP16_8ExtResiduals_SSE {
204+
class InnerProductFP16_8ExtResiduals_AVX2 {
195205
public:
196-
DEGLIB_TARGET_F16C inline static float compare(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
206+
DEGLIB_TARGET_AVX2 inline static float compare(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
197207
size_t qty = *((size_t *) qty_ptr);
198208

199209
size_t qty8 = qty >> 3 << 3;
200-
float res = InnerProductFP16_8Ext_SSE::dot(pVect1v, pVect2v, &qty8);
210+
float res = InnerProductFP16_8Ext_AVX2::dot(pVect1v, pVect2v, &qty8);
201211
const uint16_t *pVect1 = static_cast<const uint16_t *>(pVect1v) + qty8;
202212
const uint16_t *pVect2 = static_cast<const uint16_t *>(pVect2v) + qty8;
203213

@@ -213,10 +223,10 @@ namespace deglib::distances::fp16_ip {
213223
#if defined(DEGLIB_X86)
214224
, InnerProductFP16_32Ext_AVX512,
215225
InnerProductFP16_16Ext_AVX2,
216-
InnerProductFP16_8Ext_SSE,
226+
InnerProductFP16_8Ext_AVX2,
217227
InnerProductFP16_32ExtResiduals_AVX512,
218228
InnerProductFP16_16ExtResiduals_AVX2,
219-
InnerProductFP16_8ExtResiduals_SSE
229+
InnerProductFP16_8ExtResiduals_AVX2
220230
#endif
221231
>;
222232

@@ -228,27 +238,21 @@ namespace deglib::distances::fp16_ip {
228238
else if (dim % 16 == 0)
229239
return InnerProductFP16_16Ext_AVX2{};
230240
else if (dim % 8 == 0)
231-
return InnerProductFP16_8Ext_SSE{};
241+
return InnerProductFP16_8Ext_AVX2{};
232242
else if (dim > 32)
233243
return InnerProductFP16_32ExtResiduals_AVX512{};
234244
else if (dim > 8)
235-
return InnerProductFP16_8ExtResiduals_SSE{};
245+
return InnerProductFP16_8ExtResiduals_AVX2{};
236246
}
237247
else if (deglib::cpu::has_avx2()) {
238248
if (dim % 16 == 0)
239249
return InnerProductFP16_16Ext_AVX2{};
240250
else if (dim % 8 == 0)
241-
return InnerProductFP16_8Ext_SSE{};
251+
return InnerProductFP16_8Ext_AVX2{};
242252
else if (dim > 16)
243253
return InnerProductFP16_16ExtResiduals_AVX2{};
244254
else if (dim > 8)
245-
return InnerProductFP16_8ExtResiduals_SSE{};
246-
}
247-
else if (deglib::cpu::has_sse42()) {
248-
if (dim % 8 == 0)
249-
return InnerProductFP16_8Ext_SSE{};
250-
else if (dim > 8)
251-
return InnerProductFP16_8ExtResiduals_SSE{};
255+
return InnerProductFP16_8ExtResiduals_AVX2{};
252256
}
253257
#endif
254258
return InnerProductFP16{};

0 commit comments

Comments
 (0)