From 3576f3f6de073c4eb5c64ab09c56b7d312c41ad5 Mon Sep 17 00:00:00 2001 From: "KOKO\\Mimi" Date: Wed, 14 Oct 2026 18:58:00 +0900 Subject: [PATCH] perf(math): scan least significand bit with MSVC x64 intrinsic --- docs/calculation-performance/mkl-coverage.md | 25 ++++++++++++++++++++ src/fesa/math/high_low_arithmetic.h | 12 +++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/calculation-performance/mkl-coverage.md b/docs/calculation-performance/mkl-coverage.md index 57e92ae..e5ac649 100644 --- a/docs/calculation-performance/mkl-coverage.md +++ b/docs/calculation-performance/mkl-coverage.md @@ -163,6 +163,31 @@ trap 관측까지 동등하다고 주장하지 않는다. 원래 분류의 새 [검사 로그](../../.harness/performance/shared-classification/after-checks.txt)를 보존한다. 이 결과는 최종 모델의 시간 목표나 전체 회귀 검사를 대체하지 않는다. +후속 동적 Hex8의 30초 SW raw 자료에서는 `LowestBitExponent`가 51.037592 +sampled CPU초로 나타났다. 150초 제한의 개발 실행은 완료되지 않았으며 이 값은 +전체 경과 시간이나 개선 배수가 아니다. 공통 산술 담당자는 MSVC 호환 x64 macro +조건에서만 6단계 trailing-bit 검색을 `_BitScanForward64`로 교체했다. +[Microsoft 계약](https://learn.microsoft.com/en-us/cpp/intrinsics/bitscanforward-bitscanforward64?view=msvc-170)의 +least-set-bit index를 사용하며 정상 수의 implicit bit와 비영 subnormal fraction이 +mask가 0이 아님을 보장한다. Exponent 구성, -1074 exact-tail 거부, FMA와 +모든 pair 산술식은 그대로다. Macro 조건 밖에서는 기존 portable 검색을 유지한다. +MSVC 호환 macro를 정의하는 다른 compiler의 실행까지 검증했다고 주장하지 않는다. + +[독립 source 수치 검토](../../.harness/performance/shared-bitscan/source-numerical-review.md)는 +이 검색값과 downstream gate 보존을 확인했다. 정확한 include dependency를 가진 +MSVC Debug에서 unit/integration/CLI consumer를 36개 edge로 재빌드했고, +기존 frozen exponent·pair bit oracle와 통합 Hex8·paired state/kinematics·직렬/TBB +검사 **68/68이 native 0**으로 통과했다. +[실행 기록](../../.harness/performance/shared-bitscan/after-native.json)과 +[검사 로그](../../.harness/performance/shared-bitscan/after-checks.txt)를 보존한다. +새 검색의 전체 동적 시간, 최종 reference 및 전체 Debug 판정은 아직 미실행이다. + +통합한 Hex8의 ordinary MKL 연산, 원래 누적·진단 순서, private pair의 영항 생략과 +선택적 선형 tangent의 지연 준비는 [전용 기록](hex8-performance.md)에 있다. +선택적 준비는 Float64 Create의 필수 검사를 바꾸지 않으며 검증된 현재 trial의 +요청에서만 초기화한다. 해당 기록의 1.7391958초는 과거 a08 source의 개발 단일 +실행이고 최신 통합 source의 공식 시간 합격으로 소급하지 않는다. + 위 검증 중 기존 MSVC/Ninja의 include prefix 인식 오류도 확인했다. 이 PC에는 한국어 compiler resource만 있어 `VSLANG=1033`만 지정한 fresh build도 dependency가 0개였다. 실제 출력 접두사 `참고: 포함 파일: `를 별도 로컬 CMake include로 지정한 diff --git a/src/fesa/math/high_low_arithmetic.h b/src/fesa/math/high_low_arithmetic.h index b64c9de..9c2b21d 100644 --- a/src/fesa/math/high_low_arithmetic.h +++ b/src/fesa/math/high_low_arithmetic.h @@ -7,6 +7,9 @@ #include #include #include +#if defined(_MSC_VER) && defined(_M_X64) +#include +#endif #if defined(_M_X64) || defined(__SSE2__) #include #endif @@ -114,8 +117,14 @@ inline int LowestBitExponent(double value) { ? -1074 : static_cast(encoded_exponent) - 1075; if (encoded_exponent != 0U) significand |= 1ULL << 52U; +#if defined(_MSC_VER) && defined(_M_X64) + // The finite, nonzero precondition makes the significand nonzero. + unsigned long trailing = 0; + _BitScanForward64(&trailing, significand); + return exponent + static_cast(trailing); +#else int trailing = 0; - // Strip large zero groups first without vendor/compiler intrinsics. + // Preserve the portable search outside the supported MSVC x64 path. for (const int shift : {32, 16, 8, 4, 2, 1}) { const auto mask = (1ULL << shift) - 1U; if ((significand & mask) == 0U) { @@ -124,6 +133,7 @@ inline int LowestBitExponent(double value) { } } return exponent + trailing; +#endif } /// @brief Gives a product and its FMA residual when the dyadic tail fits. /// @note Rejects underflow-risk exact tails, even if the rounded high is finite.