"tasks/msdp/metrics.py" did not exist on "91a80bd10dbf79e81664c8901fcdd58cc70b4e08"
f8_utils.hpp 7.67 KB
Newer Older
1
2
3
4
5
6
7
// SPDX-License-Identifier: MIT
// Copyright (c) 2018-2023, Advanced Micro Devices, Inc. All rights reserved.

#pragma once

namespace ck {

Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
8
9
using f8_t   = uint8_t;
using half_t = _Float16;
10
11

// fp8 rounding modes
12
13
// use standard for rounding to nearest, the faster one
// use stochastic for stochastic rounding, helps to avoid error accumulation
14
15
16
17
18
19
enum class f8_rounding_mode
{
    standard,
    stochastic
};

20
21
22
23
24
25
} // namespace ck

namespace ck::utils {

namespace {

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
26
27
template <typename T, bool negative_zero_nan, bool clip, bool stoch>
__host__ __device__ f8_t run_cast_to_f8(T x, uint32_t rng)
28
{
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
29
30
31
    // check data type
    constexpr bool is_half  = std::is_same<T, half_t>::value;
    constexpr bool is_float = std::is_same<T, float>::value;
32

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
33
    // fp8 exponent/mantissa layout
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
34
    constexpr int f8_exp  = 4;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
35
    constexpr int f8_mant = 3;
36

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
37
    // resulting type exponent/mantissa layout
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
38
    constexpr int type_exp  = is_half ? 5 : 8;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
39
    constexpr int type_mant = is_half ? 10 : 23;
40
41

    int exponent;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
42
43
    uint32_t head, mantissa, sign;
    // nan code is same for float and half
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
44
    constexpr uint8_t nan_code  = 0x80;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
45
    constexpr uint32_t nan_mask = is_half ? 0x7C00 : 0x7F800000;
46

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
47
    // convert to bitwise
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
48
49
    typedef typename std::conditional<std::is_same<T, half_t>::value, uint16_t, uint32_t>::type
        T_bitwise;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
50
    T_bitwise x_bitwise = *(reinterpret_cast<T_bitwise*>(&x));
51

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    // unpack the input, depends on datatype
    if constexpr(is_float)
    {
        head     = x_bitwise & 0xFF800000;
        mantissa = x_bitwise & 0x7FFFFF;
        exponent = (head >> type_mant) & 0xFF;
        sign     = head >> (type_exp + type_mant);
    }
    else if constexpr(is_half)
    {
        head     = x_bitwise & 0xFC00;
        mantissa = x_bitwise & 0x3FF;
        exponent = (head >> type_mant) & 0x1F;
        sign     = head >> (type_exp + type_mant);
    }

Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
68
69
70
71
72
    uint32_t signed_inf   = (sign << (type_exp + type_mant)) + (((1 << type_exp) - 1) << type_mant);
    uint32_t drop_mask    = (1 << (type_mant - f8_mant)) - 1;
    constexpr int max_exp = (1 << f8_exp) - (negative_zero_nan ? 1 : 2);
    constexpr int exp_low_cutoff =
        (1 << (type_exp - 1)) - (1 << (f8_exp - 1)) + 1 - (negative_zero_nan ? 1 : 0);
73

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
74
    if constexpr(negative_zero_nan)
75
    {
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
76
77
        if((x_bitwise & nan_mask) == nan_mask)
            return nan_code;
78
79
80
    }
    else
    {
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
81
        if((x_bitwise & nan_mask) == nan_mask)
82
83
84
            return signed_inf + (mantissa != 0 ? 1 : 0);
    }

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
85
    // check if x is 0.0
86
87
88
89
90
    if(x_bitwise == 0)
        return 0;

    exponent -= exp_low_cutoff - 1;
    if(exponent <= 0)
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
91
92
93
        drop_mask = (1 << (type_mant - f8_mant + 1 - exponent)) - 1;
    mantissa += 1 << type_mant;
    // apply random number if needed
94
    mantissa += (stoch ? rng : mantissa) & drop_mask;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
95
    if(mantissa >= (2 << type_mant))
96
97
98
99
    {
        mantissa >>= 1;
        exponent++;
    }
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
100
    mantissa >>= (type_mant - f8_mant);
101

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
102
    // check negative exponent
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
    if(exponent <= 0)
    {
        if(x_bitwise == 0)
            return 0;
        else
        {
            // subnormal range; represented by a subnormal float8 (exponent 0)
            // and involves loss of accuracy
            mantissa >>= 1 - exponent;
            exponent = 0;
        }
    }
    // above range: quantize to maximum possible float of the same sign
    else if(exponent > max_exp)
    {
        if(clip)
        {
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
120
            mantissa = (1 << f8_mant) - 1;
121
122
123
124
125
126
127
            exponent = max_exp;
        }
        else
        {
            return signed_inf;
        }
    }
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
128
129

    // check if x is 0.0 or -0.0
130
    if(exponent == 0 && mantissa == 0)
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
131
132
133
134
135
136
137
        return negative_zero_nan ? 0 : (sign << (f8_exp + f8_mant));
    mantissa &= (1 << f8_mant) - 1;
    return (sign << (f8_exp + f8_mant)) | (exponent << f8_mant) | mantissa;
}

template <typename T, bool negative_zero_nan>
__host__ __device__ T run_cast_from_f8(f8_t x)
138
{
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
139
140
141
142
    // check data type
    constexpr bool is_half  = std::is_same<T, half_t>::value;
    constexpr bool is_float = std::is_same<T, float>::value;

143
    // fp8 exponent/mantissa layout
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
144
    constexpr int f8_exp  = 4;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
145
    constexpr int f8_mant = 3;
146

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
147
    // resulting type exponent/mantissa layout
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
148
    constexpr int type_exp  = is_half ? 5 : 8;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
149
150
151
152
153
154
155
156
157
158
159
    constexpr int type_mant = is_half ? 10 : 23;

    // prepare the codes
    constexpr uint8_t nan_code = 0x80;
    T fInf, fNegInf, fNaN, fNeg0;
    if constexpr(is_half)
    {
        constexpr uint16_t ihInf    = 0x7C00;
        constexpr uint16_t ihNegInf = 0xFC00;
        constexpr uint16_t ihNaN    = 0x7C01;
        constexpr uint16_t ihNeg0   = 0x8000;
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
160
161
162
163
        fInf                        = *(reinterpret_cast<const half_t*>(&ihInf));
        fNegInf                     = *(reinterpret_cast<const half_t*>(&ihNegInf));
        fNaN                        = *(reinterpret_cast<const half_t*>(&ihNaN));
        fNeg0                       = *(reinterpret_cast<const half_t*>(&ihNeg0));
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
164
165
166
167
168
169
170
    }
    else if constexpr(is_float)
    {
        constexpr uint32_t ifInf    = 0x7F800000;
        constexpr uint32_t ifNegInf = 0xFF800000;
        constexpr uint32_t ifNaN    = 0x7F800001;
        constexpr uint32_t ifNeg0   = 0x80000000;
Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
171
172
173
174
        fInf                        = *(reinterpret_cast<const float*>(&ifInf));
        fNegInf                     = *(reinterpret_cast<const float*>(&ifNegInf));
        fNaN                        = *(reinterpret_cast<const float*>(&ifNaN));
        fNeg0                       = *(reinterpret_cast<const float*>(&ifNeg0));
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
175
    }
176
177

    // unpack the input
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
178
179
180
    uint32_t sign     = x >> (f8_exp + f8_mant);
    uint32_t mantissa = x & ((1 << f8_mant) - 1);
    int exponent      = (x & 0x7F) >> f8_mant;
181

Rostyslav Geyyer's avatar
Format  
Rostyslav Geyyer committed
182
183
    constexpr int exp_low_cutoff =
        (1 << (type_exp - 1)) - (1 << (f8_exp - 1)) + 1 - (negative_zero_nan ? 1 : 0);
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
184
    typename std::conditional<std::is_same<T, half_t>::value, uint16_t, uint32_t>::type retval;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
185
186

    if constexpr(negative_zero_nan)
187
    {
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
188
        if(x == nan_code)
189
190
191
192
            return fNaN;
    }
    else
    {
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
193
        if(x == nan_code)
194
            return fNeg0;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
195
        if(exponent == ((1 << f8_exp) - 1))
196
197
198
199
200
201
202
            return (mantissa == 0) ? (sign ? fNegInf : fInf) : fNaN;
    }

    // subnormal input
    if(exponent == 0)
    {
        // guaranteed mantissa!=0 since cases 0x0 and 0x80 are handled above
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
203
        int sh = 1 + __builtin_clz(mantissa) - ((1 + type_exp + type_mant) - f8_mant);
204
        mantissa <<= sh;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
205
        mantissa &= ((1 << f8_mant) - 1);
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
206
        exponent += 1 - sh;
207
208
    }
    exponent += exp_low_cutoff - 1;
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
209
    mantissa <<= type_mant - f8_mant;
210
211
212
213

    // subnormal output (occurs when T=half, we=5, negative_zero_nan=true)
    if(exponent <= 0)
    {
Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
214
        mantissa |= 1 << type_mant;
215
216
217
218
        mantissa >>= 1 - exponent;
        exponent = 0;
    }

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
219
220
221
222
    retval = (sign << (type_exp + type_mant)) | (exponent << type_mant) | mantissa;
    return *(reinterpret_cast<const T*>(&retval));
}

223
224
225
226
227
228
229
230
231
232
233
234
235
} // namespace

template <typename T, bool negative_zero_nan, bool clip, bool stoch>
__host__ __device__ f8_t cast_to_f8(T x, uint32_t rng)
{
    // check datatype
    constexpr bool is_half  = std::is_same<T, half_t>::value;
    constexpr bool is_float = std::is_same<T, float>::value;
    static_assert(is_half || is_float, "Only half and float can be casted to f8.");

    return run_cast_to_f8<T, negative_zero_nan, clip, stoch>(x, rng);
}

Rostyslav Geyyer's avatar
Rostyslav Geyyer committed
236
237
238
239
240
241
242
243
244
245
246
247
248
template <typename T, bool negative_zero_nan>
__host__ __device__ T cast_from_f8(f8_t x)
{
    // check datatype
    constexpr bool is_half  = std::is_same<T, half_t>::value;
    constexpr bool is_float = std::is_same<T, float>::value;
    static_assert(is_half || is_float, "only half and float are supported.");

    // check if x is 0.0
    if(x == 0)
        return static_cast<T>(0);

    return run_cast_from_f8<T, negative_zero_nan>(x);
249
250
}

251
} // namespace ck::utils