vak: (Default)
Serge Vakulenko ([personal profile] vak) wrote2024-01-16 11:04 pm

BFloat16 в эпловском процессоре M2

Оказывается, процессор Apple M2 поддерживает в хардвере тип данных BFloat16. Подробности есть в статье: "BFloat16 processing for Neural Networks on Armv8-A".

Проверить факт наличия BFloat16 на конкретном маке можно командой:
$ sysctl hw.optional.arm.FEAT_BF16
hw.optional.arm.FEAT_BF16: 1
К примеру, команда BFCVT превращает число float32 в bfloat16: BFCVT--Floating-point-convert-from-single-precision-to-BFloat16-format.

Алгоритм преобразования:
// FPConvertBF()
// =============
// Converts a single-precision OP to BFloat16 value with using rounding mode of
// Round to Nearest Even when executed from AArch64 state and
// FPCR.AH == '1', otherwise rounding is controlled by FPCR/FPSCR.

bits(16) FPConvertBF(bits(32) op, FPCRType fpcr, FPRounding rounding)

    bits(32) result;                                // BF16 value in top 16 bits
    boolean altfp = HaveAltFP() && !UsingAArch32() && fpcr.AH == '1';
    boolean fpexc = !altfp;                         // Generate no floating-point exceptions
    if altfp then fpcr.<FIZ,FZ> = '11';             // Flush denormal input and output to zero
    if altfp then rounding = FPRounding_TIEEVEN;    // Use RNE rounding mode

    // Unpack floating-point operand, with always flush-to-zero if fpcr.AH == '1'.
    (fptype,sign,value) = FPUnpack(op, fpcr, fpexc);

    if fptype == FPType_SNaN || fptype == FPType_QNaN then
        if fpcr.DN == '1' then
            result = FPDefaultNaN(fpcr);
        else
            result = FPConvertNaN(op);
        if fptype == FPType_SNaN then
            if fpexc then FPProcessException(FPExc_InvalidOp, fpcr);
    elsif fptype == FPType_Infinity then
        result = FPInfinity(sign);
    elsif fptype == FPType_Zero then
        result = FPZero(sign);
    else
        result = FPRoundCVBF(value, fpcr, rounding, fpexc);

    // Returns correctly rounded BF16 value from top 16 bits
    return result<31:16>;

// FPConvertBF()
// =============
// Converts a single-precision operand to BFloat16 value.

bits(16) FPConvertBF(bits(32) op, FPCRType fpcr)
    return FPConvertBF(op, fpcr, FPRoundingMode(fpcr));
В компиляторе Си имеются соответствующие встроенные тип и функции. Вот пример.
#include <stdio.h>
int main()
{
    float f = 123.456;
    __bf16 b;

    b = __builtin_aarch64_bfcvtbf(f);
    printf("From float32 to bfloat16: %.3f -> %#04hx\n", f, b);

    f = __builtin_aarch64_bfcvtsf(b);
    printf("From bfloat16 to float32: %#04hx -> %.3f\n", b, f);
}
Компилируем, запускаем:
$ gcc-13 -O -march=armv8.6-a+bf16 bfdemo.c -o bfdemo
$ ./bfdemo
From float32 to bfloat16: 123.456 -> 0x42f7
From bfloat16 to float32: 0x42f7 -> 123.500