From 61203453aaca4e47c05c598a673150522160ca87 Mon Sep 17 00:00:00 2001 From: Cedric Nugteren Date: Sun, 19 Jun 2016 13:55:49 +0200 Subject: Renamed all C++ source files to .cpp to match the .hpp extension better --- src/utilities.cpp | 390 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 src/utilities.cpp (limited to 'src/utilities.cpp') diff --git a/src/utilities.cpp b/src/utilities.cpp new file mode 100644 index 00000000..e3a1fb75 --- /dev/null +++ b/src/utilities.cpp @@ -0,0 +1,390 @@ + +// ================================================================================================= +// This file is part of the CLBlast project. The project is licensed under Apache Version 2.0. This +// project loosely follows the Google C++ styleguide and uses a tab-size of two spaces and a max- +// width of 100 characters per line. +// +// Author(s): +// Cedric Nugteren +// +// This file implements the common (test) utility functions. +// +// ================================================================================================= + +#include "utilities.hpp" + +#include +#include +#include +#include +#include + +namespace clblast { +// ================================================================================================= + +// Returns a scalar with a default value +template +T GetScalar() { + return static_cast(2.0); +} +template float GetScalar(); +template double GetScalar(); + +// Specialized version of the above for half-precision +template <> +half GetScalar() { + return FloatToHalf(2.0f); +} + +// Specialized versions of the above for complex data-types +template <> +float2 GetScalar() { + return {2.0f, 0.5f}; +} +template <> +double2 GetScalar() { + return {2.0, 0.5}; +} + +// Returns a scalar of value 1 +template +T ConstantOne() { + return static_cast(1.0); +} +template float ConstantOne(); +template double ConstantOne(); + +// Specialized version of the above for half-precision +template <> +half ConstantOne() { + return FloatToHalf(1.0f); +} + +// Specialized versions of the above for complex data-types +template <> +float2 ConstantOne() { + return {1.0f, 0.0f}; +} +template <> +double2 ConstantOne() { + return {1.0, 0.0}; +} + +// ================================================================================================= + +// Implements the string conversion using std::to_string if possible +template +std::string ToString(T value) { + return std::to_string(value); +} +template std::string ToString(int value); +template std::string ToString(size_t value); +template std::string ToString(float value); +template std::string ToString(double value); + +// If not possible directly: special cases for complex data-types +template <> +std::string ToString(float2 value) { + std::ostringstream real, imag; + real << std::setprecision(2) << value.real(); + imag << std::setprecision(2) << value.imag(); + return real.str()+"+"+imag.str()+"i"; +} +template <> +std::string ToString(double2 value) { + std::ostringstream real, imag; + real << std::setprecision(2) << value.real(); + imag << std::setprecision(2) << value.imag(); + return real.str()+"+"+imag.str()+"i"; +} + +// If not possible directly: special case for half-precision +template <> +std::string ToString(half value) { + return std::to_string(HalfToFloat(value)); +} + +// If not possible directly: special cases for CLBlast data-types +template <> +std::string ToString(Layout value) { + switch(value) { + case Layout::kRowMajor: return ToString(static_cast(value))+" (row-major)"; + case Layout::kColMajor: return ToString(static_cast(value))+" (col-major)"; + } +} +template <> +std::string ToString(Transpose value) { + switch(value) { + case Transpose::kNo: return ToString(static_cast(value))+" (regular)"; + case Transpose::kYes: return ToString(static_cast(value))+" (transposed)"; + case Transpose::kConjugate: return ToString(static_cast(value))+" (conjugate)"; + } +} +template <> +std::string ToString(Side value) { + switch(value) { + case Side::kLeft: return ToString(static_cast(value))+" (left)"; + case Side::kRight: return ToString(static_cast(value))+" (right)"; + } +} +template <> +std::string ToString(Triangle value) { + switch(value) { + case Triangle::kUpper: return ToString(static_cast(value))+" (upper)"; + case Triangle::kLower: return ToString(static_cast(value))+" (lower)"; + } +} +template <> +std::string ToString(Diagonal value) { + switch(value) { + case Diagonal::kUnit: return ToString(static_cast(value))+" (unit)"; + case Diagonal::kNonUnit: return ToString(static_cast(value))+" (non-unit)"; + } +} +template <> +std::string ToString(Precision value) { + switch(value) { + case Precision::kHalf: return ToString(static_cast(value))+" (half)"; + case Precision::kSingle: return ToString(static_cast(value))+" (single)"; + case Precision::kDouble: return ToString(static_cast(value))+" (double)"; + case Precision::kComplexSingle: return ToString(static_cast(value))+" (complex-single)"; + case Precision::kComplexDouble: return ToString(static_cast(value))+" (complex-double)"; + } +} + +// ================================================================================================= + +// Helper for the below function to convert the argument to the value type. Adds specialization for +// complex data-types. Note that complex arguments are accepted as regular values and are copied to +// both the real and imaginary parts. +template +T ConvertArgument(const char* value) { + return static_cast(std::stoi(value)); +} +template <> half ConvertArgument(const char* value) { + return FloatToHalf(static_cast(std::stod(value))); +} +template <> float ConvertArgument(const char* value) { + return static_cast(std::stod(value)); +} +template <> double ConvertArgument(const char* value) { + return static_cast(std::stod(value)); +} +template <> float2 ConvertArgument(const char* value) { + auto val = static_cast(std::stod(value)); + return float2{val, val}; +} +template <> double2 ConvertArgument(const char* value) { + auto val = static_cast(std::stod(value)); + return double2{val, val}; +} + +// This function matches patterns in the form of "-option value" or "--option value". It returns a +// default value in case the option is not found in the argument string. +template +T GetArgument(const int argc, char *argv[], std::string &help, + const std::string &option, const T default_value) { + + // Parses the argument. Note that this supports both the given option (e.g. -device) and one with + // an extra dash in front (e.g. --device). + auto return_value = static_cast(default_value); + for (int c=0; c(argv[c]); + break; + } + } + + // Updates the help message and returns + help += " -"+option+" "+ToString(return_value)+" "; + help += (return_value == default_value) ? "[=default]\n" : "\n"; + return return_value; +} + +// Compiles the above function +template int GetArgument(const int, char **, std::string&, const std::string&, const int); +template size_t GetArgument(const int, char **, std::string&, const std::string&, const size_t); +template half GetArgument(const int, char **, std::string&, const std::string&, const half); +template float GetArgument(const int, char **, std::string&, const std::string&, const float); +template double GetArgument(const int, char **, std::string&, const std::string&, const double); +template float2 GetArgument(const int, char **, std::string&, const std::string&, const float2); +template double2 GetArgument(const int, char **, std::string&, const std::string&, const double2); +template Layout GetArgument(const int, char **, std::string&, const std::string&, const Layout); +template Transpose GetArgument(const int, char **, std::string&, const std::string&, const Transpose); +template Side GetArgument(const int, char **, std::string&, const std::string&, const Side); +template Triangle GetArgument(const int, char **, std::string&, const std::string&, const Triangle); +template Diagonal GetArgument(const int, char **, std::string&, const std::string&, const Diagonal); +template Precision GetArgument(const int, char **, std::string&, const std::string&, const Precision); + +// ================================================================================================= + +// Returns only the precision argument +Precision GetPrecision(const int argc, char *argv[], const Precision default_precision) { + auto dummy = std::string{}; + return GetArgument(argc, argv, dummy, kArgPrecision, default_precision); +} + +// ================================================================================================= + +// Checks whether an argument is given. Returns true or false. +bool CheckArgument(const int argc, char *argv[], std::string &help, + const std::string &option) { + + // Parses the argument. Note that this supports both the given option (e.g. -device) and one with + // an extra dash in front (e.g. --device). + auto return_value = false; + for (int c=0; c(std::chrono::system_clock::now().time_since_epoch().count()); +} + +// Create a random number generator and populates a vector with samples from a random distribution +template +void PopulateVector(std::vector &vector) { + auto lower_limit = static_cast(kTestDataLowerLimit); + auto upper_limit = static_cast(kTestDataUpperLimit); + std::mt19937 mt(GetRandomSeed()); + std::uniform_real_distribution dist(lower_limit, upper_limit); + for (auto &element: vector) { element = dist(mt); } +} +template void PopulateVector(std::vector&); +template void PopulateVector(std::vector&); + +// Specialized versions of the above for complex data-types +template <> +void PopulateVector(std::vector &vector) { + auto lower_limit = static_cast(kTestDataLowerLimit); + auto upper_limit = static_cast(kTestDataUpperLimit); + std::mt19937 mt(GetRandomSeed()); + std::uniform_real_distribution dist(lower_limit, upper_limit); + for (auto &element: vector) { element.real(dist(mt)); element.imag(dist(mt)); } +} +template <> +void PopulateVector(std::vector &vector) { + auto lower_limit = static_cast(kTestDataLowerLimit); + auto upper_limit = static_cast(kTestDataUpperLimit); + std::mt19937 mt(GetRandomSeed()); + std::uniform_real_distribution dist(lower_limit, upper_limit); + for (auto &element: vector) { element.real(dist(mt)); element.imag(dist(mt)); } +} + +// Specialized versions of the above for half-precision +template <> +void PopulateVector(std::vector &vector) { + const auto lower_limit = static_cast(kTestDataLowerLimit); + const auto upper_limit = static_cast(kTestDataUpperLimit); + std::mt19937 mt(GetRandomSeed()); + std::uniform_real_distribution dist(lower_limit, upper_limit); + for (auto &element: vector) { element = FloatToHalf(dist(mt)); } +} + +// ================================================================================================= + +// Conversion between half and single-precision +std::vector HalfToFloatBuffer(const std::vector& source) { + auto result = std::vector(source.size()); + for (auto i = size_t(0); i < source.size(); ++i) { result[i] = HalfToFloat(source[i]); } + return result; +} +void FloatToHalfBuffer(std::vector& result, const std::vector& source) { + for (auto i = size_t(0); i < source.size(); ++i) { result[i] = FloatToHalf(source[i]); } +} + +// As above, but now for OpenCL data-types instead of std::vectors +Buffer HalfToFloatBuffer(const Buffer& source, cl_command_queue queue_raw) { + const auto size = source.GetSize() / sizeof(half); + auto queue = Queue(queue_raw); + auto context = queue.GetContext(); + auto source_cpu = std::vector(size); + source.Read(queue, size, source_cpu); + auto result_cpu = HalfToFloatBuffer(source_cpu); + auto result = Buffer(context, size); + result.Write(queue, size, result_cpu); + return result; +} +void FloatToHalfBuffer(Buffer& result, const Buffer& source, cl_command_queue queue_raw) { + const auto size = source.GetSize() / sizeof(float); + auto queue = Queue(queue_raw); + auto context = queue.GetContext(); + auto source_cpu = std::vector(size); + source.Read(queue, size, source_cpu); + auto result_cpu = std::vector(size); + FloatToHalfBuffer(result_cpu, source_cpu); + result.Write(queue, size, result_cpu); +} + +// ================================================================================================= + +// Rounding functions performing ceiling and division operations +size_t CeilDiv(const size_t x, const size_t y) { + return 1 + ((x - 1) / y); +} +size_t Ceil(const size_t x, const size_t y) { + return CeilDiv(x,y)*y; +} + +// Helper function to determine whether or not 'a' is a multiple of 'b' +bool IsMultiple(const size_t a, const size_t b) { + return ((a/b)*b == a) ? true : false; +}; + +// ================================================================================================= + +// Convert the precision enum (as integer) into bytes +size_t GetBytes(const Precision precision) { + switch(precision) { + case Precision::kHalf: return 2; + case Precision::kSingle: return 4; + case Precision::kDouble: return 8; + case Precision::kComplexSingle: return 8; + case Precision::kComplexDouble: return 16; + } +} + +// Convert the template argument into a precision value +template <> Precision PrecisionValue() { return Precision::kHalf; } +template <> Precision PrecisionValue() { return Precision::kSingle; } +template <> Precision PrecisionValue() { return Precision::kDouble; } +template <> Precision PrecisionValue() { return Precision::kComplexSingle; } +template <> Precision PrecisionValue() { return Precision::kComplexDouble; } + +// ================================================================================================= + +// Returns false is this precision is not supported by the device +template <> bool PrecisionSupported(const Device &) { return true; } +template <> bool PrecisionSupported(const Device &) { return true; } +template <> bool PrecisionSupported(const Device &device) { + auto extensions = device.Capabilities(); + return (extensions.find(kKhronosDoublePrecision) == std::string::npos) ? false : true; +} +template <> bool PrecisionSupported(const Device &device) { + auto extensions = device.Capabilities(); + return (extensions.find(kKhronosDoublePrecision) == std::string::npos) ? false : true; +} +template <> bool PrecisionSupported(const Device &device) { + auto extensions = device.Capabilities(); + if (device.Name() == "Mali-T628") { return true; } // supports fp16 but not cl_khr_fp16 officially + return (extensions.find(kKhronosHalfPrecision) == std::string::npos) ? false : true; +} + +// ================================================================================================= +} // namespace clblast -- cgit v1.2.3