summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCedric Nugteren <web@cedricnugteren.nl>2017-08-12 17:17:10 +0200
committerGitHub <noreply@github.com>2017-08-12 17:17:10 +0200
commitd67fd6604b4a6584c4f9e856057fcc8076ce377d (patch)
tree9060e02bf3b8db1807d87415e3892b075cc38628
parenteb896838b196415caddca7fa843078129510eced (diff)
parentd30c459c5f63f7027c80d6f5f7c0fbefd85d78f8 (diff)
Merge pull request #182 from CNugteren/compilation_improvements
Compilation improvements
-rw-r--r--CMakeLists.txt81
-rw-r--r--src/routines/common.cpp75
-rw-r--r--src/routines/common.hpp25
-rw-r--r--src/utilities/utilities.cpp97
-rw-r--r--src/utilities/utilities.hpp67
-rw-r--r--test/correctness/tester.hpp2
-rw-r--r--test/performance/client.hpp2
-rw-r--r--test/routines/common.hpp1
-rw-r--r--test/test_utilities.cpp114
-rw-r--r--test/test_utilities.hpp99
10 files changed, 360 insertions, 203 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b37834af..e35c5a85 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -159,6 +159,8 @@ endif()
# Sets the supported routines and the used kernels. New routines and kernels should be added here.
set(KERNELS copy_fast copy_pad transpose_fast transpose_pad xaxpy xdot xger
xgemm xgemm_direct xgemv)
+set(DATABASES copy invert pad padtranspose transpose xaxpy xdot
+ xgemm xgemm_direct xgemv xgemv_fast xgemv_fast_rot xger xtrsv)
set(SAMPLE_PROGRAMS_CPP sgemm sgemm_batched)
set(SAMPLE_PROGRAMS_C sasum dgemv sgemm haxpy cache)
if(NETLIB)
@@ -174,7 +176,7 @@ set(PRECISIONS 32 64 3232 6464 16)
# ==================================================================================================
-# Gathers all source-files
+# Gathers all source-files (required for the compiler) and header-files (for IDEs only)
set(SOURCES
src/database/database.cpp
src/routines/common.cpp
@@ -186,27 +188,56 @@ set(SOURCES
src/routine.cpp
src/routines/levelx/xinvert.cpp # only source, don't include it as a test
)
+set(HEADERS # such that they can be discovered by IDEs such as CLion and Visual Studio
+ include/clblast.h
+ include/clblast_c.h
+ include/clblast_half.h
+ src/database/apple_cpu_fallback.hpp
+ src/database/database.hpp
+ src/database/kernel_selection.hpp
+ src/routines/level1/xamin.hpp
+ src/routines/level1/xmax.hpp
+ src/routines/level1/xmin.hpp
+ src/routines/level1/xsum.hpp
+ src/routines/common.hpp
+ src/utilities/buffer_test.hpp
+ src/utilities/clblast_exceptions.hpp
+ src/utilities/msvc.hpp
+ src/utilities/utilities.hpp
+ src/cache.hpp
+ src/clpp11.hpp
+ src/cxpp11_common.hpp
+ src/routine.hpp
+)
if(NETLIB)
set(SOURCES ${SOURCES} src/clblast_netlib_c.cpp)
+ set(HEADERS ${HEADERS} include/clblast_netlib_c.h)
endif()
foreach(ROUTINE ${LEVEL1_ROUTINES})
set(SOURCES ${SOURCES} src/routines/level1/${ROUTINE}.cpp)
+ set(HEADERS ${HEADERS} src/routines/level1/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVEL2_ROUTINES})
set(SOURCES ${SOURCES} src/routines/level2/${ROUTINE}.cpp)
+ set(HEADERS ${HEADERS} src/routines/level2/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVEL3_ROUTINES})
set(SOURCES ${SOURCES} src/routines/level3/${ROUTINE}.cpp)
+ set(HEADERS ${HEADERS} src/routines/level3/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVELX_ROUTINES})
set(SOURCES ${SOURCES} src/routines/levelx/${ROUTINE}.cpp)
+ set(HEADERS ${HEADERS} src/routines/levelx/${ROUTINE}.hpp)
+endforeach()
+foreach(DATABASE ${DATABASES})
+ set(HEADERS ${HEADERS} src/database/kernels/${DATABASE}.hpp)
endforeach()
# Creates and links the library
if(BUILD_SHARED_LIBS)
- add_library(clblast SHARED ${SOURCES})
+ add_library(clblast SHARED ${SOURCES} ${HEADERS})
else(BUILD_SHARED_LIBS)
- add_library(clblast STATIC ${SOURCES})
+ add_library(clblast STATIC ${SOURCES} ${HEADERS})
endif()
target_link_libraries(clblast ${OPENCL_LIBRARIES})
@@ -276,7 +307,7 @@ endif()
if(TUNERS)
# Visual Studio requires the sources of non-exported objects/libraries
- set(TUNERS_COMMON )
+ set(TUNERS_COMMON src/tuning/tuning.hpp)
if(MSVC)
set(TUNERS_COMMON ${TUNERS_COMMON} src/utilities/utilities.cpp)
endif()
@@ -314,6 +345,7 @@ if(CLIENTS OR TESTS)
find_package(Threads)
set(REF_LIBRARIES ${REF_LIBRARIES} ${CLBLAS_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
set(REF_INCLUDES ${REF_INCLUDES} ${CLBLAS_INCLUDE_DIRS})
+ set(WRAPPERS ${WRAPPERS} test/wrapper_clblas.hpp)
if(MSVC)
add_definitions(" /DCLBLAST_REF_CLBLAS")
else()
@@ -323,6 +355,7 @@ if(CLIENTS OR TESTS)
if(CBLAS_FOUND)
set(REF_INCLUDES ${REF_INCLUDES} ${CBLAS_INCLUDE_DIRS})
set(REF_LIBRARIES ${REF_LIBRARIES} ${CBLAS_LIBRARIES})
+ set(WRAPPERS ${WRAPPERS} test/wrapper_cblas.hpp)
if(MSVC)
add_definitions(" /DCLBLAST_REF_CBLAS")
else()
@@ -332,6 +365,7 @@ if(CLIENTS OR TESTS)
if(CUBLAS_FOUND)
set(REF_INCLUDES ${REF_INCLUDES} ${CUDA_INCLUDE_DIRS})
set(REF_LIBRARIES ${REF_LIBRARIES} ${CUDA_LIBRARIES} ${CUBLAS_LIBRARIES})
+ set(WRAPPERS ${WRAPPERS} test/wrapper_cuda.hpp test/wrapper_cublas.hpp)
if(MSVC)
add_definitions(" /DCLBLAST_REF_CUBLAS")
else()
@@ -346,14 +380,16 @@ endif()
# Section for the performance tests (i.e. the client). These compare against optionally a reference
# library, either clBLAS, a CPU BLAS, or CUDA's cuBLAS.
if(CLIENTS)
+ set(CLIENTS_COMMON ${WRAPPERS} test/test_utilities.hpp
+ test/performance/client.hpp test/routines/common.hpp)
# Visual Studio requires the sources of non-exported objects/libraries
- set(CLIENTS_COMMON )
if(MSVC)
- set(CLIENTS_COMMON ${CLIENTS_COMMON} src/utilities/utilities.cpp test/performance/client.cpp)
+ set(CLIENTS_COMMON ${CLIENTS_COMMON} src/utilities/utilities.cpp test/test_utilities.cpp
+ test/performance/client.cpp)
else()
# Creates the common performance-tests objects (requires CMake 2.8.8)
- add_library(test_performance_common OBJECT test/performance/client.cpp)
+ add_library(test_performance_common OBJECT test/test_utilities.cpp test/performance/client.cpp)
# Adds CLBlast's interface include paths because we can't link to CLBlast here
target_include_directories(test_performance_common PRIVATE
@@ -365,19 +401,23 @@ if(CLIENTS)
# Compiles the performance-tests
foreach(ROUTINE ${LEVEL1_ROUTINES})
add_executable(clblast_client_${ROUTINE} ${CLIENTS_COMMON}
- test/performance/routines/level1/${ROUTINE}.cpp)
+ test/performance/routines/level1/${ROUTINE}.cpp
+ test/routines/level1/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVEL2_ROUTINES})
add_executable(clblast_client_${ROUTINE} ${CLIENTS_COMMON}
- test/performance/routines/level2/${ROUTINE}.cpp)
+ test/performance/routines/level2/${ROUTINE}.cpp
+ test/routines/level2/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVEL3_ROUTINES})
add_executable(clblast_client_${ROUTINE} ${CLIENTS_COMMON}
- test/performance/routines/level3/${ROUTINE}.cpp)
+ test/performance/routines/level3/${ROUTINE}.cpp
+ test/routines/level3/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVELX_ROUTINES})
add_executable(clblast_client_${ROUTINE} ${CLIENTS_COMMON}
- test/performance/routines/levelx/${ROUTINE}.cpp)
+ test/performance/routines/levelx/${ROUTINE}.cpp
+ test/routines/levelx/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${ROUTINES})
target_link_libraries(clblast_client_${ROUTINE} clblast ${REF_LIBRARIES} ${OPENCL_LIBRARIES})
@@ -393,16 +433,17 @@ endif()
# CPU BLAS library, and/or cuBLAS to act as a reference.
if(TESTS)
enable_testing()
+ set(TESTS_COMMON ${WRAPPERS} test/test_utilities.hpp test/correctness/testblas.hpp
+ test/correctness/tester.hpp test/routines/common.hpp)
# Visual Studio requires the sources of non-exported objects/libraries
- set(TESTS_COMMON )
if(MSVC)
- set(TESTS_COMMON ${TESTS_COMMON} src/utilities/utilities.cpp
+ set(TESTS_COMMON ${TESTS_COMMON} src/utilities/utilities.cpp test/test_utilities.cpp
test/correctness/tester.cpp test/correctness/testblas.cpp)
else()
# Creates the common correctness-tests objects (requires CMake 2.8.8)
add_library(test_correctness_common OBJECT
- test/correctness/tester.cpp test/correctness/testblas.cpp)
+ test/test_utilities.cpp test/correctness/tester.cpp test/correctness/testblas.cpp)
target_include_directories(test_correctness_common PUBLIC
$<TARGET_PROPERTY:clblast,INTERFACE_INCLUDE_DIRECTORIES>
${clblast_SOURCE_DIR} ${REF_INCLUDES})
@@ -412,19 +453,23 @@ if(TESTS)
# Compiles the correctness-tests
foreach(ROUTINE ${LEVEL1_ROUTINES})
add_executable(clblast_test_${ROUTINE} ${TESTS_COMMON}
- test/correctness/routines/level1/${ROUTINE}.cpp)
+ test/correctness/routines/level1/${ROUTINE}.cpp
+ test/routines/level1/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVEL2_ROUTINES})
add_executable(clblast_test_${ROUTINE} ${TESTS_COMMON}
- test/correctness/routines/level2/${ROUTINE}.cpp)
+ test/correctness/routines/level2/${ROUTINE}.cpp
+ test/routines/level2/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVEL3_ROUTINES})
add_executable(clblast_test_${ROUTINE} ${TESTS_COMMON}
- test/correctness/routines/level3/${ROUTINE}.cpp)
+ test/correctness/routines/level3/${ROUTINE}.cpp
+ test/routines/level3/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${LEVELX_ROUTINES})
add_executable(clblast_test_${ROUTINE} ${TESTS_COMMON}
- test/correctness/routines/levelx/${ROUTINE}.cpp)
+ test/correctness/routines/levelx/${ROUTINE}.cpp
+ test/routines/levelx/${ROUTINE}.hpp)
endforeach()
foreach(ROUTINE ${ROUTINES})
target_link_libraries(clblast_test_${ROUTINE} clblast ${REF_LIBRARIES} ${OPENCL_LIBRARIES})
diff --git a/src/routines/common.cpp b/src/routines/common.cpp
index c995dc12..5b178e53 100644
--- a/src/routines/common.cpp
+++ b/src/routines/common.cpp
@@ -73,4 +73,79 @@ void RunKernel(Kernel &kernel, Queue &queue, const Device &device,
}
// =================================================================================================
+
+// Sets all elements of a matrix to a constant value
+template <typename T>
+void FillMatrix(Queue &queue, const Device &device,
+ const Program &program, const Databases &,
+ EventPointer event, const std::vector<Event> &waitForEvents,
+ const size_t m, const size_t n, const size_t ld, const size_t offset,
+ const Buffer<T> &dest,
+ const T constant_value) {
+ auto kernel = Kernel(program, "FillMatrix");
+ kernel.SetArgument(0, static_cast<int>(m));
+ kernel.SetArgument(1, static_cast<int>(n));
+ kernel.SetArgument(2, static_cast<int>(ld));
+ kernel.SetArgument(3, static_cast<int>(offset));
+ kernel.SetArgument(4, dest());
+ kernel.SetArgument(5, GetRealArg(constant_value));
+ auto local = std::vector<size_t>{8, 8};
+ auto global = std::vector<size_t>{Ceil(m, 8), Ceil(n, 8)};
+ RunKernel(kernel, queue, device, global, local, event, waitForEvents);
+}
+
+// Compiles the above function
+template void FillMatrix<half>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const size_t, const Buffer<half>&, const half);
+template void FillMatrix<float>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const size_t, const Buffer<float>&, const float);
+template void FillMatrix<double>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const size_t, const Buffer<double>&, const double);
+template void FillMatrix<float2>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const size_t, const Buffer<float2>&, const float2);
+template void FillMatrix<double2>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const size_t, const Buffer<double2>&, const double2);
+
+// Sets all elements of a vector to a constant value
+template <typename T>
+void FillVector(Queue &queue, const Device &device,
+ const Program &program, const Databases &,
+ EventPointer event, const std::vector<Event> &waitForEvents,
+ const size_t n, const size_t inc, const size_t offset,
+ const Buffer<T> &dest,
+ const T constant_value) {
+ auto kernel = Kernel(program, "FillVector");
+ kernel.SetArgument(0, static_cast<int>(n));
+ kernel.SetArgument(1, static_cast<int>(inc));
+ kernel.SetArgument(2, static_cast<int>(offset));
+ kernel.SetArgument(3, dest());
+ kernel.SetArgument(4, GetRealArg(constant_value));
+ auto local = std::vector<size_t>{64};
+ auto global = std::vector<size_t>{Ceil(n, 64)};
+ RunKernel(kernel, queue, device, global, local, event, waitForEvents);
+}
+
+// Compiles the above function
+template void FillVector<half>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const Buffer<half>&, const half);
+template void FillVector<float>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const Buffer<float>&, const float);
+template void FillVector<double>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const Buffer<double>&, const double);
+template void FillVector<float2>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const Buffer<float2>&, const float2);
+template void FillVector<double2>(Queue&, const Device&, const Program&, const Databases&,
+ EventPointer, const std::vector<Event>&, const size_t, const size_t,
+ const size_t, const Buffer<double2>&, const double2);
+
+// =================================================================================================
} // namespace clblast
diff --git a/src/routines/common.hpp b/src/routines/common.hpp
index 28a43da5..84ccd9d2 100644
--- a/src/routines/common.hpp
+++ b/src/routines/common.hpp
@@ -40,18 +40,7 @@ void FillMatrix(Queue &queue, const Device &device,
EventPointer event, const std::vector<Event> &waitForEvents,
const size_t m, const size_t n, const size_t ld, const size_t offset,
const Buffer<T> &dest,
- const T constant_value) {
- auto kernel = Kernel(program, "FillMatrix");
- kernel.SetArgument(0, static_cast<int>(m));
- kernel.SetArgument(1, static_cast<int>(n));
- kernel.SetArgument(2, static_cast<int>(ld));
- kernel.SetArgument(3, static_cast<int>(offset));
- kernel.SetArgument(4, dest());
- kernel.SetArgument(5, GetRealArg(constant_value));
- auto local = std::vector<size_t>{8, 8};
- auto global = std::vector<size_t>{Ceil(m, 8), Ceil(n, 8)};
- RunKernel(kernel, queue, device, global, local, event, waitForEvents);
-}
+ const T constant_value);
// Sets all elements of a vector to a constant value
template <typename T>
@@ -60,17 +49,7 @@ void FillVector(Queue &queue, const Device &device,
EventPointer event, const std::vector<Event> &waitForEvents,
const size_t n, const size_t inc, const size_t offset,
const Buffer<T> &dest,
- const T constant_value) {
- auto kernel = Kernel(program, "FillVector");
- kernel.SetArgument(0, static_cast<int>(n));
- kernel.SetArgument(1, static_cast<int>(inc));
- kernel.SetArgument(2, static_cast<int>(offset));
- kernel.SetArgument(3, dest());
- kernel.SetArgument(4, GetRealArg(constant_value));
- auto local = std::vector<size_t>{64};
- auto global = std::vector<size_t>{Ceil(n, 64)};
- RunKernel(kernel, queue, device, global, local, event, waitForEvents);
-}
+ const T constant_value);
// =================================================================================================
diff --git a/src/utilities/utilities.cpp b/src/utilities/utilities.cpp
index 95b70cd5..0cd00438 100644
--- a/src/utilities/utilities.cpp
+++ b/src/utilities/utilities.cpp
@@ -7,7 +7,7 @@
// Author(s):
// Cedric Nugteren <www.cedricnugteren.nl>
//
-// This file implements the common (test) utility functions.
+// This file implements the common utility functions.
//
// =================================================================================================
@@ -85,14 +85,6 @@ template <> double AbsoluteValue(const double2 value) {
return std::sqrt(value.real() * value.real() + value.imag() * value.imag());
}
-// Returns whether a scalar is close to zero
-template <typename T> bool IsCloseToZero(const T value) { return (value > -SmallConstant<T>()) && (value < SmallConstant<T>()); }
-template bool IsCloseToZero<float>(const float);
-template bool IsCloseToZero<double>(const double);
-template <> bool IsCloseToZero(const half value) { return IsCloseToZero(HalfToFloat(value)); }
-template <> bool IsCloseToZero(const float2 value) { return IsCloseToZero(value.real()) || IsCloseToZero(value.imag()); }
-template <> bool IsCloseToZero(const double2 value) { return IsCloseToZero(value.real()) || IsCloseToZero(value.imag()); }
-
// =================================================================================================
// Implements the string conversion using std::to_string if possible
@@ -319,12 +311,6 @@ bool CheckArgument(const std::vector<std::string> &arguments, std::string &help,
// =================================================================================================
-// Returns a random seed. This used to be implemented using 'std::random_device', but that doesn't
-// always work. The chrono-timers are more reliable in that sense, but perhaps less random.
-unsigned int GetRandomSeed() {
- return static_cast<unsigned int>(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 <typename T>
void PopulateVector(std::vector<T> &vector, std::mt19937 &mt, std::uniform_real_distribution<double> &dist) {
@@ -354,87 +340,6 @@ void PopulateVector(std::vector<half> &vector, std::mt19937 &mt, std::uniform_re
// =================================================================================================
-template <typename T, typename U>
-void DeviceToHost(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
- Queue &queue, const std::vector<std::string> &names) {
- for (auto &name: names) {
- if (name == kBufVecX) {buffers_host.x_vec = std::vector<T>(args.x_size, static_cast<T>(0)); buffers.x_vec.Read(queue, args.x_size, buffers_host.x_vec); }
- else if (name == kBufVecY) { buffers_host.y_vec = std::vector<T>(args.y_size, static_cast<T>(0)); buffers.y_vec.Read(queue, args.y_size, buffers_host.y_vec); }
- else if (name == kBufMatA) { buffers_host.a_mat = std::vector<T>(args.a_size, static_cast<T>(0)); buffers.a_mat.Read(queue, args.a_size, buffers_host.a_mat); }
- else if (name == kBufMatB) { buffers_host.b_mat = std::vector<T>(args.b_size, static_cast<T>(0)); buffers.b_mat.Read(queue, args.b_size, buffers_host.b_mat); }
- else if (name == kBufMatC) { buffers_host.c_mat = std::vector<T>(args.c_size, static_cast<T>(0)); buffers.c_mat.Read(queue, args.c_size, buffers_host.c_mat); }
- else if (name == kBufMatAP) { buffers_host.ap_mat = std::vector<T>(args.ap_size, static_cast<T>(0)); buffers.ap_mat.Read(queue, args.ap_size, buffers_host.ap_mat); }
- else if (name == kBufScalar) { buffers_host.scalar = std::vector<T>(args.scalar_size, static_cast<T>(0)); buffers.scalar.Read(queue, args.scalar_size, buffers_host.scalar); }
- else { throw std::runtime_error("Invalid buffer name"); }
- }
-}
-
-template <typename T, typename U>
-void HostToDevice(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
- Queue &queue, const std::vector<std::string> &names) {
- for (auto &name: names) {
- if (name == kBufVecX) { buffers.x_vec.Write(queue, args.x_size, buffers_host.x_vec); }
- else if (name == kBufVecY) { buffers.y_vec.Write(queue, args.y_size, buffers_host.y_vec); }
- else if (name == kBufMatA) { buffers.a_mat.Write(queue, args.a_size, buffers_host.a_mat); }
- else if (name == kBufMatB) { buffers.b_mat.Write(queue, args.b_size, buffers_host.b_mat); }
- else if (name == kBufMatC) { buffers.c_mat.Write(queue, args.c_size, buffers_host.c_mat); }
- else if (name == kBufMatAP) { buffers.ap_mat.Write(queue, args.ap_size, buffers_host.ap_mat); }
- else if (name == kBufScalar) { buffers.scalar.Write(queue, args.scalar_size, buffers_host.scalar); }
- else { throw std::runtime_error("Invalid buffer name"); }
- }
-}
-
-// Compiles the above functions
-template void DeviceToHost(const Arguments<half>&, Buffers<half>&, BuffersHost<half>&, Queue&, const std::vector<std::string>&);
-template void DeviceToHost(const Arguments<float>&, Buffers<float>&, BuffersHost<float>&, Queue&, const std::vector<std::string>&);
-template void DeviceToHost(const Arguments<double>&, Buffers<double>&, BuffersHost<double>&, Queue&, const std::vector<std::string>&);
-template void DeviceToHost(const Arguments<float>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
-template void DeviceToHost(const Arguments<double>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
-template void DeviceToHost(const Arguments<float2>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
-template void DeviceToHost(const Arguments<double2>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
-template void HostToDevice(const Arguments<half>&, Buffers<half>&, BuffersHost<half>&, Queue&, const std::vector<std::string>&);
-template void HostToDevice(const Arguments<float>&, Buffers<float>&, BuffersHost<float>&, Queue&, const std::vector<std::string>&);
-template void HostToDevice(const Arguments<double>&, Buffers<double>&, BuffersHost<double>&, Queue&, const std::vector<std::string>&);
-template void HostToDevice(const Arguments<float>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
-template void HostToDevice(const Arguments<double>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
-template void HostToDevice(const Arguments<float2>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
-template void HostToDevice(const Arguments<double2>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
-
-// =================================================================================================
-
-// Conversion between half and single-precision
-std::vector<float> HalfToFloatBuffer(const std::vector<half>& source) {
- auto result = std::vector<float>(source.size());
- for (auto i = size_t(0); i < source.size(); ++i) { result[i] = HalfToFloat(source[i]); }
- return result;
-}
-void FloatToHalfBuffer(std::vector<half>& result, const std::vector<float>& 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<float> HalfToFloatBuffer(const Buffer<half>& 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<half>(size);
- source.Read(queue, size, source_cpu);
- auto result_cpu = HalfToFloatBuffer(source_cpu);
- auto result = Buffer<float>(context, size);
- result.Write(queue, size, result_cpu);
- return result;
-}
-void FloatToHalfBuffer(Buffer<half>& result, const Buffer<float>& 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<float>(size);
- source.Read(queue, size, source_cpu);
- auto result_cpu = std::vector<half>(size);
- FloatToHalfBuffer(result_cpu, source_cpu);
- result.Write(queue, size, result_cpu);
-}
-
// Converts a 'real' value to a 'real argument' value to be passed to a kernel. Normally there is
// no conversion, but half-precision is not supported as kernel argument so it is converted to float.
template <> typename RealArg<half>::Type GetRealArg(const half value) { return HalfToFloat(value); }
diff --git a/src/utilities/utilities.hpp b/src/utilities/utilities.hpp
index 03051354..72997d7f 100644
--- a/src/utilities/utilities.hpp
+++ b/src/utilities/utilities.hpp
@@ -7,10 +7,9 @@
// Author(s):
// Cedric Nugteren <www.cedricnugteren.nl>
//
-// This file provides declarations for the common (test) utility functions such as a command-line
+// This file provides declarations for the common utility functions such as a command-line
// argument parser. On top of this, it serves as the 'common' header, including the C++ OpenCL
-// wrapper. These utilities are not only used for CLBlast, but also included as part of the tuners,
-// the performance client and the correctness testers.
+// wrapper.
//
// =================================================================================================
@@ -81,19 +80,6 @@ constexpr auto kArgBatchCount = "batch_num";
// The tuner-specific arguments in string form
constexpr auto kArgFraction = "fraction";
-// The client-specific arguments in string form
-constexpr auto kArgCompareclblas = "clblas";
-constexpr auto kArgComparecblas = "cblas";
-constexpr auto kArgComparecublas = "cublas";
-constexpr auto kArgStepSize = "step";
-constexpr auto kArgNumSteps = "num_steps";
-constexpr auto kArgNumRuns = "runs";
-constexpr auto kArgWarmUp = "warm_up";
-
-// The test-specific arguments in string form
-constexpr auto kArgFullTest = "full_test";
-constexpr auto kArgVerbose = "verbose";
-
// The common arguments in string form
constexpr auto kArgPlatform = "platform";
constexpr auto kArgDevice = "device";
@@ -101,6 +87,7 @@ constexpr auto kArgPrecision = "precision";
constexpr auto kArgHelp = "h";
constexpr auto kArgQuiet = "q";
constexpr auto kArgNoAbbreviations = "no_abbrv";
+constexpr auto kArgNumRuns = "runs";
// The buffer names
constexpr auto kBufVecX = "X";
@@ -133,9 +120,6 @@ template <typename T> T SmallConstant();
// Returns the absolute value of a scalar (modulus in case of complex numbers)
template <typename T> typename BaseType<T>::Type AbsoluteValue(const T value);
-// Returns whether a scalar is close to zero
-template <typename T> bool IsCloseToZero(const T value);
-
// =================================================================================================
// Structure containing all possible arguments for test clients, including their default values
@@ -208,28 +192,6 @@ struct Arguments {
bool no_abbrv = false;
};
-// Structure containing all possible buffers for test clients
-template <typename T>
-struct Buffers {
- Buffer<T> x_vec;
- Buffer<T> y_vec;
- Buffer<T> a_mat;
- Buffer<T> b_mat;
- Buffer<T> c_mat;
- Buffer<T> ap_mat;
- Buffer<T> scalar;
-};
-template <typename T>
-struct BuffersHost {
- std::vector<T> x_vec;
- std::vector<T> y_vec;
- std::vector<T> a_mat;
- std::vector<T> b_mat;
- std::vector<T> c_mat;
- std::vector<T> ap_mat;
- std::vector<T> scalar;
-};
-
// =================================================================================================
// Converts a value (e.g. an integer) to a string. This also covers special cases for CLBlast
@@ -264,9 +226,6 @@ bool CheckArgument(const std::vector<std::string> &arguments, std::string &help,
// =================================================================================================
-// Returns a random number to be used as a seed
-unsigned int GetRandomSeed();
-
// Test/example data lower and upper limit
constexpr auto kTestDataLowerLimit = -2.0;
constexpr auto kTestDataUpperLimit = 2.0;
@@ -277,26 +236,6 @@ void PopulateVector(std::vector<T> &vector, std::mt19937 &mt, std::uniform_real_
// =================================================================================================
-// Copies buffers from the OpenCL device to the host
-template <typename T, typename U>
-void DeviceToHost(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
- Queue &queue, const std::vector<std::string> &names);
-
-// Copies buffers from the host to the OpenCL device
-template <typename T, typename U>
-void HostToDevice(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
- Queue &queue, const std::vector<std::string> &names);
-
-// =================================================================================================
-
-// Conversion between half and single-precision
-std::vector<float> HalfToFloatBuffer(const std::vector<half>& source);
-void FloatToHalfBuffer(std::vector<half>& result, const std::vector<float>& source);
-
-// As above, but now for OpenCL data-types instead of std::vectors
-Buffer<float> HalfToFloatBuffer(const Buffer<half>& source, cl_command_queue queue_raw);
-void FloatToHalfBuffer(Buffer<half>& result, const Buffer<float>& source, cl_command_queue queue_raw);
-
// Converts a 'real' value to a 'real argument' value to be passed to a kernel. Normally there is
// no conversion, but half-precision is not supported as kernel argument so it is converted to float.
template <typename T> struct RealArg { using Type = T; };
diff --git a/test/correctness/tester.hpp b/test/correctness/tester.hpp
index dfbefdfd..caf03787 100644
--- a/test/correctness/tester.hpp
+++ b/test/correctness/tester.hpp
@@ -22,7 +22,7 @@
#include <vector>
#include <memory>
-#include "utilities/utilities.hpp"
+#include "test/test_utilities.hpp"
// The libraries
#ifdef CLBLAST_REF_CLBLAS
diff --git a/test/performance/client.hpp b/test/performance/client.hpp
index 47a13017..2ba09cb9 100644
--- a/test/performance/client.hpp
+++ b/test/performance/client.hpp
@@ -25,7 +25,7 @@
#include <vector>
#include <utility>
-#include "utilities/utilities.hpp"
+#include "test/test_utilities.hpp"
// The libraries to test
#ifdef CLBLAST_REF_CLBLAS
diff --git a/test/routines/common.hpp b/test/routines/common.hpp
index 9708288a..47c8f8d7 100644
--- a/test/routines/common.hpp
+++ b/test/routines/common.hpp
@@ -18,6 +18,7 @@
#include <string>
#include "utilities/utilities.hpp"
+#include "test/test_utilities.hpp"
#ifdef CLBLAST_REF_CLBLAS
#include "test/wrapper_clblas.hpp"
diff --git a/test/test_utilities.cpp b/test/test_utilities.cpp
new file mode 100644
index 00000000..b8fd94a9
--- /dev/null
+++ b/test/test_utilities.cpp
@@ -0,0 +1,114 @@
+
+// =================================================================================================
+// 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 <www.cedricnugteren.nl>
+//
+// This file implements the test utility functions.
+//
+// =================================================================================================
+
+#include "test/test_utilities.hpp"
+
+#include <string>
+#include <vector>
+
+namespace clblast {
+// =================================================================================================
+
+// Returns whether a scalar is close to zero
+template <typename T> bool IsCloseToZero(const T value) { return (value > -SmallConstant<T>()) && (value < SmallConstant<T>()); }
+template bool IsCloseToZero<float>(const float);
+template bool IsCloseToZero<double>(const double);
+template <> bool IsCloseToZero(const half value) { return IsCloseToZero(HalfToFloat(value)); }
+template <> bool IsCloseToZero(const float2 value) { return IsCloseToZero(value.real()) || IsCloseToZero(value.imag()); }
+template <> bool IsCloseToZero(const double2 value) { return IsCloseToZero(value.real()) || IsCloseToZero(value.imag()); }
+
+// =================================================================================================
+
+template <typename T, typename U>
+void DeviceToHost(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
+ Queue &queue, const std::vector<std::string> &names) {
+ for (auto &name: names) {
+ if (name == kBufVecX) {buffers_host.x_vec = std::vector<T>(args.x_size, static_cast<T>(0)); buffers.x_vec.Read(queue, args.x_size, buffers_host.x_vec); }
+ else if (name == kBufVecY) { buffers_host.y_vec = std::vector<T>(args.y_size, static_cast<T>(0)); buffers.y_vec.Read(queue, args.y_size, buffers_host.y_vec); }
+ else if (name == kBufMatA) { buffers_host.a_mat = std::vector<T>(args.a_size, static_cast<T>(0)); buffers.a_mat.Read(queue, args.a_size, buffers_host.a_mat); }
+ else if (name == kBufMatB) { buffers_host.b_mat = std::vector<T>(args.b_size, static_cast<T>(0)); buffers.b_mat.Read(queue, args.b_size, buffers_host.b_mat); }
+ else if (name == kBufMatC) { buffers_host.c_mat = std::vector<T>(args.c_size, static_cast<T>(0)); buffers.c_mat.Read(queue, args.c_size, buffers_host.c_mat); }
+ else if (name == kBufMatAP) { buffers_host.ap_mat = std::vector<T>(args.ap_size, static_cast<T>(0)); buffers.ap_mat.Read(queue, args.ap_size, buffers_host.ap_mat); }
+ else if (name == kBufScalar) { buffers_host.scalar = std::vector<T>(args.scalar_size, static_cast<T>(0)); buffers.scalar.Read(queue, args.scalar_size, buffers_host.scalar); }
+ else { throw std::runtime_error("Invalid buffer name"); }
+ }
+}
+
+template <typename T, typename U>
+void HostToDevice(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
+ Queue &queue, const std::vector<std::string> &names) {
+ for (auto &name: names) {
+ if (name == kBufVecX) { buffers.x_vec.Write(queue, args.x_size, buffers_host.x_vec); }
+ else if (name == kBufVecY) { buffers.y_vec.Write(queue, args.y_size, buffers_host.y_vec); }
+ else if (name == kBufMatA) { buffers.a_mat.Write(queue, args.a_size, buffers_host.a_mat); }
+ else if (name == kBufMatB) { buffers.b_mat.Write(queue, args.b_size, buffers_host.b_mat); }
+ else if (name == kBufMatC) { buffers.c_mat.Write(queue, args.c_size, buffers_host.c_mat); }
+ else if (name == kBufMatAP) { buffers.ap_mat.Write(queue, args.ap_size, buffers_host.ap_mat); }
+ else if (name == kBufScalar) { buffers.scalar.Write(queue, args.scalar_size, buffers_host.scalar); }
+ else { throw std::runtime_error("Invalid buffer name"); }
+ }
+}
+
+// Compiles the above functions
+template void DeviceToHost(const Arguments<half>&, Buffers<half>&, BuffersHost<half>&, Queue&, const std::vector<std::string>&);
+template void DeviceToHost(const Arguments<float>&, Buffers<float>&, BuffersHost<float>&, Queue&, const std::vector<std::string>&);
+template void DeviceToHost(const Arguments<double>&, Buffers<double>&, BuffersHost<double>&, Queue&, const std::vector<std::string>&);
+template void DeviceToHost(const Arguments<float>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
+template void DeviceToHost(const Arguments<double>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
+template void DeviceToHost(const Arguments<float2>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
+template void DeviceToHost(const Arguments<double2>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
+template void HostToDevice(const Arguments<half>&, Buffers<half>&, BuffersHost<half>&, Queue&, const std::vector<std::string>&);
+template void HostToDevice(const Arguments<float>&, Buffers<float>&, BuffersHost<float>&, Queue&, const std::vector<std::string>&);
+template void HostToDevice(const Arguments<double>&, Buffers<double>&, BuffersHost<double>&, Queue&, const std::vector<std::string>&);
+template void HostToDevice(const Arguments<float>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
+template void HostToDevice(const Arguments<double>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
+template void HostToDevice(const Arguments<float2>&, Buffers<float2>&, BuffersHost<float2>&, Queue&, const std::vector<std::string>&);
+template void HostToDevice(const Arguments<double2>&, Buffers<double2>&, BuffersHost<double2>&, Queue&, const std::vector<std::string>&);
+
+// =================================================================================================
+
+// Conversion between half and single-precision
+std::vector<float> HalfToFloatBuffer(const std::vector<half>& source) {
+ auto result = std::vector<float>(source.size());
+ for (auto i = size_t(0); i < source.size(); ++i) { result[i] = HalfToFloat(source[i]); }
+ return result;
+}
+void FloatToHalfBuffer(std::vector<half>& result, const std::vector<float>& 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<float> HalfToFloatBuffer(const Buffer<half>& 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<half>(size);
+ source.Read(queue, size, source_cpu);
+ auto result_cpu = HalfToFloatBuffer(source_cpu);
+ auto result = Buffer<float>(context, size);
+ result.Write(queue, size, result_cpu);
+ return result;
+}
+void FloatToHalfBuffer(Buffer<half>& result, const Buffer<float>& 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<float>(size);
+ source.Read(queue, size, source_cpu);
+ auto result_cpu = std::vector<half>(size);
+ FloatToHalfBuffer(result_cpu, source_cpu);
+ result.Write(queue, size, result_cpu);
+}
+
+// =================================================================================================
+} // namespace clblast
diff --git a/test/test_utilities.hpp b/test/test_utilities.hpp
new file mode 100644
index 00000000..fc50a754
--- /dev/null
+++ b/test/test_utilities.hpp
@@ -0,0 +1,99 @@
+
+// =================================================================================================
+// 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 <www.cedricnugteren.nl>
+//
+// This file provides declarations for the common test utility functions (performance clients and
+// correctness testers).
+//
+// =================================================================================================
+
+#ifndef CLBLAST_TEST_UTILITIES_H_
+#define CLBLAST_TEST_UTILITIES_H_
+
+#include <string>
+
+#include "utilities/utilities.hpp"
+
+namespace clblast {
+// =================================================================================================
+
+// The client-specific arguments in string form
+constexpr auto kArgCompareclblas = "clblas";
+constexpr auto kArgComparecblas = "cblas";
+constexpr auto kArgComparecublas = "cublas";
+constexpr auto kArgStepSize = "step";
+constexpr auto kArgNumSteps = "num_steps";
+constexpr auto kArgWarmUp = "warm_up";
+
+// The test-specific arguments in string form
+constexpr auto kArgFullTest = "full_test";
+constexpr auto kArgVerbose = "verbose";
+
+// =================================================================================================
+
+// Returns whether a scalar is close to zero
+template <typename T> bool IsCloseToZero(const T value);
+
+// =================================================================================================
+
+// Structure containing all possible buffers for test clients
+template <typename T>
+struct Buffers {
+ Buffer<T> x_vec;
+ Buffer<T> y_vec;
+ Buffer<T> a_mat;
+ Buffer<T> b_mat;
+ Buffer<T> c_mat;
+ Buffer<T> ap_mat;
+ Buffer<T> scalar;
+};
+template <typename T>
+struct BuffersHost {
+ std::vector<T> x_vec;
+ std::vector<T> y_vec;
+ std::vector<T> a_mat;
+ std::vector<T> b_mat;
+ std::vector<T> c_mat;
+ std::vector<T> ap_mat;
+ std::vector<T> scalar;
+};
+
+// =================================================================================================
+
+// Converts a value (e.g. an integer) to a string. This also covers special cases for CLBlast
+// data-types such as the Layout and Transpose data-types.
+template <typename T>
+std::string ToString(T value);
+
+// =================================================================================================
+
+// Copies buffers from the OpenCL device to the host
+template <typename T, typename U>
+void DeviceToHost(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
+ Queue &queue, const std::vector<std::string> &names);
+
+// Copies buffers from the host to the OpenCL device
+template <typename T, typename U>
+void HostToDevice(const Arguments<U> &args, Buffers<T> &buffers, BuffersHost<T> &buffers_host,
+ Queue &queue, const std::vector<std::string> &names);
+
+// =================================================================================================
+
+// Conversion between half and single-precision
+std::vector<float> HalfToFloatBuffer(const std::vector<half>& source);
+void FloatToHalfBuffer(std::vector<half>& result, const std::vector<float>& source);
+
+// As above, but now for OpenCL data-types instead of std::vectors
+Buffer<float> HalfToFloatBuffer(const Buffer<half>& source, cl_command_queue queue_raw);
+void FloatToHalfBuffer(Buffer<half>& result, const Buffer<float>& source, cl_command_queue queue_raw);
+
+// =================================================================================================
+} // namespace clblast
+
+// CLBLAST_TEST_UTILITIES_H_
+#endif