Vectorize ggml_fp16_to_fp32_row / ggml_fp32_to_fp16_row with F16C hardware conversion - #19
Open
codspeed-hq[bot] wants to merge 2 commits into
Conversation
…sion
Add F16C-accelerated fast paths to ggml_fp16_to_fp32_row() and
ggml_fp32_to_fp16_row() in the base library. These functions live in the
portable base library, which is not compiled with -mf16c, so dedicated F16C
helpers are compiled via a GCC/Clang function target attribute
(__attribute__((target("f16c,avx")))) and dispatched at runtime through
__builtin_cpu_supports("f16c"). CPUs and compilers without F16C support fall
back to the original scalar loop, and a scalar tail handles counts that are
not a multiple of 8.
vcvtph2ps is an exact fp16->fp32 widening and vcvtps2ph with
round-to-nearest-even matches the scalar GGML_FP32_TO_FP16 rounding, so the
results are bit-exact with the scalar path (verified over the full fp16 range
and a range of sizes).
fp32_to_fp16_row: 399.4us -> 99.1us (~4x)
fp16_to_fp32_row: 262.9us -> 97.9us (~2.7x)
Author
Merging this PR will improve performance by ×3.2
Performance Changes
Tip Curious why this is faster? Comment Comparing |
On Windows, ggml-base.dll is linked with clang/lld-link using -nostdlib, and __builtin_cpu_supports() references the __cpu_features2 compiler-runtime symbol which is unavailable in that link, producing an undefined-symbol link error. Exclude _WIN32 from the F16C runtime-dispatch guard so Windows keeps the portable scalar path; Linux/macOS x86-64 still use the accelerated path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds F16C-accelerated fast paths to the two FP16↔FP32 row-conversion functions in the ggml base library (
ggml/src/ggml.c), converting 8 elements per instruction (vcvtph2ps/vcvtps2ph) instead of the scalar software bit-manipulation fallback.Analysis
Flamegraphs of the baseline showed both row-conversion benchmarks were spending ~100% of their time in the scalar software converters from
ggml-impl.h:fp16_to_fp32_row:ggml_compute_fp16_to_fp32+fp32_to_bitsbit-manipulation.fp32_to_fp16_row: the equivalent scalarGGML_FP32_TO_FP16path.These functions live in the portable base library, which is intentionally compiled without
-mf16c(only theggml-cpubackend gets the AVX2/F16C arch flags), so they could not simply use__F16C__-guarded intrinsics.Change
Compiles two dedicated F16C helpers with a GCC/Clang function target attribute (
__attribute__((target("f16c,avx")))) so the intrinsics are available even though the translation unit is built with a conservative baseline, then dispatches to them at runtime via__builtin_cpu_supports("f16c"). On any CPU or compiler without F16C support the code falls back to the original scalar loop, so the base library stays fully portable. A scalar tail handles counts that are not a multiple of 8.Correctness
vcvtph2psis an exact IEEE-754 fp16→fp32 widening, andvcvtps2phwith round-to-nearest-even matches the softwareggml_compute_fp32_to_fp16rounding (the same instruction/mode the CPU backend already uses).Verified with a standalone A/B harness linked against the compiled base library (confirmed built without
-mf16c, so the runtime-dispatch path is genuinely exercised): compared the optimized functions against independent scalar references over sizes 1, 7, 8, 9, 15, 16, 31, 32, 33, 63, 64, 100, 4096 and the full 32768 fp16 range — bit-exact, 0 mismatches in both directions.Performance (CodSpeed, CPU simulation)
Measured locally via the CodSpeed CLI, comparing the unmodified base-library build against the optimized build:
fp32_to_fp16_rowfp16_to_fp32_rowNo regressions in the other 13 benchmarks — this path is untouched by the graph-compute and quantization kernels.