mirror of
https://github.com/CNugteren/CLBlast.git
synced 2024-08-26 23:07:00 +02:00
Merge pull request #137 from CNugteren/custom_parameters
API to override tuning parameters
This commit is contained in:
commit
0ea30263ac
|
@ -5,6 +5,7 @@ Development version (next release)
|
||||||
- Fixed a bug when using offsets in the direct version of the GEMM kernels
|
- Fixed a bug when using offsets in the direct version of the GEMM kernels
|
||||||
- Fixed a missing cl_khr_fp64 when running double-precision on Intel CPUs
|
- Fixed a missing cl_khr_fp64 when running double-precision on Intel CPUs
|
||||||
- Tests now also exit with an error code when OpenCL errors or compilation errors occur
|
- Tests now also exit with an error code when OpenCL errors or compilation errors occur
|
||||||
|
- Added the OverrideParameters function to the API to be able to supply custom tuning parmeters
|
||||||
- Various minor fixes and enhancements
|
- Various minor fixes and enhancements
|
||||||
- Added tuned parameters for various devices (see README)
|
- Added tuned parameters for various devices (see README)
|
||||||
|
|
||||||
|
|
|
@ -414,6 +414,18 @@ if(TESTS)
|
||||||
add_test(clblast_test_${ROUTINE} clblast_test_${ROUTINE})
|
add_test(clblast_test_${ROUTINE} clblast_test_${ROUTINE})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
# Miscellaneous tests
|
||||||
|
set(MISC_TESTS override_parameters)
|
||||||
|
foreach(MISC_TEST ${MISC_TESTS})
|
||||||
|
add_executable(clblast_test_${MISC_TEST} ${TESTS_COMMON}
|
||||||
|
test/correctness/misc/${MISC_TEST}.cpp)
|
||||||
|
target_link_libraries(clblast_test_${MISC_TEST} clblast ${REF_LIBRARIES} ${OPENCL_LIBRARIES})
|
||||||
|
target_include_directories(clblast_test_${MISC_TEST} PUBLIC
|
||||||
|
$<TARGET_PROPERTY:clblast,INTERFACE_INCLUDE_DIRECTORIES>
|
||||||
|
${clblast_SOURCE_DIR} ${REF_INCLUDES})
|
||||||
|
add_test(clblast_test_${MISC_TEST} clblast_test_${MISC_TEST})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
# Adds 'alltests' target: runs all tests
|
# Adds 'alltests' target: runs all tests
|
||||||
set(ALLTESTS )
|
set(ALLTESTS )
|
||||||
set(ALLTESTSDEPENDS )
|
set(ALLTESTSDEPENDS )
|
||||||
|
|
|
@ -156,7 +156,7 @@ Note that CLBlast's tuners are based on the [CLTune auto-tuning library](https:/
|
||||||
|
|
||||||
Compiling with `-DTUNERS=ON` will generate a number of tuners, each named `clblast_tuner_xxxxx`, in which `xxxxx` corresponds to a `.opencl` kernel file as found in `src/kernels`. These kernels corresponds to routines (e.g. `xgemm`) or to common pre-processing or post-processing kernels (`copy` and `transpose`). Running such a tuner will test a number of parameter-value combinations on your device and report which one gave the best performance. Running `make alltuners` runs all tuners for all precisions in one go. You can set the default device and platform for `alltuners` by setting the `CLBLAST_DEVICE` and `CLBLAST_PLATFORM` environmental variables.
|
Compiling with `-DTUNERS=ON` will generate a number of tuners, each named `clblast_tuner_xxxxx`, in which `xxxxx` corresponds to a `.opencl` kernel file as found in `src/kernels`. These kernels corresponds to routines (e.g. `xgemm`) or to common pre-processing or post-processing kernels (`copy` and `transpose`). Running such a tuner will test a number of parameter-value combinations on your device and report which one gave the best performance. Running `make alltuners` runs all tuners for all precisions in one go. You can set the default device and platform for `alltuners` by setting the `CLBLAST_DEVICE` and `CLBLAST_PLATFORM` environmental variables.
|
||||||
|
|
||||||
The tuners output a JSON-file with the results. The best results need to be added to `src/database/kernels/xxxxx.hpp` in the appropriate section. However, this can be done automatically based on the JSON-data using a Python script in `scripts/database/database.py`. If you want the found parameters to be included in future releases of CLBlast, please attach the JSON files to the corresponding issue on GitHub or [email the main author](http://www.cedricnugteren.nl).
|
The tuners output a JSON-file with the results. The best results need to be added to `src/database/kernels/xxxxx.hpp` in the appropriate section. However, this can be done automatically based on the JSON-data using a Python (2.7 or 3.x) script in `scripts/database/database.py`. If you want the found parameters to be included in future releases of CLBlast, please attach the JSON files to the corresponding issue on GitHub or [email the main author](http://www.cedricnugteren.nl).
|
||||||
|
|
||||||
In summary, tuning the entire library for your device can be done as follows (starting from the root of the CLBlast folder):
|
In summary, tuning the entire library for your device can be done as follows (starting from the root of the CLBlast folder):
|
||||||
|
|
||||||
|
@ -168,6 +168,8 @@ In summary, tuning the entire library for your device can be done as follows (st
|
||||||
python ../scripts/database/database.py . ..
|
python ../scripts/database/database.py . ..
|
||||||
make
|
make
|
||||||
|
|
||||||
|
Alternatively, you can also supply your tuning parameters programmatically through the CLBlast API. This is especially useful if you tune for specific non-standard arguments (e.g. a rectangular or a very small matrix). To do so, you can call the `OverrideParameters` function which will set new parameters for a specific kernel. At the first next call of the target routine, CLBlast will compile a new binary and use it together with the new parameters from then on. Until `OverrideParameters` is called again of course. See the [API documentation](doc/clblast.md#overrideparameters-override-tuning-parameters-auxiliary-function) for more details.
|
||||||
|
|
||||||
|
|
||||||
Compiling the correctness tests (optional)
|
Compiling the correctness tests (optional)
|
||||||
-------------
|
-------------
|
||||||
|
|
|
@ -2781,3 +2781,66 @@ Requirements for OMATCOPY:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ClearCache: Resets the cache of compiled binaries (auxiliary function)
|
||||||
|
-------------
|
||||||
|
|
||||||
|
CLBlast stores binaries of compiled kernels into a cache in case the same kernel is used later on for the same device. This cache can be cleared to free up system memory or it can be useful in case of debugging.
|
||||||
|
|
||||||
|
C++ API:
|
||||||
|
```
|
||||||
|
StatusCode ClearCache()
|
||||||
|
```
|
||||||
|
|
||||||
|
C API:
|
||||||
|
```
|
||||||
|
CLBlastStatusCode CLBlastClearCache()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FillCache: Populates the cache of compiled binaries for a specific device (auxiliary function)
|
||||||
|
-------------
|
||||||
|
|
||||||
|
CLBlast stores binaries of compiled kernels into a cache in case the same kernel is used later on for the same device. This cache is automatically populated whenever a new binary is created. Thus, the first run of a specific kernel could take extra time. For debugging or performance evaluation purposes, it might be useful to populate the cache upfront. This function populates the cache for all kernels in CLBlast for all precisions, but for a specific device only.
|
||||||
|
|
||||||
|
C++ API:
|
||||||
|
```
|
||||||
|
StatusCode FillCache(const cl_device_id device)
|
||||||
|
```
|
||||||
|
|
||||||
|
C API:
|
||||||
|
```
|
||||||
|
CLBlastStatusCode CLBlastFillCache(const cl_device_id device)
|
||||||
|
```
|
||||||
|
|
||||||
|
Arguments to FillCache:
|
||||||
|
|
||||||
|
* `const cl_device_id device`: The OpenCL device to fill the cache for.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
OverrideParameters: Override tuning parameters (auxiliary function)
|
||||||
|
-------------
|
||||||
|
|
||||||
|
This function overrides tuning parameters for a specific device-precision-kernel combination. The next time the target routine is called it will be re-compiled and use the new parameters. All further times (until `OverrideParameters` is called again) it will load the kernel from the cache and thus continue to use the new parameters. Note that the first time after calling `OverrideParameters` a performance drop can be observable due to the re-compilation of the kernel.
|
||||||
|
|
||||||
|
C++ API:
|
||||||
|
```
|
||||||
|
StatusCode OverrideParameters(const cl_device_id device, const std::string &kernel_name,
|
||||||
|
const Precision precision,
|
||||||
|
const std::unordered_map<std::string,size_t> ¶meters)
|
||||||
|
```
|
||||||
|
|
||||||
|
C API:
|
||||||
|
```
|
||||||
|
CLBlastStatusCode CLBlastOverrideParameters(const cl_device_id device, const char* kernel_name,
|
||||||
|
const CLBlastPrecision precision, const size_t num_parameters,
|
||||||
|
const char** parameters_names, const size_t* parameters_values)
|
||||||
|
```
|
||||||
|
|
||||||
|
Arguments to OverrideParameters (C++ version):
|
||||||
|
|
||||||
|
* `const cl_device_id device`: The OpenCL device to set the new parameters for.
|
||||||
|
* `const std::string &kernel_name`: The target kernel name. This has to be one of the existing CLBlast kernels (Xaxpy, Xdot, Xgemv, XgemvFast, XgemvFastRot, Xgemv, Xger, Copy, Pad, Transpose, Padtranspose, Xgemm, or XgemmDirect). If this argument is incorrect, this function will return with the `clblast::kInvalidOverrideKernel` status-code.
|
||||||
|
* `const Precision precision`: The CLBlast precision enum to set the new parameters for.
|
||||||
|
* `const std::unordered_map<std::string,size_t> ¶meters`: An unordered map of strings to integers. This has to contain all the tuning parameters for a specific kernel as reported by the included tuners (e.g. `{ {"COPY_DIMX",8}, {"COPY_DIMY",32}, {"COPY_VW",4}, {"COPY_WPT",8} }` for the `Copy` kernel). If this argument is incorrect, this function will return with the `clblast::kMissingOverrideParameter` status-code.
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#define CLBLAST_CLBLAST_H_
|
#define CLBLAST_CLBLAST_H_
|
||||||
|
|
||||||
#include <cstdlib> // For size_t
|
#include <cstdlib> // For size_t
|
||||||
|
#include <string> // For OverrideParameters function
|
||||||
|
#include <unordered_map> // For OverrideParameters function
|
||||||
|
|
||||||
// Includes the normal OpenCL C header
|
// Includes the normal OpenCL C header
|
||||||
#if defined(__APPLE__) || defined(__MACOSX)
|
#if defined(__APPLE__) || defined(__MACOSX)
|
||||||
|
@ -95,6 +97,8 @@ enum class StatusCode {
|
||||||
kInsufficientMemoryY = -1007, // Vector Y's OpenCL buffer is too small
|
kInsufficientMemoryY = -1007, // Vector Y's OpenCL buffer is too small
|
||||||
|
|
||||||
// Custom additional status codes for CLBlast
|
// Custom additional status codes for CLBlast
|
||||||
|
kInvalidOverrideKernel = -2048, // Trying to override parameters for an invalid kernel
|
||||||
|
kMissingOverrideParameter = -2047, // Missing override parameter(s) for the target kernel
|
||||||
kInvalidLocalMemUsage = -2046, // Not enough local memory available on this device
|
kInvalidLocalMemUsage = -2046, // Not enough local memory available on this device
|
||||||
kNoHalfPrecision = -2045, // Half precision (16-bits) not supported by the device
|
kNoHalfPrecision = -2045, // Half precision (16-bits) not supported by the device
|
||||||
kNoDoublePrecision = -2044, // Double precision (64-bits) not supported by the device
|
kNoDoublePrecision = -2044, // Double precision (64-bits) not supported by the device
|
||||||
|
@ -617,6 +621,14 @@ StatusCode PUBLIC_API FillCache(const cl_device_id device);
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
|
// Overrides tuning parameters for a specific device-precision-kernel combination. The next time
|
||||||
|
// the target routine is called it will re-compile and use the new parameters from then on.
|
||||||
|
StatusCode PUBLIC_API OverrideParameters(const cl_device_id device, const std::string &kernel_name,
|
||||||
|
const Precision precision,
|
||||||
|
const std::unordered_map<std::string,size_t> ¶meters);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
} // namespace clblast
|
} // namespace clblast
|
||||||
|
|
||||||
// CLBLAST_CLBLAST_H_
|
// CLBLAST_CLBLAST_H_
|
||||||
|
|
|
@ -96,6 +96,8 @@ typedef enum CLBlastStatusCode_ {
|
||||||
CLBlastInsufficientMemoryY = -1007, // Vector Y's OpenCL buffer is too small
|
CLBlastInsufficientMemoryY = -1007, // Vector Y's OpenCL buffer is too small
|
||||||
|
|
||||||
// Custom additional status codes for CLBlast
|
// Custom additional status codes for CLBlast
|
||||||
|
CLBlastInvalidOverrideKernel = -2048, // Trying to override parameters for an invalid kernel
|
||||||
|
CLBlastMissingOverrideParameter = -2047, // Missing override parameter(s) for the target kernel
|
||||||
CLBlastInvalidLocalMemUsage = -2046, // Not enough local memory available on this device
|
CLBlastInvalidLocalMemUsage = -2046, // Not enough local memory available on this device
|
||||||
CLBlastNoHalfPrecision = -2045, // Half precision (16-bits) not supported by the device
|
CLBlastNoHalfPrecision = -2045, // Half precision (16-bits) not supported by the device
|
||||||
CLBlastNoDoublePrecision = -2044, // Double precision (64-bits) not supported by the device
|
CLBlastNoDoublePrecision = -2044, // Double precision (64-bits) not supported by the device
|
||||||
|
@ -117,6 +119,11 @@ typedef enum CLBlastDiagonal_ { CLBlastDiagonalNonUnit = 131,
|
||||||
CLBlastDiagonalUnit = 132 } CLBlastDiagonal;
|
CLBlastDiagonalUnit = 132 } CLBlastDiagonal;
|
||||||
typedef enum CLBlastSide_ { CLBlastSideLeft = 141, CLBlastSideRight = 142 } CLBlastSide;
|
typedef enum CLBlastSide_ { CLBlastSideLeft = 141, CLBlastSideRight = 142 } CLBlastSide;
|
||||||
|
|
||||||
|
// Precision enum (values in bits)
|
||||||
|
typedef enum CLBlastPrecision_ { CLBlastPrecisionHalf = 16, CLBlastPrecisionSingle = 32,
|
||||||
|
CLBlastPrecisionDouble = 64, CLBlastPrecisionComplexSingle = 3232,
|
||||||
|
CLBlastPrecisionComplexDouble = 6464 } CLBlastPrecision;
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
// BLAS level-1 (vector-vector) routines
|
// BLAS level-1 (vector-vector) routines
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
@ -1338,6 +1345,14 @@ CLBlastStatusCode PUBLIC_API CLBlastFillCache(const cl_device_id device);
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
|
// Overrides tuning parameters for a specific device-precision-kernel combination. The next time
|
||||||
|
// the target routine is called it will re-compile and use the new parameters from then on.
|
||||||
|
CLBlastStatusCode PUBLIC_API CLBlastOverrideParameters(const cl_device_id device, const char* kernel_name,
|
||||||
|
const CLBlastPrecision precision, const size_t num_parameters,
|
||||||
|
const char** parameters_names, const size_t* parameters_values);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,8 +41,10 @@ FILES = [
|
||||||
"/include/clblast_netlib_c.h",
|
"/include/clblast_netlib_c.h",
|
||||||
"/src/clblast_netlib_c.cpp",
|
"/src/clblast_netlib_c.cpp",
|
||||||
]
|
]
|
||||||
HEADER_LINES = [117, 73, 118, 22, 29, 41, 65, 32]
|
HEADER_LINES = [121, 73, 125, 23, 29, 41, 65, 32]
|
||||||
FOOTER_LINES = [17, 95, 19, 18, 6, 6, 9, 2]
|
FOOTER_LINES = [25, 139, 27, 38, 6, 6, 9, 2]
|
||||||
|
HEADER_LINES_DOC = 0
|
||||||
|
FOOTER_LINES_DOC = 63
|
||||||
|
|
||||||
# Different possibilities for requirements
|
# Different possibilities for requirements
|
||||||
ald_m = "The value of `a_ld` must be at least `m`."
|
ald_m = "The value of `a_ld` must be at least `m`."
|
||||||
|
@ -233,11 +235,20 @@ def main(argv):
|
||||||
f.write(cpp.performance_test(routine, level_string))
|
f.write(cpp.performance_test(routine, level_string))
|
||||||
f.write(cpp.FOOTER)
|
f.write(cpp.FOOTER)
|
||||||
|
|
||||||
# Outputs the API documentation
|
# API documentation
|
||||||
filename = cl_args.clblast_root + "/doc/clblast.md"
|
filename = cl_args.clblast_root + "/doc/clblast.md"
|
||||||
|
|
||||||
|
# Stores the header and the footer of the original documentation file
|
||||||
|
with open(filename) as f:
|
||||||
|
original = f.readlines()
|
||||||
|
file_header = original[:HEADER_LINES_DOC]
|
||||||
|
file_footer = original[-FOOTER_LINES_DOC:]
|
||||||
|
|
||||||
|
# Outputs the API documentation
|
||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
|
|
||||||
# Outputs the header
|
# Outputs the header
|
||||||
|
f.write("".join(file_header))
|
||||||
doc_header = doc.header()
|
doc_header = doc.header()
|
||||||
f.write(doc_header)
|
f.write(doc_header)
|
||||||
|
|
||||||
|
@ -248,5 +259,8 @@ def main(argv):
|
||||||
doc_routine = doc.generate(routine)
|
doc_routine = doc.generate(routine)
|
||||||
f.write(doc_routine)
|
f.write(doc_routine)
|
||||||
|
|
||||||
|
# Outputs the footer
|
||||||
|
f.write("".join(file_footer))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
|
@ -64,6 +64,37 @@ void Cache<Key, Value>::Store(Key &&key, Value &&value) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Value>
|
||||||
|
void Cache<Key, Value>::Remove(const Key &key) {
|
||||||
|
std::lock_guard<std::mutex> lock(cache_mutex_);
|
||||||
|
#if __cplusplus >= 201402L
|
||||||
|
cache_.erase(key);
|
||||||
|
#else
|
||||||
|
auto it = cache_.begin();
|
||||||
|
while (it != cache_.end()) {
|
||||||
|
if ((*it).first == key) {
|
||||||
|
it = cache_.erase(it);
|
||||||
|
}
|
||||||
|
else ++it;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Value>
|
||||||
|
template <int I1, int I2>
|
||||||
|
void Cache<Key, Value>::RemoveBySubset(const Key &key) {
|
||||||
|
std::lock_guard<std::mutex> lock(cache_mutex_);
|
||||||
|
auto it = cache_.begin();
|
||||||
|
while (it != cache_.end()) {
|
||||||
|
const auto current_key = (*it).first;
|
||||||
|
if ((std::get<I1>(key) == std::get<I1>(current_key)) &&
|
||||||
|
(std::get<I2>(key) == std::get<I2>(current_key))) {
|
||||||
|
it = cache_.erase(it);
|
||||||
|
}
|
||||||
|
else ++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Key, typename Value>
|
template <typename Key, typename Value>
|
||||||
void Cache<Key, Value>::Invalidate() {
|
void Cache<Key, Value>::Invalidate() {
|
||||||
std::lock_guard<std::mutex> lock(cache_mutex_);
|
std::lock_guard<std::mutex> lock(cache_mutex_);
|
||||||
|
@ -88,6 +119,7 @@ template std::string BinaryCache::Get(const BinaryKeyRef &, bool *) const;
|
||||||
|
|
||||||
template class Cache<ProgramKey, Program>;
|
template class Cache<ProgramKey, Program>;
|
||||||
template Program ProgramCache::Get(const ProgramKeyRef &, bool *) const;
|
template Program ProgramCache::Get(const ProgramKeyRef &, bool *) const;
|
||||||
|
template void ProgramCache::RemoveBySubset<1, 2>(const ProgramKey &); // precision and routine name
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@ public:
|
||||||
void Store(Key &&key, Value &&value);
|
void Store(Key &&key, Value &&value);
|
||||||
void Invalidate();
|
void Invalidate();
|
||||||
|
|
||||||
|
// Removes all entries with a given key
|
||||||
|
void Remove(const Key &key);
|
||||||
|
template <int I1, int I2> void RemoveBySubset(const Key &key); // currently supports 2 indices
|
||||||
|
|
||||||
static Cache<Key, Value> &Instance();
|
static Cache<Key, Value> &Instance();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -72,7 +76,6 @@ typedef Cache<BinaryKey, std::string> BinaryCache;
|
||||||
extern template class Cache<BinaryKey, std::string>;
|
extern template class Cache<BinaryKey, std::string>;
|
||||||
extern template std::string BinaryCache::Get(const BinaryKeyRef &, bool *) const;
|
extern template std::string BinaryCache::Get(const BinaryKeyRef &, bool *) const;
|
||||||
|
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
// The key struct for the cache of compiled OpenCL programs (context-dependent)
|
// The key struct for the cache of compiled OpenCL programs (context-dependent)
|
||||||
|
@ -90,9 +93,9 @@ extern template Program ProgramCache::Get(const ProgramKeyRef &, bool *) const;
|
||||||
class Database;
|
class Database;
|
||||||
|
|
||||||
// The key struct for the cache of database maps.
|
// The key struct for the cache of database maps.
|
||||||
// Order of fields: precision, device_name, routines (smaller fields first)
|
// Order of fields: precision, device_name, kernel_name (smaller fields first)
|
||||||
typedef std::tuple<Precision, std::string, std::vector<std::string>> DatabaseKey;
|
typedef std::tuple<Precision, std::string, std::string> DatabaseKey;
|
||||||
typedef std::tuple<const Precision &, const std::string &, const std::vector<std::string> &> DatabaseKeyRef;
|
typedef std::tuple<const Precision &, const std::string &, const std::string &> DatabaseKeyRef;
|
||||||
|
|
||||||
typedef Cache<DatabaseKey, Database> DatabaseCache;
|
typedef Cache<DatabaseKey, Database> DatabaseCache;
|
||||||
|
|
||||||
|
|
|
@ -2253,5 +2253,48 @@ StatusCode FillCache(const cl_device_id device) {
|
||||||
return StatusCode::kSuccess;
|
return StatusCode::kSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
|
// Overrides the tuning parameters for this device-precision-kernel combination
|
||||||
|
StatusCode OverrideParameters(const cl_device_id device, const std::string &kernel_name,
|
||||||
|
const Precision precision,
|
||||||
|
const std::unordered_map<std::string,size_t> ¶meters) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Retrieves the device name
|
||||||
|
const auto device_cpp = Device(device);
|
||||||
|
const auto device_name = device_cpp.Name();
|
||||||
|
|
||||||
|
// Retrieves the current database values to verify whether the new ones are complete
|
||||||
|
auto in_cache = false;
|
||||||
|
const auto current_database = DatabaseCache::Instance().Get(DatabaseKeyRef{ precision, device_name, kernel_name }, &in_cache);
|
||||||
|
if (!in_cache) { return StatusCode::kInvalidOverrideKernel; }
|
||||||
|
for (const auto ¤t_param : current_database.GetParameterNames()) {
|
||||||
|
if (parameters.find(current_param) == parameters.end()) {
|
||||||
|
return StatusCode::kMissingOverrideParameter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears the existing program & binary cache for routines with the target kernel
|
||||||
|
const auto routine_names = Routine::routines_by_kernel.at(kernel_name);
|
||||||
|
for (const auto &routine_name : routine_names) {
|
||||||
|
ProgramCache::Instance().RemoveBySubset<1, 2>(ProgramKey{nullptr, precision, routine_name});
|
||||||
|
BinaryCache::Instance().Remove(BinaryKey{precision, routine_name, device_name});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a small custom database based on the provided parameters
|
||||||
|
const auto database_device = Database::DatabaseDevice{"default", parameters};
|
||||||
|
const auto database_vendor = Database::DatabaseVendor{database::kDeviceTypeAll, "default", {database_device}};
|
||||||
|
const auto database_entry = Database::DatabaseEntry{kernel_name, precision, {database_vendor}};
|
||||||
|
const auto database = Database(device_cpp, kernel_name, precision, {&database_entry});
|
||||||
|
|
||||||
|
// Removes the old database entry and stores the new one in the cache
|
||||||
|
DatabaseCache::Instance().Remove(DatabaseKey{ precision, device_name, kernel_name });
|
||||||
|
DatabaseCache::Instance().Store(DatabaseKey{ precision, device_name, kernel_name }, Database(database));
|
||||||
|
|
||||||
|
} catch (...) { return DispatchException(); }
|
||||||
|
return StatusCode::kSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
} // namespace clblast
|
} // namespace clblast
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "utilities/utilities.hpp"
|
#include "utilities/utilities.hpp"
|
||||||
#include "clblast_c.h"
|
#include "clblast_c.h"
|
||||||
|
@ -3484,3 +3485,23 @@ CLBlastStatusCode CLBlastFillCache(const cl_device_id device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
|
// Overrides the tuning parameters for this device-precision-kernel combination
|
||||||
|
CLBlastStatusCode PUBLIC_API CLBlastOverrideParameters(const cl_device_id device, const char* kernel_name,
|
||||||
|
const CLBlastPrecision precision, const size_t num_parameters,
|
||||||
|
const char** parameters_names, const size_t* parameters_values) {
|
||||||
|
try {
|
||||||
|
const auto kernel_name_cpp = std::string(kernel_name);
|
||||||
|
const auto precision_cpp = static_cast<clblast::Precision>(precision);
|
||||||
|
auto parameters = std::unordered_map<std::string, size_t>();
|
||||||
|
for (auto i = size_t{0}; i < num_parameters; ++i) {
|
||||||
|
const auto parameter_name = std::string(parameters_names[i]);
|
||||||
|
const auto parameter_value = parameters_values[i];
|
||||||
|
parameters[parameter_name] = parameter_value;
|
||||||
|
}
|
||||||
|
const auto status = clblast::OverrideParameters(device, kernel_name_cpp, precision_cpp, parameters);
|
||||||
|
return static_cast<CLBlastStatusCode>(status);
|
||||||
|
} catch (...) { return static_cast<CLBlastStatusCode>(clblast::DispatchExceptionForC()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
|
|
@ -63,7 +63,7 @@ const std::unordered_map<std::string, std::string> Database::kVendorNames{
|
||||||
|
|
||||||
// Constructor, computing device properties and populating the parameter-vector from the database.
|
// Constructor, computing device properties and populating the parameter-vector from the database.
|
||||||
// This takes an optional overlay database in case of custom tuning or custom kernels.
|
// This takes an optional overlay database in case of custom tuning or custom kernels.
|
||||||
Database::Database(const Device &device, const std::vector<std::string> &kernels,
|
Database::Database(const Device &device, const std::string &kernel_name,
|
||||||
const Precision precision, const std::vector<const DatabaseEntry*> &overlay):
|
const Precision precision, const std::vector<const DatabaseEntry*> &overlay):
|
||||||
parameters_(std::make_shared<Parameters>()) {
|
parameters_(std::make_shared<Parameters>()) {
|
||||||
|
|
||||||
|
@ -79,12 +79,10 @@ Database::Database(const Device &device, const std::vector<std::string> &kernels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterates over all kernels to include, and retrieves the parameters for each of them
|
// Searches potentially multiple databases
|
||||||
for (auto &kernel: kernels) {
|
|
||||||
auto search_result = ParametersPtr{};
|
auto search_result = ParametersPtr{};
|
||||||
|
for (auto &db: { overlay, database}) {
|
||||||
for (auto &db: { database, overlay}) {
|
search_result = Search(kernel_name, device_type, device_vendor, device_name, precision, db);
|
||||||
search_result = Search(kernel, device_type, device_vendor, device_name, precision, db);
|
|
||||||
if (search_result) {
|
if (search_result) {
|
||||||
parameters_->insert(search_result->begin(), search_result->end());
|
parameters_->insert(search_result->begin(), search_result->end());
|
||||||
break;
|
break;
|
||||||
|
@ -93,7 +91,6 @@ Database::Database(const Device &device, const std::vector<std::string> &kernels
|
||||||
|
|
||||||
if (!search_result) { throw RuntimeErrorCode(StatusCode::kDatabaseError); }
|
if (!search_result) { throw RuntimeErrorCode(StatusCode::kDatabaseError); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
|
@ -106,6 +103,15 @@ std::string Database::GetDefines() const {
|
||||||
return defines;
|
return defines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieves the names of all the parameters
|
||||||
|
std::vector<std::string> Database::GetParameterNames() const {
|
||||||
|
auto parameter_names = std::vector<std::string>();
|
||||||
|
for (auto ¶meter: *parameters_) {
|
||||||
|
parameter_names.push_back(parameter.first);
|
||||||
|
}
|
||||||
|
return parameter_names;
|
||||||
|
}
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
// Searches a particular database for the right kernel and precision
|
// Searches a particular database for the right kernel and precision
|
||||||
|
|
|
@ -75,15 +75,19 @@ class Database {
|
||||||
Database() = default;
|
Database() = default;
|
||||||
|
|
||||||
// The constructor with a user-provided database overlay (potentially an empty vector)
|
// The constructor with a user-provided database overlay (potentially an empty vector)
|
||||||
explicit Database(const Device &device, const std::vector<std::string> &routines,
|
explicit Database(const Device &device, const std::string &kernel_name,
|
||||||
const Precision precision, const std::vector<const DatabaseEntry*> &overlay);
|
const Precision precision, const std::vector<const DatabaseEntry*> &overlay);
|
||||||
|
|
||||||
// Accessor of values by key
|
// Accessor of values by key
|
||||||
size_t operator[](const std::string key) const { return parameters_->find(key)->second; }
|
size_t operator[](const std::string &key) const { return parameters_->find(key)->second; }
|
||||||
|
bool exists(const std::string &key) const { return (parameters_->count(key) == 1); }
|
||||||
|
|
||||||
// Obtain a list of OpenCL pre-processor defines based on the parameters
|
// Obtain a list of OpenCL pre-processor defines based on the parameters
|
||||||
std::string GetDefines() const;
|
std::string GetDefines() const;
|
||||||
|
|
||||||
|
// Retrieves the names of all the parameters
|
||||||
|
std::vector<std::string> GetParameterNames() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Search method for a specified database, returning pointer (possibly a nullptr)
|
// Search method for a specified database, returning pointer (possibly a nullptr)
|
||||||
ParametersPtr Search(const std::string &this_kernel, const std::string &this_type,
|
ParametersPtr Search(const std::string &this_kernel, const std::string &this_type,
|
||||||
|
@ -95,6 +99,31 @@ class Database {
|
||||||
std::shared_ptr<Parameters> parameters_;
|
std::shared_ptr<Parameters> parameters_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
|
// Multiple databases together in a map
|
||||||
|
class Databases {
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit Databases(const std::vector<std::string> &kernel_names): kernel_names_(kernel_names) { }
|
||||||
|
|
||||||
|
// Database accessor
|
||||||
|
Database& operator()(const std::string &kernel_name) { return databases_[kernel_name]; }
|
||||||
|
|
||||||
|
// Retrieves a parameter from the database
|
||||||
|
size_t operator[](const std::string &key) const {
|
||||||
|
for (const auto &kernel_name : kernel_names_) {
|
||||||
|
const auto &kernel_db = databases_.find(kernel_name)->second;
|
||||||
|
if (kernel_db.exists(key)) { return kernel_db[key]; }
|
||||||
|
}
|
||||||
|
throw RuntimeErrorCode(StatusCode::kDatabaseError);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::vector<std::string> kernel_names_;
|
||||||
|
std::unordered_map<std::string, Database> databases_;
|
||||||
|
};
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
} // namespace clblast
|
} // namespace clblast
|
||||||
|
|
||||||
|
|
|
@ -21,36 +21,64 @@
|
||||||
namespace clblast {
|
namespace clblast {
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
|
// For each kernel this map contains a list of routines it is used in
|
||||||
|
const std::vector<std::string> Routine::routines_axpy = {"AXPY", "COPY", "SCAL", "SWAP"};
|
||||||
|
const std::vector<std::string> Routine::routines_dot = {"AMAX", "ASUM", "DOT", "DOTC", "DOTU", "MAX", "MIN", "NRM2", "SUM"};
|
||||||
|
const std::vector<std::string> Routine::routines_ger = {"GER", "GERC", "GERU", "HER", "HER2", "HPR", "HPR2", "SPR", "SPR2", "SYR", "SYR2"};
|
||||||
|
const std::vector<std::string> Routine::routines_gemv = {"GBMV", "GEMV", "HBMV", "HEMV", "HPMV", "SBMV", "SPMV", "SYMV", "TMBV", "TPMV", "TRMV"};
|
||||||
|
const std::vector<std::string> Routine::routines_gemm = {"GEMM", "HEMM", "SYMM", "TRMM"};
|
||||||
|
const std::vector<std::string> Routine::routines_gemm_syrk = {"GEMM", "HEMM", "HER2K", "HERK", "SYMM", "SYR2K", "SYRK", "TRMM"};
|
||||||
|
const std::unordered_map<std::string, const std::vector<std::string>> Routine::routines_by_kernel = {
|
||||||
|
{"Xaxpy", routines_axpy},
|
||||||
|
{"Xdot", routines_dot},
|
||||||
|
{"Xgemv", routines_gemv},
|
||||||
|
{"XgemvFast", routines_gemv},
|
||||||
|
{"XgemvFastRot", routines_gemv},
|
||||||
|
{"Xgemv", {}},
|
||||||
|
{"Xger", routines_ger},
|
||||||
|
{"Copy", routines_gemm_syrk},
|
||||||
|
{"Pad", routines_gemm_syrk},
|
||||||
|
{"Transpose", routines_gemm_syrk},
|
||||||
|
{"Padtranspose", routines_gemm_syrk},
|
||||||
|
{"Xgemm", routines_gemm_syrk},
|
||||||
|
{"XgemmDirect", routines_gemm},
|
||||||
|
{"KernelSelection", routines_gemm},
|
||||||
|
};
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
// The constructor does all heavy work, errors are returned as exceptions
|
// The constructor does all heavy work, errors are returned as exceptions
|
||||||
Routine::Routine(Queue &queue, EventPointer event, const std::string &name,
|
Routine::Routine(Queue &queue, EventPointer event, const std::string &name,
|
||||||
const std::vector<std::string> &routines, const Precision precision,
|
const std::vector<std::string> &kernel_names, const Precision precision,
|
||||||
const std::vector<const Database::DatabaseEntry*> &userDatabase,
|
const std::vector<const Database::DatabaseEntry*> &userDatabase,
|
||||||
std::initializer_list<const char *> source):
|
std::initializer_list<const char *> source):
|
||||||
precision_(precision),
|
precision_(precision),
|
||||||
routine_name_(name),
|
routine_name_(name),
|
||||||
|
kernel_names_(kernel_names),
|
||||||
queue_(queue),
|
queue_(queue),
|
||||||
event_(event),
|
event_(event),
|
||||||
context_(queue_.GetContext()),
|
context_(queue_.GetContext()),
|
||||||
device_(queue_.GetDevice()),
|
device_(queue_.GetDevice()),
|
||||||
device_name_(device_.Name()) {
|
device_name_(device_.Name()),
|
||||||
|
db_(kernel_names) {
|
||||||
|
|
||||||
InitDatabase(routines, userDatabase);
|
InitDatabase(userDatabase);
|
||||||
InitProgram(source);
|
InitProgram(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Routine::InitDatabase(const std::vector<std::string> &routines,
|
void Routine::InitDatabase(const std::vector<const Database::DatabaseEntry*> &userDatabase) {
|
||||||
const std::vector<const Database::DatabaseEntry*> &userDatabase) {
|
for (const auto &kernel_name : kernel_names_) {
|
||||||
|
|
||||||
// Queries the cache to see whether or not the kernel parameter database is already there
|
// Queries the cache to see whether or not the kernel parameter database is already there
|
||||||
bool has_db;
|
bool has_db;
|
||||||
db_ = DatabaseCache::Instance().Get(DatabaseKeyRef{ precision_, device_name_, routines },
|
db_(kernel_name) = DatabaseCache::Instance().Get(DatabaseKeyRef{ precision_, device_name_, kernel_name },
|
||||||
&has_db);
|
&has_db);
|
||||||
if (has_db) { return; }
|
if (has_db) { continue; }
|
||||||
|
|
||||||
// Builds the parameter database for this device and routine set and stores it in the cache
|
// Builds the parameter database for this device and routine set and stores it in the cache
|
||||||
db_ = Database(device_, routines, precision_, userDatabase);
|
db_(kernel_name) = Database(device_, kernel_name, precision_, userDatabase);
|
||||||
DatabaseCache::Instance().Store(DatabaseKey{ precision_, device_name_, routines },
|
DatabaseCache::Instance().Store(DatabaseKey{ precision_, device_name_, kernel_name },
|
||||||
Database{ db_ });
|
Database{ db_(kernel_name) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Routine::InitProgram(std::initializer_list<const char *> source) {
|
void Routine::InitProgram(std::initializer_list<const char *> source) {
|
||||||
|
@ -96,7 +124,10 @@ void Routine::InitProgram(std::initializer_list<const char *> source) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collects the parameters for this device in the form of defines, and adds the precision
|
// Collects the parameters for this device in the form of defines, and adds the precision
|
||||||
auto source_string = db_.GetDefines();
|
auto source_string = std::string{""};
|
||||||
|
for (const auto &kernel_name : kernel_names_) {
|
||||||
|
source_string += db_(kernel_name).GetDefines();
|
||||||
|
}
|
||||||
source_string += "#define PRECISION "+ToString(static_cast<int>(precision_))+"\n";
|
source_string += "#define PRECISION "+ToString(static_cast<int>(precision_))+"\n";
|
||||||
|
|
||||||
// Adds the name of the routine as a define
|
// Adds the name of the routine as a define
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "utilities/utilities.hpp"
|
#include "utilities/utilities.hpp"
|
||||||
#include "cache.hpp"
|
#include "cache.hpp"
|
||||||
|
@ -42,22 +43,31 @@ class Routine {
|
||||||
const std::vector<const Database::DatabaseEntry*> &userDatabase,
|
const std::vector<const Database::DatabaseEntry*> &userDatabase,
|
||||||
std::initializer_list<const char *> source);
|
std::initializer_list<const char *> source);
|
||||||
|
|
||||||
|
// List of kernel-routine look-ups
|
||||||
|
static const std::vector<std::string> routines_axpy;
|
||||||
|
static const std::vector<std::string> routines_dot;
|
||||||
|
static const std::vector<std::string> routines_ger;
|
||||||
|
static const std::vector<std::string> routines_gemv;
|
||||||
|
static const std::vector<std::string> routines_gemm;
|
||||||
|
static const std::vector<std::string> routines_gemm_syrk;
|
||||||
|
static const std::unordered_map<std::string, const std::vector<std::string>> routines_by_kernel;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Initializes program_, fetching cached program or building one
|
// Initializes program_, fetching cached program or building one
|
||||||
void InitProgram(std::initializer_list<const char *> source);
|
void InitProgram(std::initializer_list<const char *> source);
|
||||||
|
|
||||||
// Initializes db_, fetching cached database or building one
|
// Initializes db_, fetching cached database or building one
|
||||||
void InitDatabase(const std::vector<std::string> &routines,
|
void InitDatabase(const std::vector<const Database::DatabaseEntry*> &userDatabase);
|
||||||
const std::vector<const Database::DatabaseEntry*> &userDatabase);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// Non-static variable for the precision
|
// Non-static variable for the precision
|
||||||
const Precision precision_;
|
const Precision precision_;
|
||||||
|
|
||||||
// The routine's name
|
// The routine's name and the corresponding kernels
|
||||||
const std::string routine_name_;
|
const std::string routine_name_;
|
||||||
|
const std::vector<std::string> kernel_names_;
|
||||||
|
|
||||||
// The OpenCL objects, accessible only from derived classes
|
// The OpenCL objects, accessible only from derived classes
|
||||||
Queue queue_;
|
Queue queue_;
|
||||||
|
@ -72,7 +82,7 @@ class Routine {
|
||||||
Program program_;
|
Program program_;
|
||||||
|
|
||||||
// Connection to the database for all the device-specific parameters
|
// Connection to the database for all the device-specific parameters
|
||||||
Database db_;
|
Databases db_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
|
@ -37,7 +37,7 @@ void RunKernel(Kernel &kernel, Queue &queue, const Device &device,
|
||||||
// to write to symmetric and triangular matrices through optional arguments.
|
// to write to symmetric and triangular matrices through optional arguments.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void PadCopyTransposeMatrix(Queue &queue, const Device &device,
|
void PadCopyTransposeMatrix(Queue &queue, const Device &device,
|
||||||
const Database &db,
|
const Databases &db,
|
||||||
EventPointer event, const std::vector<Event> &waitForEvents,
|
EventPointer event, const std::vector<Event> &waitForEvents,
|
||||||
const size_t src_one, const size_t src_two,
|
const size_t src_one, const size_t src_two,
|
||||||
const size_t src_ld, const size_t src_offset,
|
const size_t src_ld, const size_t src_offset,
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace clblast {
|
||||||
// Constructor: forwards to base class constructor
|
// Constructor: forwards to base class constructor
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Xgemv<T>::Xgemv(Queue &queue, EventPointer event, const std::string &name):
|
Xgemv<T>::Xgemv(Queue &queue, EventPointer event, const std::string &name):
|
||||||
Routine(queue, event, name, {"Pad", "Xgemv", "XgemvFast", "XgemvFastRot"}, PrecisionValue<T>(), {}, {
|
Routine(queue, event, name, {"Xgemv", "XgemvFast", "XgemvFastRot"}, PrecisionValue<T>(), {}, {
|
||||||
#include "../../kernels/level2/xgemv.opencl"
|
#include "../../kernels/level2/xgemv.opencl"
|
||||||
#include "../../kernels/level2/xgemv_fast.opencl"
|
#include "../../kernels/level2/xgemv_fast.opencl"
|
||||||
}) {
|
}) {
|
||||||
|
|
139
test/correctness/misc/override_parameters.cpp
Normal file
139
test/correctness/misc/override_parameters.cpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// 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 contains the tests for the OverrideParameters function
|
||||||
|
//
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
|
#include "utilities/utilities.hpp"
|
||||||
|
#include "test/routines/level3/xgemm.hpp"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace clblast {
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
size_t RunOverrideTests(int argc, char *argv[], const bool silent, const std::string &routine_name) {
|
||||||
|
auto arguments = RetrieveCommandLineArguments(argc, argv);
|
||||||
|
auto errors = size_t{0};
|
||||||
|
auto passed = size_t{0};
|
||||||
|
auto example_routine = TestXgemm<T>();
|
||||||
|
constexpr auto kSeed = 42; // fixed seed for reproducibility
|
||||||
|
|
||||||
|
// Determines the test settings
|
||||||
|
const auto kernel_name = std::string{"Xgemm"};
|
||||||
|
const auto precision = PrecisionValue<T>();
|
||||||
|
const auto valid_settings = std::vector<std::unordered_map<std::string,size_t>>{
|
||||||
|
{ {"KWG",16}, {"KWI",2}, {"MDIMA",4}, {"MDIMC",4}, {"MWG",16}, {"NDIMB",4}, {"NDIMC",4}, {"NWG",16}, {"SA",0}, {"SB",0}, {"STRM",0}, {"STRN",0}, {"VWM",1}, {"VWN",1} },
|
||||||
|
{ {"KWG",32}, {"KWI",2}, {"MDIMA",4}, {"MDIMC",4}, {"MWG",32}, {"NDIMB",4}, {"NDIMC",4}, {"NWG",32}, {"SA",0}, {"SB",0}, {"STRM",0}, {"STRN",0}, {"VWM",1}, {"VWN",1} },
|
||||||
|
};
|
||||||
|
const auto invalid_settings = std::vector<std::unordered_map<std::string,size_t>>{
|
||||||
|
{ {"KWI",2}, {"MDIMA",4}, {"MDIMC",4}, {"MWG",16}, {"NDIMB",4}, {"NDIMC",4}, {"NWG",16}, {"SA",0} },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieves the arguments
|
||||||
|
auto help = std::string{"Options given/available:\n"};
|
||||||
|
const auto platform_id = GetArgument(arguments, help, kArgPlatform, ConvertArgument(std::getenv("CLBLAST_PLATFORM"), size_t{0}));
|
||||||
|
const auto device_id = GetArgument(arguments, help, kArgDevice, ConvertArgument(std::getenv("CLBLAST_DEVICE"), size_t{0}));
|
||||||
|
auto args = Arguments<T>{};
|
||||||
|
args.m = GetArgument(arguments, help, kArgM, size_t{256});
|
||||||
|
args.n = GetArgument(arguments, help, kArgN, size_t{256});
|
||||||
|
args.k = GetArgument(arguments, help, kArgK, size_t{256});
|
||||||
|
args.a_ld = GetArgument(arguments, help, kArgALeadDim, args.k);
|
||||||
|
args.b_ld = GetArgument(arguments, help, kArgBLeadDim, args.n);
|
||||||
|
args.c_ld = GetArgument(arguments, help, kArgCLeadDim, args.n);
|
||||||
|
args.a_offset = GetArgument(arguments, help, kArgAOffset, size_t{0});
|
||||||
|
args.b_offset = GetArgument(arguments, help, kArgBOffset, size_t{0});
|
||||||
|
args.c_offset = GetArgument(arguments, help, kArgCOffset, size_t{0});
|
||||||
|
args.layout = GetArgument(arguments, help, kArgLayout, Layout::kRowMajor);
|
||||||
|
args.a_transpose = GetArgument(arguments, help, kArgATransp, Transpose::kNo);
|
||||||
|
args.b_transpose = GetArgument(arguments, help, kArgBTransp, Transpose::kNo);
|
||||||
|
args.alpha = GetArgument(arguments, help, kArgAlpha, GetScalar<T>());
|
||||||
|
args.beta = GetArgument(arguments, help, kArgBeta, GetScalar<T>());
|
||||||
|
|
||||||
|
// Prints the help message (command-line arguments)
|
||||||
|
if (!silent) { fprintf(stdout, "\n* %s\n", help.c_str()); }
|
||||||
|
|
||||||
|
// Initializes OpenCL
|
||||||
|
const auto platform = Platform(platform_id);
|
||||||
|
const auto device = Device(platform, device_id);
|
||||||
|
const auto context = Context(device);
|
||||||
|
auto queue = Queue(context, device);
|
||||||
|
|
||||||
|
// Populate host matrices with some example data
|
||||||
|
auto host_a = std::vector<T>(args.m * args.k);
|
||||||
|
auto host_b = std::vector<T>(args.n * args.k);
|
||||||
|
auto host_c = std::vector<T>(args.m * args.n);
|
||||||
|
PopulateVector(host_a, kSeed);
|
||||||
|
PopulateVector(host_b, kSeed);
|
||||||
|
PopulateVector(host_c, kSeed);
|
||||||
|
|
||||||
|
// Copy the matrices to the device
|
||||||
|
auto device_a = Buffer<T>(context, host_a.size());
|
||||||
|
auto device_b = Buffer<T>(context, host_b.size());
|
||||||
|
auto device_c = Buffer<T>(context, host_c.size());
|
||||||
|
device_a.Write(queue, host_a.size(), host_a);
|
||||||
|
device_b.Write(queue, host_b.size(), host_b);
|
||||||
|
device_c.Write(queue, host_c.size(), host_c);
|
||||||
|
auto dummy = Buffer<T>(context, 1);
|
||||||
|
auto buffers = Buffers<T>{dummy, dummy, device_a, device_b, device_c, dummy, dummy};
|
||||||
|
|
||||||
|
// Loops over the valid combinations: run before and run afterwards
|
||||||
|
fprintf(stdout, "* Testing OverrideParameters for '%s'\n", routine_name.c_str());
|
||||||
|
for (const auto &override_setting : valid_settings) {
|
||||||
|
const auto status_before = example_routine.RunRoutine(args, buffers, queue);
|
||||||
|
if (status_before != StatusCode::kSuccess) { errors++; continue; }
|
||||||
|
|
||||||
|
// Overrides the parameters
|
||||||
|
const auto status = OverrideParameters(device(), kernel_name, precision, override_setting);
|
||||||
|
if (status != StatusCode::kSuccess) { errors++; continue; } // error shouldn't occur
|
||||||
|
|
||||||
|
const auto status_after = example_routine.RunRoutine(args, buffers, queue);
|
||||||
|
if (status_after != StatusCode::kSuccess) { errors++; continue; }
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loops over the invalid combinations: run before and run afterwards
|
||||||
|
for (const auto &override_setting : invalid_settings) {
|
||||||
|
const auto status_before = example_routine.RunRoutine(args, buffers, queue);
|
||||||
|
if (status_before != StatusCode::kSuccess) { errors++; continue; }
|
||||||
|
|
||||||
|
// Overrides the parameters
|
||||||
|
const auto status = OverrideParameters(device(), kernel_name, precision, override_setting);
|
||||||
|
if (status == StatusCode::kSuccess) { errors++; continue; } // error should occur
|
||||||
|
|
||||||
|
const auto status_after = example_routine.RunRoutine(args, buffers, queue);
|
||||||
|
if (status_after != StatusCode::kSuccess) { errors++; continue; }
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints and returns the statistics
|
||||||
|
fprintf(stdout, " %zu test(s) passed\n", passed);
|
||||||
|
fprintf(stdout, " %zu test(s) failed\n", errors);
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
} // namespace clblast
|
||||||
|
|
||||||
|
// Shortcuts to the clblast namespace
|
||||||
|
using float2 = clblast::float2;
|
||||||
|
using double2 = clblast::double2;
|
||||||
|
|
||||||
|
// Main function (not within the clblast namespace)
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
auto errors = size_t{0};
|
||||||
|
errors += clblast::RunOverrideTests<float>(argc, argv, false, "SGEMM");
|
||||||
|
errors += clblast::RunOverrideTests<float2>(argc, argv, true, "CGEMM");
|
||||||
|
if (errors > 0) { return 1; } else { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
Loading…
Reference in a new issue