diff options
| author | aamir <[email protected]> | 2018-07-21 19:30:40 -0700 |
|---|---|---|
| committer | aamir <[email protected]> | 2018-07-21 19:30:40 -0700 |
| commit | fcf40649feb6046fb9b1ed984fb9b19422cd5463 (patch) | |
| tree | 92b28621af353598ad3a49df70ebb596d1205609 /cutlass-example/half.h | |
| parent | b3ad8abea43b7d1e8887f57d6e30c5a40cf752a6 (diff) | |
adding the cutlass examples
Diffstat (limited to 'cutlass-example/half.h')
| -rw-r--r-- | cutlass-example/half.h | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/cutlass-example/half.h b/cutlass-example/half.h new file mode 100644 index 0000000..ee536e5 --- /dev/null +++ b/cutlass-example/half.h @@ -0,0 +1,743 @@ +/*************************************************************************************************** + * Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************************************/ +/*! + \file + \brief Host-side implementation of half-precision float +*/ + +#pragma once + +#include <stdint.h> +#include <cmath> +#include <limits> +#include <utility> +#include <utility> + +#include <iomanip> +#include <istream> +#include <ostream> + +#include <cuda_fp16.h> + +namespace cutlass { + +/// IEEE binary16 floating-point value +class half_t { + public: + half_t(); + half_t(int); /// conversion from integer + half_t(float); /// conversion from fp32 + half_t(double); /// conversion from fp64 + + static half_t bitcast(unsigned short); /// bitcast performs no conversion + + static half_t convert(float const&); /// FP conversion - round toward nearest even + static float convert(unsigned short const&); /// floating point conversion to fp32 + + static half_t zero() { return bitcast(0); } /// +zero + static half_t one() { return bitcast(0x3c00); } /// one + static half_t nan() { return bitcast(0x7fff); } /// canonical not a number + static half_t inf() { return bitcast(0x7c00); } /// +infinity + static half_t ninf() { return bitcast(0xfc00); } /// -infinity + static half_t epsilon() { return bitcast(0x1000); } /// Machine epsilon + + bool signbit() const; /// sign bit - true: negative, false: positive + int exponent() const; /// unbiased exponent + unsigned short mantissa() const; /// mantissa bits + + bool isfinite() const; /// true if neither inf nor nan + bool isinf() const; /// true if value is + or - infinity + bool isnan() const; /// true if value is not a number + bool isnormal() const; /// true if nonzero value is normalized + bool iszero() const; /// true if value is + or - zero + + bool operator==(half_t const&) const; + bool operator!=(half_t const&) const; + bool operator==(float const&) const; + bool operator!=(float const&) const; + + bool operator<(half_t const&) const; + bool operator<=(half_t const&) const; + bool operator>(half_t const&) const; + bool operator>=(half_t const&) const; + + half_t operator+(half_t const&) const; + half_t operator-() const; + half_t operator-(half_t const&) const; + half_t operator*(half_t const&)const; + half_t operator/(half_t const&) const; + + half_t& operator+=(half_t const&); + half_t& operator-=(half_t const&); + half_t& operator*=(half_t const&); + half_t& operator/=(half_t const&); + + half_t& operator++(); + half_t& operator--(); + half_t operator++(int); + half_t operator--(int); + + operator bool() const; /// false if zero + operator int() const; /// conversion to int + operator float() const; /// conversion to fp32 + operator half() const; /// conversion to half + + uint16_t& raw() { return x; } + uint16_t raw() const { return x; } + + public: + /// data + unsigned short x; +}; + +/// Packed pair of half-precision elements +class half2_t { + public: + half2_t(); + half2_t(half_t lo, half_t hi); + half2_t(std::pair<float, float> const&); + explicit half2_t(unsigned data); + + half2_t operator+(half2_t const&) const; + half2_t operator-(half2_t const&) const; + half2_t operator*(half2_t const&)const; + half2_t operator/(half2_t const&) const; + + half2_t& operator+=(half2_t const&); + half2_t& operator-=(half2_t const&); + half2_t& operator*=(half2_t const&); + half2_t& operator/=(half2_t const&); + + float dot(half2_t const&) const; /// dot product with single-precision accumulation + float dot(half2_t const&, float) const; /// dot product with single-precision accumulation + + half_t doth(half2_t const&) const; /// dot product with half_t-precision accumulation + half_t doth(half2_t const&, half_t) const; /// dot product with half_t-precision accumulation + + unsigned packed() const; + + operator std::pair<float, float>() const; + operator unsigned() const; + + public: + half_t lo; + half_t hi; +}; + +template <typename Dest, typename Src> +Dest bitcast(Src const&); +template <> +float bitcast<float, unsigned>(unsigned const&); +template <> +float bitcast<float, int>(int const&); +template <> +unsigned bitcast<unsigned, float>(float const&); +template <> +half_t bitcast<half_t, unsigned short>(unsigned short const&); +template <> +unsigned short bitcast<unsigned short, half_t>(half_t const&); +template <> +half bitcast<half, unsigned short>(unsigned short const&); +} // namespace cutlass + +cutlass::half_t operator+(float, cutlass::half_t const&); +cutlass::half_t operator-(float, cutlass::half_t const&); +cutlass::half_t operator*(float, cutlass::half_t const&); +cutlass::half_t operator/(float, cutlass::half_t const&); + +std::ostream& operator<<(std::ostream&, cutlass::half_t const&); /// writes a half_t +std::istream& operator>>(std::istream&, cutlass::half_t&); /// reads a half_t + +#ifdef BOOST_LEXICAL_CAST_INCLUDED +namespace boost { + +/// lexical cast from string to half_t +template <> +cutlass::half_t lexical_cast<cutlass::half_t>(std::string const& arg); + +/// lexical cast from half_t to string +template <> +std::string lexical_cast<std::string>(cutlass::half_t const& arg); +} // namespace boost +#endif + +#define HLF_MANT_DIG 10 + +namespace std { + +cutlass::half_t abs(cutlass::half_t const&); /// absolute value + +bool isnan(cutlass::half_t const&); /// true if argument is NaN + +bool isfinite(cutlass::half_t const&); /// true if argument is neither NaN nor infinity + +cutlass::half_t nanh(const char* = 0); /// returns a not-a-number + +bool isinf(cutlass::half_t const&); /// returns true if argument is infinitey (+ or -) + +bool isnormal( + cutlass::half_t const&); /// returns true if argument is normal (neither zero nor infinity) + +int fpclassify(cutlass::half_t const&); /// returns a flag classifying floating-point value + +bool signbit(cutlass::half_t const&); /// returns true if negative, false if positive + +cutlass::half_t sqrt(cutlass::half_t const&); /// square root of half_t + +/// Numeric limits +template <> +struct numeric_limits<cutlass::half_t> { + static bool const is_specialized = true; + static bool const is_signed = true; + static bool const is_integer = false; + static bool const is_exact = false; + static bool const has_infinity = true; + static bool const has_quiet_NaN = true; + static bool const has_signaling_NaN = false; + static std::float_denorm_style const has_denorm = std::denorm_present; + static bool const has_denorm_loss = true; + static std::float_round_style const round_style = std::round_to_nearest; + static bool const is_iec559 = false; + static bool const is_bounded = true; + static bool const is_modulo = false; + static int const digits = HLF_MANT_DIG; + + static cutlass::half_t min() { return cutlass::half_t::bitcast(0x0001); } + + static cutlass::half_t lowest() { return cutlass::half_t::bitcast(0xfbff); } + + static cutlass::half_t max() { return cutlass::half_t::bitcast(0x7bff); } + + /// Returns smallest finite value + static cutlass::half_t epsilon() { return cutlass::half_t::epsilon(); } + + /// Returns smallest finite value + static cutlass::half_t round_error() { return cutlass::half_t(0.5f); } + + /// Returns smallest finite value + static cutlass::half_t infinity() { return cutlass::half_t::inf(); } + + /// Returns smallest finite value + static cutlass::half_t quiet_NaN() { return cutlass::half_t::nan(); } + + /// Returns smallest finite value + static cutlass::half_t signaling_NaN() { return cutlass::half_t::nan(); } + + /// Returns smallest finite value + static cutlass::half_t denorm_min() { return cutlass::half_t::bitcast(0x0001); } +}; +} // namespace std + +// +// +// + +inline cutlass::half_t cutlass::half_t::bitcast(unsigned short _x) { + half_t h; + h.x = _x; + return h; +} + +/// FP32 -> FP16 conversion - rounds to nearest even +inline cutlass::half_t cutlass::half_t::convert(float const& flt) { + // software implementation rounds toward nearest even + unsigned const& s = *reinterpret_cast<unsigned const*>(&flt); + uint16_t sign = uint16_t((s >> 16) & 0x8000); + int16_t exp = uint16_t(((s >> 23) & 0xff) - 127); + int mantissa = s & 0x7fffff; + uint16_t u = 0; + + if ((s & 0x7fffffff) == 0) { + // sign-preserving zero + return cutlass::half_t::bitcast(sign); + } + + if (exp > 15) { + if (exp == 128 && mantissa) { + // not a number + u = 0x7fff; + } else { + // overflow to infinity + u = sign | 0x7c00; + } + return cutlass::half_t::bitcast(u); + } + + int sticky_bit = 0; + + if (exp >= -14) { + // normal fp32 to normal fp16 + exp = uint16_t(exp + uint16_t(15)); + u = uint16_t(((exp & 0x1f) << 10)); + u = uint16_t(u | (mantissa >> 13)); + } else { + // normal single-precision to subnormal half_t-precision representation + int rshift = (-14 - exp); + if (rshift < 32) { + mantissa |= (1 << 23); + + sticky_bit = ((mantissa & ((1 << rshift) - 1)) != 0); + + mantissa = (mantissa >> rshift); + u = (uint16_t(mantissa >> 13) & 0x3ff); + } else { + mantissa = 0; + u = 0; + } + } + + // round to nearest even + int round_bit = ((mantissa >> 12) & 1); + sticky_bit |= ((mantissa & ((1 << 12) - 1)) != 0); + + if ((round_bit && sticky_bit) || (round_bit && (u & 1))) { + u = uint16_t(u + 1); + } + + u |= sign; + + return cutlass::half_t::bitcast(u); +} + +inline float cutlass::half_t::convert(unsigned short const& h) { + int sign = ((h >> 15) & 1); + int exp = ((h >> 10) & 0x1f); + int mantissa = (h & 0x3ff); + unsigned f = 0; + + if (exp > 0 && exp < 31) { + // normal + exp += 112; + f = (sign << 31) | (exp << 23) | (mantissa << 13); + } else if (exp == 0) { + if (mantissa) { + // subnormal + exp += 113; + while ((mantissa & (1 << 10)) == 0) { + mantissa <<= 1; + exp--; + } + mantissa &= 0x3ff; + f = (sign << 31) | (exp << 23) | (mantissa << 13); + } else { + // sign-preserving zero + f = (sign << 31); + } + } else if (exp == 31) { + if (mantissa) { + f = 0x7fffffff; // not a number + } else { + f = (0xff << 23) | (sign << 31); // inf + } + } + return *reinterpret_cast<float const*>(&f); +} + +inline cutlass::half_t::half_t() {} + +inline cutlass::half_t::half_t(int i) { x = convert(float(i)).x; } + +inline cutlass::half_t::half_t(float f) { x = convert(f).x; } + +inline cutlass::half_t::half_t(double d) { x = convert(float(d)).x; } + +inline bool cutlass::half_t::signbit() const { return (x >> 15) & 1; } + +inline int cutlass::half_t::exponent() const { return ((x >> 10) & 0x1f) - 15; } + +inline unsigned short cutlass::half_t::mantissa() const { return x & 0x3ff; } + +inline cutlass::half_t::operator bool() const { return (x & 0x7fff) != 0; } + +inline cutlass::half_t::operator int() const { return static_cast<int>(convert(x)); } + +inline cutlass::half_t::operator float() const { return convert(x); } + +inline cutlass::half_t::operator half() const { return cutlass::bitcast<half, unsigned short>(x); } + +inline bool cutlass::half_t::operator==(cutlass::half_t const& h) const { + if (iszero() && h.iszero()) { + return true; + } + return x == h.x; +} + +inline bool cutlass::half_t::operator!=(cutlass::half_t const& h) const { + if (iszero() && h.iszero()) { + return false; + } + return x != h.x; +} + +inline bool cutlass::half_t::operator==(float const& b) const { return x == half_t(b).x; } + +inline bool cutlass::half_t::operator!=(float const& b) const { return x != half_t(b).x; } + +inline bool cutlass::half_t::iszero() const { return (x & 0x7fff) == 0; } + +inline bool cutlass::half_t::isfinite() const { return (exponent() < 16); } + +inline bool cutlass::half_t::isnan() const { + int exp = ((x >> 10) & 0x1f); + if (exp == 0x1f) { + return (x & 0x3ff) != 0; + } + return false; +} + +inline bool cutlass::half_t::isinf() const { + int exp = ((x >> 10) & 0x1f); + if (exp == 0x1f) { + return (x & 0x3ff) == 0; + } + return false; +} + +inline bool cutlass::half_t::isnormal() const { + int exp = exponent(); + return exp > -15 && exp < 16; +} + +inline bool cutlass::half_t::operator<(half_t const& h) const { + int sign = ((x >> 15) & 1); + int h_sign = ((h.x >> 15) & 1); + if (sign == h_sign) { + return (x & 0x7fff) < (h.x & 0x7fff); + } else if (sign) { + return true; + } + return false; +} + +inline bool cutlass::half_t::operator<=(half_t const& h) const { + int sign = ((x >> 15) & 1); + int h_sign = ((h.x >> 15) & 1); + if (sign == h_sign) { + return (x & 0x7fff) <= (h.x & 0x7fff); + } else if (sign) { + return true; + } + return false; +} + +inline bool cutlass::half_t::operator>(half_t const& h) const { + int sign = ((x >> 15) & 1); + int h_sign = ((h.x >> 15) & 1); + if (sign == h_sign) { + return (x & 0x7fff) > (h.x & 0x7fff); + } else if (h_sign) { + return true; + } + return false; +} + +inline bool cutlass::half_t::operator>=(half_t const& h) const { + int sign = ((x >> 15) & 1); + int h_sign = ((h.x >> 15) & 1); + if (sign == h_sign) { + return (x & 0x7fff) >= (h.x & 0x7fff); + } else if (h_sign) { + return true; + } + return false; +} + +inline cutlass::half_t cutlass::half_t::operator+(cutlass::half_t const& b) const { + return cutlass::half_t(float(*this) + float(b)); +} + +inline cutlass::half_t cutlass::half_t::operator-() const { return bitcast(x ^ 0x8000); } + +inline cutlass::half_t cutlass::half_t::operator-(cutlass::half_t const& b) const { + return cutlass::half_t(float(*this) - float(b)); +} + +inline cutlass::half_t cutlass::half_t::operator*(cutlass::half_t const& b) const { + return cutlass::half_t(float(*this) * float(b)); +} + +inline cutlass::half_t cutlass::half_t::operator/(cutlass::half_t const& b) const { + return cutlass::half_t(float(*this) / float(b)); +} + +inline cutlass::half_t& cutlass::half_t::operator+=(cutlass::half_t const& b) { + *this = cutlass::half_t(float(*this) + float(b)); + return *this; +} + +inline cutlass::half_t& cutlass::half_t::operator-=(cutlass::half_t const& b) { + *this = cutlass::half_t(float(*this) - float(b)); + return *this; +} + +inline cutlass::half_t& cutlass::half_t::operator*=(cutlass::half_t const& b) { + *this = cutlass::half_t(float(*this) * float(b)); + return *this; +} + +inline cutlass::half_t& cutlass::half_t::operator/=(cutlass::half_t const& b) { + *this = cutlass::half_t(float(*this) / float(b)); + return *this; +} + +inline cutlass::half_t& cutlass::half_t::operator++() { + *this = cutlass::half_t(float(*this) + 1.0f); + return *this; +} + +inline cutlass::half_t& cutlass::half_t::operator--() { + *this = cutlass::half_t(float(*this) - 1.0f); + return *this; +} + +inline cutlass::half_t cutlass::half_t::operator++(int) { + half_t h = *this; + *this = cutlass::half_t(float(*this) + 1.0f); + return h; +} + +inline cutlass::half_t cutlass::half_t::operator--(int) { + half_t h = *this; + *this = cutlass::half_t(float(*this) - 1.0f); + return h; +} + +inline cutlass::half_t operator+(float a, cutlass::half_t const& b) { + return cutlass::half_t(a + float(b)); +} + +inline cutlass::half_t operator-(float a, cutlass::half_t const& b) { + return cutlass::half_t(a - float(b)); +} + +inline cutlass::half_t operator*(float a, cutlass::half_t const& b) { + return cutlass::half_t(a * float(b)); +} + +inline cutlass::half_t operator/(float a, cutlass::half_t const& b) { + return cutlass::half_t(a / float(b)); +} + +// +// +// + +inline cutlass::half2_t::half2_t() {} + +inline cutlass::half2_t::half2_t(half_t lo, half_t hi) : lo(lo), hi(hi) {} + +inline cutlass::half2_t::half2_t(std::pair<float, float> const& p) : lo(p.first), hi(p.second) {} + +inline cutlass::half2_t::half2_t(unsigned data) + : lo(half_t::bitcast(uint16_t(data & 0x0ffff))), + hi(half_t::bitcast(uint16_t((data >> 16) & 0x0ffff))) {} + +inline cutlass::half2_t cutlass::half2_t::operator+(half2_t const& b) const { + return half2_t(lo + b.lo, hi + b.hi); +} + +inline cutlass::half2_t cutlass::half2_t::operator-(half2_t const& b) const { + return half2_t(lo - b.lo, hi - b.hi); +} + +inline cutlass::half2_t cutlass::half2_t::operator*(half2_t const& b) const { + return half2_t(lo * b.lo, hi * b.hi); +} + +inline cutlass::half2_t cutlass::half2_t::operator/(half2_t const& b) const { + return half2_t(lo / b.lo, hi / b.hi); +} + +inline cutlass::half2_t& cutlass::half2_t::operator+=(half2_t const& b) { + lo += b.lo; + hi += b.hi; + return *this; +} + +inline cutlass::half2_t& cutlass::half2_t::operator-=(half2_t const& b) { + lo -= b.lo; + hi -= b.hi; + return *this; +} + +inline cutlass::half2_t& cutlass::half2_t::operator*=(half2_t const& b) { + lo *= b.lo; + hi *= b.hi; + return *this; +} + +inline cutlass::half2_t& cutlass::half2_t::operator/=(half2_t const& b) { + lo /= b.lo; + hi /= b.hi; + return *this; +} + +inline float cutlass::half2_t::dot(half2_t const& b) const { + return float(lo) * float(b.lo) + float(hi) * float(b.hi); +} + +inline float cutlass::half2_t::dot(half2_t const& b, float c) const { return c + dot(b); } + +inline cutlass::half_t cutlass::half2_t::doth(half2_t const& b) const { + return cutlass::half_t(dot(b)); +} + +inline cutlass::half_t cutlass::half2_t::doth(half2_t const& b, half_t c) const { + return cutlass::half_t(dot(b, float(c))); +} + +inline cutlass::half2_t::operator std::pair<float, float>() const { + return std::pair<float, float>(float(lo), float(hi)); +} + +inline unsigned cutlass::half2_t::packed() const { return (lo.x | (hi.x << 16)); } + +inline cutlass::half2_t::operator unsigned() const { return packed(); } + +// +// +// + +template <> +inline float cutlass::bitcast<float, unsigned>(unsigned const& u) { + return *reinterpret_cast<float const*>(&u); +} + +template <> +inline float cutlass::bitcast<float, int>(int const& i) { + return *reinterpret_cast<float const*>(&i); +} + +template <> +inline unsigned cutlass::bitcast<unsigned, float>(float const& f) { + return *reinterpret_cast<unsigned const*>(&f); +} + +template <> +inline cutlass::half_t cutlass::bitcast<cutlass::half_t, unsigned short>(unsigned short const& s) { + return *reinterpret_cast<cutlass::half_t const*>(&s); +} + +template <> +inline unsigned short cutlass::bitcast<unsigned short, cutlass::half_t>(cutlass::half_t const& h) { + return *reinterpret_cast<unsigned short const*>(&h); +} + +template <> +inline half cutlass::bitcast<half, unsigned short>(unsigned short const& s) { + return *reinterpret_cast<half const*>(&s); +} + +// +// Lexical casts +// + +#ifdef BOOST_LEXICAL_CAST_INCLUDED +namespace boost { +template <> +cutlass::half_t lexical_cast<cutlass::half_t>(std::string const& arg) { + return cutlass::half_t(boost::lexical_cast<float>(arg)); +} + +template <> +std::string lexical_cast<std::string>(cutlass::half_t const& arg) { + return boost::lexical_cast<std::string>(float(arg)); +} +} // namespace boost +#endif + +// +// Standard Library Operations +// + +// std +namespace std { + +inline cutlass::half_t abs(cutlass::half_t const& h) { + return cutlass::half_t::bitcast(h.x & 0x7fff); +} + +inline bool isnan(cutlass::half_t const& h) { return h.isnan(); } + +inline bool isfinite(cutlass::half_t const& h) { return h.isfinite(); } + +inline cutlass::half_t nanh(const char*) { return cutlass::half_t::nan(); } + +inline bool isinf(cutlass::half_t const& h) { return h.isinf(); } + +inline bool isnormal(cutlass::half_t const& h) { return h.isnormal(); } + +inline int fpclassify(cutlass::half_t const& h) { + int exp = h.exponent(); + unsigned short mantissa = h.mantissa(); + if (exp < -14) { + if (mantissa == 0) { + return FP_ZERO; + } else { + return FP_SUBNORMAL; + } + } else if (exp > 15) { + if (mantissa == 0) { + return FP_INFINITE; + } else { + return FP_NAN; + } + } + return FP_NORMAL; +} + +inline bool signbit(cutlass::half_t const& h) { return h.signbit(); } + +inline cutlass::half_t sqrt(cutlass::half_t const& h) { + return cutlass::half_t(std::sqrt(float(h))); +} +} // namespace std + +// +// Stream interactions +// + +/// put to stream - half_t-precision types bitcast as unsigned shorts if base is hexadecimal +inline std::ostream& operator<<(std::ostream& out, cutlass::half_t const& h) { + if (out.flags() & std::ios::hex) { + return out << h.x; + } else { + return out << float(h); + } +} + +/// read from stream - half_t-precision types parsed as unsigned shorts if base is hexadecimal +inline std::istream& operator>>(std::istream& in, cutlass::half_t& h) { + if (in.flags() & std::ios::hex) { + unsigned short u = 0; + in >> u; + h = cutlass::half_t::bitcast(u); + } else { + float f = 0; + in >> f; + h = cutlass::half_t(f); + } + return in; +} |
