diff --git a/CMakeLists.txt b/CMakeLists.txt index b204310fdd..3ffe04c032 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,10 @@ ELSE(FREEIMAGE_FOUND) MESSAGE(WARNING, "FreeImage not found!") ENDIF(FREEIMAGE_FOUND) +ADD_DEFINITIONS(-DBOOST_ALL_NO_LIB) +SET(Boost_USE_STATIC_LIBS OFF) +FIND_PACKAGE(Boost REQUIRED) + OPTION(USE_SYSTEM_CL2HPP "Use cl2.hpp installed on system" OFF) IF(BUILD_GRAPHICS) @@ -128,10 +132,12 @@ INCLUDE_DIRECTORIES(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/src/backend" "${CMAKE_CURRENT_SOURCE_DIR}/src/api/c" + "${Boost_INCLUDE_DIR}" ) IF(${UNIX}) - ADD_DEFINITIONS(-Wall -std=c++11 -fvisibility=hidden) + ADD_DEFINITIONS(-std=c++11 -Wall -fvisibility=hidden) + IF(${WITH_COVERAGE}) SET(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage") SET(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage") diff --git a/CMakeModules/build_glbinding.cmake b/CMakeModules/build_glbinding.cmake index c929778d92..05ad586e76 100644 --- a/CMakeModules/build_glbinding.cmake +++ b/CMakeModules/build_glbinding.cmake @@ -3,7 +3,7 @@ INCLUDE(ExternalProject) SET(prefix ${PROJECT_BINARY_DIR}/third_party/glb) SET(LIB_POSTFIX "") -IF (${CMAKE_BUILD_TYPE} STREQUAL "Debug") +IF (CMAKE_BUILD_TYPE MATCHES Debug) SET(LIB_POSTFIX "d") ENDIF() diff --git a/docs/details/algorithm.dox b/docs/details/algorithm.dox index 27ef55ef48..6e6f3b8cde 100644 --- a/docs/details/algorithm.dox +++ b/docs/details/algorithm.dox @@ -107,7 +107,7 @@ Return type is u32 for all input types \ingroup scan_mat -Perform exclusive sum along specified dimension +Perform inclusive sum along specified dimension This table defines the return value types for the corresponding input types diff --git a/include/af/algorithm.h b/include/af/algorithm.h index 39d948fc20..737ef1a3d3 100644 --- a/include/af/algorithm.h +++ b/include/af/algorithm.h @@ -306,7 +306,7 @@ namespace af template void max(T *val, unsigned *idx, const array &in); /** - C++ Interface exclusive sum (cumulative sum) of an array + C++ Interface inclusive sum (cumulative sum) of an array \param[in] in is the input array \param[in] dim The dimension along which exclusive sum is performed @@ -761,7 +761,7 @@ extern "C" { AFAPI af_err af_imax_all(double *real, double *imag, unsigned *idx, const af_array in); /** - C Interface exclusive sum (cumulative sum) of an array + C Interface inclusive sum (cumulative sum) of an array \param[out] out will contain exclusive sums of the input \param[in] in is the input array diff --git a/include/af/signal.h b/include/af/signal.h index ffa0c108af..fa04bc8dbb 100644 --- a/include/af/signal.h +++ b/include/af/signal.h @@ -654,6 +654,18 @@ AFAPI array medfilt1(const array& in, const dim_t wind_width = 3, const borderTy AFAPI array medfilt2(const array& in, const dim_t wind_length = 3, const dim_t wind_width = 3, const borderType edge_pad = AF_PAD_ZERO); #endif +#if AF_API_VERSION >= 35 +/** + C++ Interface for setting plan cache size + + This function doesn't do anything if called when CPU backend is active. The plans associated with + the most recently used array sizes are cached. + + \param[in] cacheSize is the number of plans that shall be cached +*/ +AFAPI void setFFTPlanCacheSize(size_t cacheSize); +#endif + } #endif @@ -1190,6 +1202,8 @@ AFAPI af_err af_iir(af_array *y, const af_array b, const af_array a, const af_ar the most recently used array sizes are cached. \param[in] cache_size is the number of plans that shall be cached + + \ingroup signal_func_fft */ AFAPI af_err af_set_fft_plan_cache_size(size_t cache_size); #endif diff --git a/src/api/c/graphics_common.cpp b/src/api/c/graphics_common.cpp index 725aac19a8..249a325586 100644 --- a/src/api/c/graphics_common.cpp +++ b/src/api/c/graphics_common.cpp @@ -10,13 +10,12 @@ #if defined(WITH_GRAPHICS) #include +#include #include #include #include #include - -#include -#include +#include using namespace std; using namespace gl; @@ -54,7 +53,7 @@ INSTANTIATE_GET_FG_TYPE(short , forge::s16); gl::GLenum glErrorSkip(const char *msg, const char* file, int line) { #ifndef NDEBUG - gl::GLenum x = glGetError(); + gl::GLenum x = gl::glGetError(); if (x != GL_NO_ERROR) { char buf[1024]; sprintf(buf, "GL Error Skipped at: %s:%d Message: %s Error Code: %d \"%s\"\n", file, line, msg, (int)x, glbinding::Meta::getString(x).c_str()); @@ -70,7 +69,7 @@ gl::GLenum glErrorCheck(const char *msg, const char* file, int line) { // Skipped in release mode #ifndef NDEBUG - gl::GLenum x = glGetError(); + gl::GLenum x = gl::glGetError(); if (x != GL_NO_ERROR) { char buf[1024]; @@ -85,7 +84,7 @@ gl::GLenum glErrorCheck(const char *msg, const char* file, int line) gl::GLenum glForceErrorCheck(const char *msg, const char* file, int line) { - gl::GLenum x = glGetError(); + gl::GLenum x = gl::glGetError(); if (x != GL_NO_ERROR) { char buf[1024]; @@ -173,7 +172,6 @@ double step_round(const double in, const bool dir) namespace graphics { - ForgeManager& ForgeManager::getInstance() { static ForgeManager my_instance; @@ -182,50 +180,66 @@ ForgeManager& ForgeManager::getInstance() ForgeManager::~ForgeManager() { - destroyResources(); + /* clear all OpenGL resource objects (images, plots, histograms etc) first + * and then delete the windows */ + for(ImgMapIter iter = mImgMap.begin(); iter != mImgMap.end(); iter++) + delete (iter->second); + + for(PltMapIter iter = mPltMap.begin(); iter != mPltMap.end(); iter++) + delete (iter->second); + + for(HstMapIter iter = mHstMap.begin(); iter != mHstMap.end(); iter++) + delete (iter->second); + + for(ChartMapIter iter = mChartMap.begin(); iter != mChartMap.end(); iter++) { + for(int i = 0; i < (int)(iter->second).size(); i++) { + if((iter->second)[i] != NULL) { + delete (iter->second)[i]; + mChartAxesOverrideMap.erase((iter->second)[i]); + } + } + } } -forge::Font* ForgeManager::getFont(const bool dontCreate) +forge::Font* ForgeManager::getFont() { - static bool flag = true; - static forge::Font* fnt = NULL; + static std::once_flag flag; + static std::unique_ptr fnt; CheckGL("Begin ForgeManager::getFont"); - - if (flag && !dontCreate) { - fnt = new forge::Font(); + std::call_once(flag, + [] { + fnt.reset(new forge::Font()); #if defined(_WIN32) || defined(_MSC_VER) - fnt->loadSystemFont("Arial"); + fnt->loadSystemFont("Arial"); #else - fnt->loadSystemFont("Vera"); + fnt->loadSystemFont("Vera"); #endif - CheckGL("End ForgeManager::getFont"); - flag = false; - }; + }); + CheckGL("End ForgeManager::getFont"); - return fnt; + return fnt.get(); } -forge::Window* ForgeManager::getMainWindow(const bool dontCreate) +forge::Window* ForgeManager::getMainWindow() { - static bool flag = true; - static forge::Window* wnd = NULL; + static std::once_flag flag; + static std::unique_ptr wnd; // Define AF_DISABLE_GRAPHICS with any value to disable initialization std::string noGraphicsENV = getEnvVar("AF_DISABLE_GRAPHICS"); - if(noGraphicsENV.empty()) { // If AF_DISABLE_GRAPHICS is not defined - if (flag && !dontCreate) { - wnd = new forge::Window(WIDTH, HEIGHT, "ArrayFire", NULL, true); - makeContextCurrent(wnd); - ForgeManager& fgMngr = ForgeManager::getInstance(); - fgMngr.setWindowChartGrid(wnd, 1, 1); + if (noGraphicsENV.empty()) { // If AF_DISABLE_GRAPHICS is not defined + std::call_once(flag, + [] { + wnd.reset(new forge::Window(WIDTH, HEIGHT, "ArrayFire", NULL, true)); + makeContextCurrent(wnd.get()); - CheckGL("End ForgeManager::getMainWindow"); - flag = false; - }; + ForgeManager::getInstance().setWindowChartGrid(wnd.get(), 1, 1); + }); } - return wnd; + + return wnd.get(); } void ForgeManager::setWindowChartGrid(const forge::Window* window, @@ -259,13 +273,15 @@ forge::Chart* ForgeManager::getChart(const forge::Window* window, const int r, c forge::Chart* chart = NULL; ChartMapIter iter = mChartMap.find(window); - if(iter != mChartMap.end()) { + if (iter != mChartMap.end()) { + int gRows = window->gridRows(); int gCols = window->gridCols(); if(c >= gCols || r >= gRows) AF_ERROR("Grid points are out of bounds", AF_ERR_TYPE); + // upgrade to exclusive access to make changes chart = (iter->second)[c * gRows + r]; if (chart == NULL) { @@ -303,9 +319,12 @@ forge::Image* ForgeManager::getImage(int w, int h, forge::ChannelFormat mode, fo key = (((key << 16) | mode) << 16) | type; ChartKey_t keypair = std::make_pair(key, nullptr); + ImgMapIter iter = mImgMap.find(keypair); + if (iter==mImgMap.end()) { forge::Image* temp = new forge::Image(w, h, mode, type); + mImgMap[keypair] = temp; } @@ -326,13 +345,17 @@ forge::Image* ForgeManager::getImage(forge::Chart* chart, int w, int h, key = (((key << 16) | mode) << 16) | type; ChartKey_t keypair = std::make_pair(key, chart); + ImgMapIter iter = mImgMap.find(keypair); + if (iter==mImgMap.end()) { if(chart->getChartType() != FG_CHART_2D) AF_ERROR("Image can only be added to chart of type FG_CHART_2D", AF_ERR_TYPE); forge::Image* temp = new forge::Image(w, h, mode, type); + mImgMap[keypair] = temp; + chart->add(*mImgMap[keypair]); } @@ -352,10 +375,14 @@ forge::Plot* ForgeManager::getPlot(forge::Chart* chart, int nPoints, forge::dtyp key |= (((((dtype & 0x000F) << 12) | (ptype & 0x000F)) << 8) | (mtype & 0x000F)); ChartKey_t keypair = std::make_pair(key, chart); + PltMapIter iter = mPltMap.find(keypair); + if (iter==mPltMap.end()) { forge::Plot* temp = new forge::Plot(nPoints, dtype, chart->getChartType(), ptype, mtype); + mPltMap[keypair] = temp; + chart->add(*mPltMap[keypair]); } @@ -373,13 +400,17 @@ forge::Histogram* ForgeManager::getHistogram(forge::Chart* chart, int nBins, for long long key = ((nBins & _48BIT) << 48) | (type & _16BIT); ChartKey_t keypair = std::make_pair(key, chart); + HstMapIter iter = mHstMap.find(keypair); + if (iter==mHstMap.end()) { if(chart->getChartType() != FG_CHART_2D) AF_ERROR("Histogram can only be added to chart of type FG_CHART_2D", AF_ERR_TYPE); forge::Histogram* temp = new forge::Histogram(nBins, type); + mHstMap[keypair] = temp; + chart->add(*mHstMap[keypair]); } @@ -397,13 +428,17 @@ forge::Surface* ForgeManager::getSurface(forge::Chart* chart, int nX, int nY, fo long long key = (((nX * nY) & _48BIT) << 48) | (type & _16BIT); ChartKey_t keypair = std::make_pair(key, chart); + SfcMapIter iter = mSfcMap.find(keypair); + if (iter==mSfcMap.end()) { if(chart->getChartType() != FG_CHART_3D) AF_ERROR("Surface can only be added to chart of type FG_CHART_3D", AF_ERR_TYPE); forge::Surface* temp = new forge::Surface(nX, nY, type); + mSfcMap[keypair] = temp; + chart->add(*mSfcMap[keypair]); } @@ -421,10 +456,14 @@ forge::VectorField* ForgeManager::getVectorField(forge::Chart* chart, int nPoint long long key = (((nPoints) & _48BIT) << 48) | (type & _16BIT); ChartKey_t keypair = std::make_pair(key, chart); + VcfMapIter iter = mVcfMap.find(keypair); + if (iter==mVcfMap.end()) { forge::VectorField* temp = new forge::VectorField(nPoints, type, chart->getChartType()); + mVcfMap[keypair] = temp; + chart->add(*mVcfMap[keypair]); } @@ -448,33 +487,6 @@ void ForgeManager::setChartAxesOverride(forge::Chart* chart, bool flag) } mChartAxesOverrideMap[chart] = flag; } - -void ForgeManager::destroyResources() -{ - /* clear all OpenGL resource objects (images, plots, histograms etc) first - * and then delete the windows */ - for(ImgMapIter iter = mImgMap.begin(); iter != mImgMap.end(); iter++) - delete (iter->second); - - for(PltMapIter iter = mPltMap.begin(); iter != mPltMap.end(); iter++) - delete (iter->second); - - for(HstMapIter iter = mHstMap.begin(); iter != mHstMap.end(); iter++) - delete (iter->second); - - for(ChartMapIter iter = mChartMap.begin(); iter != mChartMap.end(); iter++) { - for(int i = 0; i < (int)(iter->second).size(); i++) { - if((iter->second)[i] != NULL) { - delete (iter->second)[i]; - mChartAxesOverrideMap.erase((iter->second)[i]); - } - } - } - - delete getFont(true); - delete getMainWindow(true); -} - } #endif diff --git a/src/api/c/graphics_common.hpp b/src/api/c/graphics_common.hpp index 06fb68d96b..b8f29a343a 100644 --- a/src/api/c/graphics_common.hpp +++ b/src/api/c/graphics_common.hpp @@ -12,9 +12,11 @@ #if defined(WITH_GRAPHICS) #include + #include #include #include + #include #include #include @@ -41,7 +43,6 @@ double step_round(const double in, const bool dir); namespace graphics { - enum Defaults { WIDTH = 1280, HEIGHT= 720 @@ -102,8 +103,8 @@ class ForgeManager static ForgeManager& getInstance(); ~ForgeManager(); - forge::Font* getFont(const bool dontCreate=false); - forge::Window* getMainWindow(const bool dontCreate=false); + forge::Font* getFont(); + forge::Window* getMainWindow(); void setWindowChartGrid(const forge::Window* window, const int r, const int c); @@ -128,11 +129,9 @@ class ForgeManager ForgeManager() {} ForgeManager(ForgeManager const&); void operator=(ForgeManager const&); - void destroyResources(); }; - } -#define MAIN_WINDOW graphics::ForgeManager::getInstance().getMainWindow(true) +#define MAIN_WINDOW graphics::ForgeManager::getInstance().getMainWindow() #endif diff --git a/src/api/cpp/fft.cpp b/src/api/cpp/fft.cpp index 1fe4a0921c..e7e96ca195 100644 --- a/src/api/cpp/fft.cpp +++ b/src/api/cpp/fft.cpp @@ -14,8 +14,6 @@ namespace af { - - array fftNorm(const array& in, const double norm_factor, const dim_t odim0) { af_array out = 0; @@ -281,4 +279,8 @@ FFT_REAL(1) FFT_REAL(2) FFT_REAL(3) +void setFFTPlanCacheSize(size_t cacheSize) +{ + AF_THROW(af_set_fft_plan_cache_size(cacheSize)); +} } diff --git a/src/backend/MemoryManager.cpp b/src/backend/MemoryManager.cpp deleted file mode 100644 index 3b0e81ddc5..0000000000 --- a/src/backend/MemoryManager.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#include -#include -#include -#include -#include "MemoryManager.hpp" -#include "dispatch.hpp" -#include "err_common.hpp" -#include "util.hpp" - -namespace common -{ - -MemoryManager::MemoryManager(int num_devices, unsigned MAX_BUFFERS, bool debug): - mem_step_size(1024), - max_buffers(MAX_BUFFERS), - memory(num_devices), - debug_mode(debug) -{ - lock_guard_t lock(this->memory_mutex); - - for (int n = 0; n < num_devices; n++) { - // Calling getMaxMemorySize() here calls the virtual function that returns 0 - // Call it from outside the constructor. - memory[n].max_bytes = ONE_GB; - memory[n].total_bytes = 0; - memory[n].total_buffers = 0; - memory[n].lock_bytes = 0; - memory[n].lock_buffers = 0; - } - - // Check for environment variables - - std::string env_var; - - // Debug mode - env_var = getEnvVar("AF_MEM_DEBUG"); - if (!env_var.empty()) { - this->debug_mode = env_var[0] != '0'; - } - if (this->debug_mode) mem_step_size = 1; - - // Max Buffer count - env_var = getEnvVar("AF_MAX_BUFFERS"); - if (!env_var.empty()) { - this->max_buffers = std::max(1, std::stoi(env_var)); - } -} - -void MemoryManager::setMaxMemorySize() -{ - for (unsigned n = 0; n < memory.size(); n++) { - // Calls garbage collection when: - // total_bytes > memsize * 0.75 when memsize < 4GB - // total_bytes > memsize - 1 GB when memsize >= 4GB - // If memsize returned 0, then use 1GB - size_t memsize = this->getMaxMemorySize(n); - memory[n].max_bytes = memsize == 0 ? ONE_GB : std::max(memsize * 0.75, (double)(memsize - ONE_GB)); - } -} - -void MemoryManager::garbageCollect() -{ - if (this->debug_mode) return; - - lock_guard_t lock(this->memory_mutex); - memory_info& current = this->getCurrentMemoryInfo(); - - // Return if all buffers are locked - if (current.total_buffers == current.lock_buffers) return; - - for (auto &kv : current.free_map) { - size_t num_ptrs = kv.second.size(); - //Free memory by popping the last element - for (int n = num_ptrs-1; n >= 0; n--) { - this->nativeFree(kv.second[n]); - current.total_bytes -= kv.first; - current.total_buffers--; - kv.second.pop_back(); - } - } - current.free_map.clear(); -} - -void MemoryManager::unlock(void *ptr, bool user_unlock) -{ - // Shortcut for empty arrays - if (!ptr) return; - - lock_guard_t lock(this->memory_mutex); - memory_info& current = this->getCurrentMemoryInfo(); - - locked_iter iter = current.locked_map.find((void *)ptr); - - // Pointer not found in locked map - if (iter == current.locked_map.end()) { - // Probably came from user, just free it - this->nativeFree(ptr); - return; - } - - if (user_unlock) { - (iter->second).user_lock = false; - } else { - (iter->second).manager_lock = false; - } - - // Return early if either one is locked - if ((iter->second).user_lock || (iter->second).manager_lock) return; - - size_t bytes = iter->second.bytes; - current.lock_bytes -= iter->second.bytes; - current.lock_buffers--; - - current.locked_map.erase(iter); - - if (this->debug_mode) { - // Just free memory in debug mode - if ((iter->second).bytes > 0) { - this->nativeFree(iter->first); - current.total_buffers--; - current.total_bytes -= iter->second.bytes; - } - } else { - // In regular mode, move buffer to free map - free_iter fiter = current.free_map.find(bytes); - if (fiter != current.free_map.end()) { - // If found, push back - fiter->second.push_back(ptr); - } else { - // If not found, create new vector for this size - std::vector ptrs; - ptrs.push_back(ptr); - current.free_map[bytes] = ptrs; - } - } -} - -void *MemoryManager::alloc(const size_t bytes, bool user_lock) -{ - lock_guard_t lock(this->memory_mutex); - - void *ptr = NULL; - size_t alloc_bytes = this->debug_mode ? bytes : (divup(bytes, mem_step_size) * mem_step_size); - - if (bytes > 0) { - memory_info& current = this->getCurrentMemoryInfo(); - - // There is no memory cache in debug mode - if (!this->debug_mode) { - - // FIXME: Add better checks for garbage collection - // Perhaps look at total memory available as a metric - if (this->checkMemoryLimit()) { - this->garbageCollect(); - } - - free_iter iter = current.free_map.find(alloc_bytes); - - if (iter != current.free_map.end() && !iter->second.empty()) { - ptr = iter->second.back(); - iter->second.pop_back(); - } - - } - - // Only comes here if buffer size not found or in debug mode - if (ptr == NULL) { - // Perform garbage collection if memory can not be allocated - try { - ptr = this->nativeAlloc(alloc_bytes); - } catch (AfError &ex) { - // If out of memory, run garbage collect and try again - if (ex.getError() != AF_ERR_NO_MEM) throw; - this->garbageCollect(); - ptr = this->nativeAlloc(alloc_bytes); - } - // Increment these two only when it succeeds to come here. - current.total_bytes += alloc_bytes; - current.total_buffers += 1; - } - - - locked_info info = {!user_lock, user_lock, alloc_bytes}; - current.locked_map[ptr] = info; - current.lock_bytes += alloc_bytes; - current.lock_buffers++; - } - return ptr; -} - -void MemoryManager::userLock(const void *ptr) -{ - memory_info& current = this->getCurrentMemoryInfo(); - - lock_guard_t lock(this->memory_mutex); - - locked_iter iter = current.locked_map.find(const_cast(ptr)); - - if (iter != current.locked_map.end()) { - iter->second.user_lock = true; - } else { - locked_info info = {false, - true, - 100}; //This number is not relevant - - current.locked_map[(void *)ptr] = info; - } -} - -void MemoryManager::userUnlock(const void *ptr) -{ - this->unlock(const_cast(ptr), true); -} - -bool MemoryManager::isUserLocked(const void *ptr) -{ - memory_info& current = this->getCurrentMemoryInfo(); - lock_guard_t lock(this->memory_mutex); - locked_iter iter = current.locked_map.find(const_cast(ptr)); - if (iter != current.locked_map.end()) { - return iter->second.user_lock; - } else { - return false; - } -} - -size_t MemoryManager::getMemStepSize() -{ - lock_guard_t lock(this->memory_mutex); - return this->mem_step_size; -} - -void MemoryManager::setMemStepSize(size_t new_step_size) -{ - lock_guard_t lock(this->memory_mutex); - this->mem_step_size = new_step_size; -} - -size_t MemoryManager::getMaxBytes() -{ - lock_guard_t lock(this->memory_mutex); - return this->getCurrentMemoryInfo().max_bytes; -} - -void MemoryManager::printInfo(const char *msg, const int device) -{ - lock_guard_t lock(this->memory_mutex); - const memory_info& current = this->getCurrentMemoryInfo(); - - std::cout << msg << std::endl; - - static const std::string head("| POINTER | SIZE | AF LOCK | USER LOCK |"); - static const std::string line(head.size(), '-'); - std::cout << line << std::endl << head << std::endl << line << std::endl; - - for(auto& kv : current.locked_map) { - std::string status_mngr("Yes"); - std::string status_user("Unknown"); - if(kv.second.user_lock) status_user = "Yes"; - else status_user = " No"; - - std::string unit = "KB"; - double size = (double)(kv.second.bytes) / 1024; - if(size >= 1024) { - size = size / 1024; - unit = "MB"; - } - - std::cout << "| " << std::right << std::setw(14) << kv.first << " " - << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit - << " | " << std::setw(9) << status_mngr - << " | " << std::setw(9) << status_user - << " |" << std::endl; - } - - for(auto &kv : current.free_map) { - - std::string status_mngr("No"); - std::string status_user("No"); - - std::string unit = "KB"; - double size = (double)(kv.first) / 1024; - if(size >= 1024) { - size = size / 1024; - unit = "MB"; - } - - for (auto &ptr : kv.second) { - std::cout << "| " << std::right << std::setw(14) << ptr << " " - << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit - << " | " << std::setw(9) << status_mngr - << " | " << std::setw(9) << status_user - << " |" << std::endl; - } - } - - std::cout << line << std::endl; -} - -void MemoryManager::bufferInfo(size_t *alloc_bytes, size_t *alloc_buffers, - size_t *lock_bytes, size_t *lock_buffers) -{ - lock_guard_t lock(this->memory_mutex); - const memory_info& current = this->getCurrentMemoryInfo(); - if (alloc_bytes ) *alloc_bytes = current.total_bytes; - if (alloc_buffers ) *alloc_buffers = current.total_buffers; - if (lock_bytes ) *lock_bytes = current.lock_bytes; - if (lock_buffers ) *lock_buffers = current.lock_buffers; -} - -unsigned MemoryManager::getMaxBuffers() -{ - return this->max_buffers; -} - -bool MemoryManager::checkMemoryLimit() -{ - const memory_info& current = this->getCurrentMemoryInfo(); - return current.lock_bytes >= current.max_bytes || current.total_buffers >= this->max_buffers; -} - -} diff --git a/src/backend/MemoryManager.hpp b/src/backend/MemoryManager.hpp deleted file mode 100644 index 39ff8e1281..0000000000 --- a/src/backend/MemoryManager.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#pragma once - -#include -#include -#include - -namespace common -{ - -typedef std::recursive_mutex mutex_t; -typedef std::lock_guard lock_guard_t; - -const unsigned MAX_BUFFERS = 1000; -const size_t ONE_GB = 1 << 30; - -class MemoryManager -{ - typedef struct - { - bool manager_lock; - bool user_lock; - size_t bytes; - } locked_info; - - typedef std::unordered_map locked_t; - typedef locked_t::iterator locked_iter; - - typedef std::unordered_map >free_t; - typedef free_t::iterator free_iter; - - typedef struct - { - locked_t locked_map; - free_t free_map; - - size_t lock_bytes; - size_t lock_buffers; - size_t total_bytes; - size_t total_buffers; - size_t max_bytes; - } memory_info; - - size_t mem_step_size; - unsigned max_buffers; - std::vector memory; - bool debug_mode; - - memory_info& getCurrentMemoryInfo() - { - return memory[this->getActiveDeviceId()]; - } - - virtual int getActiveDeviceId() - { - return 0; - } - - virtual size_t getMaxMemorySize(int id) - { - return 0; - } - -public: - MemoryManager(int num_devices, unsigned MAX_BUFFERS, bool debug); - - void setMaxMemorySize(); - - void *alloc(const size_t bytes, bool user_lock); - - void unlock(void *ptr, bool user_unlock); - - void garbageCollect(); - - void printInfo(const char *msg, const int device); - - void bufferInfo(size_t *alloc_bytes, size_t *alloc_buffers, - size_t *lock_bytes, size_t *lock_buffers); - - void userLock(const void *ptr); - - void userUnlock(const void *ptr); - - bool isUserLocked(const void *ptr); - - size_t getMemStepSize(); - - size_t getMaxBytes(); - - unsigned getMaxBuffers(); - - void setMemStepSize(size_t new_step_size); - - virtual void *nativeAlloc(const size_t bytes) - { - return malloc(bytes); - } - - virtual void nativeFree(void *ptr) - { - free((void *)ptr); - } - - virtual ~MemoryManager() - { - } - - bool checkMemoryLimit(); - -protected: - mutex_t memory_mutex; - -}; - -} diff --git a/src/backend/common/FFTPlanCache.hpp b/src/backend/common/FFTPlanCache.hpp new file mode 100644 index 0000000000..c2d9d7c9bd --- /dev/null +++ b/src/backend/common/FFTPlanCache.hpp @@ -0,0 +1,78 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once +#include +#include +#include +#include + +namespace common +{ +// FFTPlanCache caches backend specific fft plans in FIFO order +// +// new plan |--> IF number of plans cached is at limit, pop the oldest entry and push new plan. +// | +// |--> ELSE just push the plan +// existing plan -> reuse a plan +template +class FFTPlanCache +{ + using plan_t = typename std::shared_ptr

; + using plan_pair_t = typename std::pair; + using plan_cache_t = typename std::deque; + + public: + FFTPlanCache() : mMaxCacheSize(5) {} + + void setMaxCacheSize(size_t size) + { + mMaxCacheSize = size; + while (mCache.size()>mMaxCacheSize) + mCache.pop_back(); + } + + size_t getMaxCacheSize() const { return mMaxCacheSize; } + + // iterates through plan cache from front to back + // of the cache(queue) + // A valid shared_ptr of the plan in the cache is returned + // if found, and empty share_ptr otherwise. + plan_t find(const std::string& key) const + { + std::shared_ptr

res; + + for(unsigned i=0; i=mMaxCacheSize) + mCache.pop_back(); + + mCache.push_front(plan_pair_t(key, plan)); + } + + protected: + FFTPlanCache(FFTPlanCache const&); + void operator=(FFTPlanCache const&); + + size_t mMaxCacheSize; + + plan_cache_t mCache; +}; +} diff --git a/src/backend/common/InteropManager.cpp b/src/backend/common/InteropManager.cpp new file mode 100644 index 0000000000..7c1ae3bc07 --- /dev/null +++ b/src/backend/common/InteropManager.cpp @@ -0,0 +1,134 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#if defined(WITH_GRAPHICS) +//FIXME CPU backend doesn't required the following class implementation +//InteropManager.hpp is not used while building CPU backend. +#ifndef AF_CPU +#include +#include +#include +#include +#include +#include +#include +#include + +template +using RVector = std::vector>; + +namespace common +{ +template +InteropManager::~InteropManager() +{ + try { + destroyResources(); + } catch (AfError &ex) { + + std::string perr = getEnvVar("AF_PRINT_ERRORS"); + if(!perr.empty()) { + if(perr != "0") fprintf(stderr, "%s\n", ex.what()); + } + } +} + +template +RVector InteropManager::getBufferResource(const forge::Image* image) +{ + void * key = (void*)image; + + if (mInteropMap.find(key) == mInteropMap.end()) { + std::vector handles; + handles.push_back(image->pixels()); + std::vector output = static_cast(this)->registerResources(handles); + + mInteropMap[key] = output; + } + + return mInteropMap[key]; +} + +template +RVector InteropManager::getBufferResource(const forge::Plot* plot) +{ + void * key = (void*)plot; + + if (mInteropMap.find(key) == mInteropMap.end()) { + std::vector handles; + handles.push_back(plot->vertices()); + std::vector output = static_cast(this)->registerResources(handles); + + mInteropMap[key] = output; + } + + return mInteropMap[key]; +} + +template +RVector InteropManager::getBufferResource(const forge::Histogram* histogram) +{ + void * key = (void*)histogram; + + if (mInteropMap.find(key) == mInteropMap.end()) { + std::vector handles; + handles.push_back(histogram->vertices()); + std::vector output = static_cast(this)->registerResources(handles); + + mInteropMap[key] = output; + } + + return mInteropMap[key]; +} + +template +RVector InteropManager::getBufferResource(const forge::Surface* surface) +{ + void * key = (void*)surface; + + if (mInteropMap.find(key) == mInteropMap.end()) { + std::vector handles; + handles.push_back(surface->vertices()); + std::vector output = static_cast(this)->registerResources(handles); + + mInteropMap[key] = output; + } + + return mInteropMap[key]; +} + +template +RVector InteropManager::getBufferResource(const forge::VectorField* field) +{ + void * key = (void*)field; + + if (mInteropMap.find(key) == mInteropMap.end()) { + std::vector handles; + handles.push_back(field->vertices()); + handles.push_back(field->directions()); + std::vector output = static_cast(this)->registerResources(handles); + + mInteropMap[key] = output; + } + + return mInteropMap[key]; +} + +template +void InteropManager::destroyResources() +{ + for(auto iter : mInteropMap) { + iter.second.clear(); + } +} + +template class InteropManager; +} +#endif +#endif diff --git a/src/backend/common/InteropManager.hpp b/src/backend/common/InteropManager.hpp new file mode 100644 index 0000000000..a026340afd --- /dev/null +++ b/src/backend/common/InteropManager.hpp @@ -0,0 +1,45 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once + +#if defined(WITH_GRAPHICS) +#include +#include +#include +#include + +namespace common +{ +template +class InteropManager +{ + using resource_t = typename std::shared_ptr; + using res_vec_t = typename std::vector; + using res_map_t = typename std::map; + + public: + InteropManager() {} + ~InteropManager(); + + res_vec_t getBufferResource(const forge::Image* image); + res_vec_t getBufferResource(const forge::Plot* plot); + res_vec_t getBufferResource(const forge::Histogram* histogram); + res_vec_t getBufferResource(const forge::Surface* surface); + res_vec_t getBufferResource(const forge::VectorField* field); + + protected: + InteropManager(InteropManager const&); + void operator=(InteropManager const&); + void destroyResources(); + + res_map_t mInteropMap; +}; +} +#endif diff --git a/src/backend/common/MatrixAlgebraHandle.hpp b/src/backend/common/MatrixAlgebraHandle.hpp new file mode 100644 index 0000000000..bf4f6c1110 --- /dev/null +++ b/src/backend/common/MatrixAlgebraHandle.hpp @@ -0,0 +1,39 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once + +#include +#include + +namespace common +{ +template +class MatrixAlgebraHandle +{ + public: + MatrixAlgebraHandle() { + static_cast(this)->createHandle(&handle); + } + + ~MatrixAlgebraHandle() { + static_cast(this)->destroyHandle(handle); + } + + H get() const { + return handle; + } + + private: + MatrixAlgebraHandle(MatrixAlgebraHandle const&); + void operator=(MatrixAlgebraHandle const&); + + H handle; +}; +} diff --git a/src/backend/common/MemoryManager.hpp b/src/backend/common/MemoryManager.hpp new file mode 100644 index 0000000000..b2138da488 --- /dev/null +++ b/src/backend/common/MemoryManager.hpp @@ -0,0 +1,434 @@ +/******************************************************* + * Copyright (c) 2014, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace common +{ +typedef std::recursive_mutex mutex_t; +typedef std::lock_guard lock_guard_t; + +const unsigned MAX_BUFFERS = 1000; +const size_t ONE_GB = 1 << 30; + +template +class MemoryManager +{ + typedef struct + { + bool manager_lock; + bool user_lock; + size_t bytes; + } locked_info; + + using locked_t = typename std::unordered_map; + using locked_iter = typename locked_t::iterator; + + typedef std::unordered_map >free_t; + typedef free_t::iterator free_iter; + + typedef struct memory_info + { + locked_t locked_map; + free_t free_map; + + size_t lock_bytes; + size_t lock_buffers; + size_t total_bytes; + size_t total_buffers; + size_t max_bytes; + + memory_info() + { + // Calling getMaxMemorySize() here calls the virtual function that returns 0 + // Call it from outside the constructor. + max_bytes = ONE_GB; + total_bytes = 0; + total_buffers = 0; + lock_bytes = 0; + lock_buffers = 0; + } + } memory_info; + + size_t mem_step_size; + unsigned max_buffers; + std::vector memory; + bool debug_mode; + + memory_info& getCurrentMemoryInfo() + { + return memory[this->getActiveDeviceId()]; + } + + inline int getActiveDeviceId() + { + return static_cast(this)->getActiveDeviceId(); + } + + inline size_t getMaxMemorySize(int id) + { + return static_cast(this)->getMaxMemorySize(id); + } + + void cleanDeviceMemoryManager(int device) + { + if (this->debug_mode) return; + + lock_guard_t lock(this->memory_mutex); + memory_info& current = memory[device]; + + // Return if all buffers are locked + if (current.total_buffers == current.lock_buffers) return; + + for (auto &kv : current.free_map) { + size_t num_ptrs = kv.second.size(); + //Free memory by popping the last element + for (int n = num_ptrs-1; n >= 0; n--) { + this->nativeFree(kv.second[n]); + current.total_bytes -= kv.first; + current.total_buffers--; + kv.second.pop_back(); + } + } + current.free_map.clear(); + } + + public: + MemoryManager(int num_devices, unsigned MAX_BUFFERS, bool debug) + : mem_step_size(1024), max_buffers(MAX_BUFFERS), memory(num_devices), debug_mode(debug) + { + lock_guard_t lock(this->memory_mutex); + + // Check for environment variables + + // Debug mode + std::string env_var = getEnvVar("AF_MEM_DEBUG"); + if (!env_var.empty()) this->debug_mode = env_var[0] != '0'; + if (this->debug_mode) mem_step_size = 1; + + // Max Buffer count + env_var = getEnvVar("AF_MAX_BUFFERS"); + if (!env_var.empty()) this->max_buffers = std::max(1, std::stoi(env_var)); + } + + // Intended to be used with OpenCL backend, where + // users are allowed to add external devices(context, device pair) + // to the list of devices automatically detected by the library + void addMemoryManagement(int device) + { + // If there is a memory manager allocated for + // this device id, we might as well use it and the + // buffers allocated for it + if ((size_t)device < memory.size()) + return; + + // Assuming, device need not be always the next device + // Lets resize to current_size + device + 1 + // +1 is to account for device being 0-based index of devices + memory.resize(memory.size()+device+1); + } + + // Intended to be used with OpenCL backend, where + // users are allowed to add external devices(context, device pair) + // to the list of devices automatically detected by the library + void removeMemoryManagement(int device) + { + if ((size_t)device>=memory.size()) + AF_ERROR("No matching device found", AF_ERR_ARG); + + // Do garbage collection for the device and leave + // the memory_info struct from the memory vector intact + cleanDeviceMemoryManager(device); + } + + void setMaxMemorySize() + { + for (unsigned n = 0; n < memory.size(); n++) { + // Calls garbage collection when: + // total_bytes > memsize * 0.75 when memsize < 4GB + // total_bytes > memsize - 1 GB when memsize >= 4GB + // If memsize returned 0, then use 1GB + size_t memsize = this->getMaxMemorySize(n); + memory[n].max_bytes = memsize == 0 ? ONE_GB : + std::max(memsize * 0.75, (double)(memsize - ONE_GB)); + } + } + + void *alloc(const size_t bytes, bool user_lock) + { + lock_guard_t lock(this->memory_mutex); + + void *ptr = NULL; + size_t alloc_bytes = this->debug_mode ? bytes : (divup(bytes, mem_step_size) * mem_step_size); + + if (bytes > 0) { + memory_info& current = this->getCurrentMemoryInfo(); + + // There is no memory cache in debug mode + if (!this->debug_mode) { + + // FIXME: Add better checks for garbage collection + // Perhaps look at total memory available as a metric + if (this->checkMemoryLimit()) { + this->garbageCollect(); + } + + free_iter iter = current.free_map.find(alloc_bytes); + + if (iter != current.free_map.end() && !iter->second.empty()) { + ptr = iter->second.back(); + iter->second.pop_back(); + } + + } + + // Only comes here if buffer size not found or in debug mode + if (ptr == NULL) { + // Perform garbage collection if memory can not be allocated + try { + ptr = this->nativeAlloc(alloc_bytes); + } catch (AfError &ex) { + // If out of memory, run garbage collect and try again + if (ex.getError() != AF_ERR_NO_MEM) throw; + this->garbageCollect(); + ptr = this->nativeAlloc(alloc_bytes); + } + // Increment these two only when it succeeds to come here. + current.total_bytes += alloc_bytes; + current.total_buffers += 1; + } + + + locked_info info = {!user_lock, user_lock, alloc_bytes}; + current.locked_map[ptr] = info; + current.lock_bytes += alloc_bytes; + current.lock_buffers++; + } + return ptr; + } + + void unlock(void *ptr, bool user_unlock) + { + // Shortcut for empty arrays + if (!ptr) return; + + lock_guard_t lock(this->memory_mutex); + memory_info& current = this->getCurrentMemoryInfo(); + + locked_iter iter = current.locked_map.find((void *)ptr); + + // Pointer not found in locked map + if (iter == current.locked_map.end()) { + // Probably came from user, just free it + this->nativeFree(ptr); + return; + } + + if (user_unlock) { + (iter->second).user_lock = false; + } else { + (iter->second).manager_lock = false; + } + + // Return early if either one is locked + if ((iter->second).user_lock || (iter->second).manager_lock) return; + + size_t bytes = iter->second.bytes; + current.lock_bytes -= iter->second.bytes; + current.lock_buffers--; + + current.locked_map.erase(iter); + + if (this->debug_mode) { + // Just free memory in debug mode + if ((iter->second).bytes > 0) { + this->nativeFree(iter->first); + current.total_buffers--; + current.total_bytes -= iter->second.bytes; + } + } else { + // In regular mode, move buffer to free map + free_iter fiter = current.free_map.find(bytes); + if (fiter != current.free_map.end()) { + // If found, push back + fiter->second.push_back(ptr); + } else { + // If not found, create new vector for this size + std::vector ptrs; + ptrs.push_back(ptr); + current.free_map[bytes] = ptrs; + } + } + } + + void garbageCollect() + { + cleanDeviceMemoryManager(this->getActiveDeviceId()); + } + + + void printInfo(const char *msg, const int device) + { + lock_guard_t lock(this->memory_mutex); + const memory_info& current = this->getCurrentMemoryInfo(); + + std::cout << msg << std::endl; + + static const std::string head("| POINTER | SIZE | AF LOCK | USER LOCK |"); + static const std::string line(head.size(), '-'); + std::cout << line << std::endl << head << std::endl << line << std::endl; + + for(auto& kv : current.locked_map) { + std::string status_mngr("Yes"); + std::string status_user("Unknown"); + if(kv.second.user_lock) status_user = "Yes"; + else status_user = " No"; + + std::string unit = "KB"; + double size = (double)(kv.second.bytes) / 1024; + if(size >= 1024) { + size = size / 1024; + unit = "MB"; + } + + std::cout << "| " << std::right << std::setw(14) << kv.first << " " + << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit + << " | " << std::setw(9) << status_mngr + << " | " << std::setw(9) << status_user + << " |" << std::endl; + } + + for(auto &kv : current.free_map) { + + std::string status_mngr("No"); + std::string status_user("No"); + + std::string unit = "KB"; + double size = (double)(kv.first) / 1024; + if(size >= 1024) { + size = size / 1024; + unit = "MB"; + } + + for (auto &ptr : kv.second) { + std::cout << "| " << std::right << std::setw(14) << ptr << " " + << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit + << " | " << std::setw(9) << status_mngr + << " | " << std::setw(9) << status_user + << " |" << std::endl; + } + } + + std::cout << line << std::endl; + } + + void bufferInfo(size_t *alloc_bytes, size_t *alloc_buffers, + size_t *lock_bytes, size_t *lock_buffers) + { + lock_guard_t lock(this->memory_mutex); + const memory_info& current = this->getCurrentMemoryInfo(); + if (alloc_bytes ) *alloc_bytes = current.total_bytes; + if (alloc_buffers ) *alloc_buffers = current.total_buffers; + if (lock_bytes ) *lock_bytes = current.lock_bytes; + if (lock_buffers ) *lock_buffers = current.lock_buffers; + } + + void userLock(const void *ptr) + { + memory_info& current = this->getCurrentMemoryInfo(); + + lock_guard_t lock(this->memory_mutex); + + locked_iter iter = current.locked_map.find(const_cast(ptr)); + + if (iter != current.locked_map.end()) { + iter->second.user_lock = true; + } else { + locked_info info = {false, + true, + 100}; //This number is not relevant + + current.locked_map[(void *)ptr] = info; + } + } + + void userUnlock(const void *ptr) + { + this->unlock(const_cast(ptr), true); + } + + bool isUserLocked(const void *ptr) + { + memory_info& current = this->getCurrentMemoryInfo(); + lock_guard_t lock(this->memory_mutex); + locked_iter iter = current.locked_map.find(const_cast(ptr)); + if (iter != current.locked_map.end()) { + return iter->second.user_lock; + } else { + return false; + } + } + + size_t getMemStepSize() + { + lock_guard_t lock(this->memory_mutex); + return this->mem_step_size; + } + + size_t getMaxBytes() + { + lock_guard_t lock(this->memory_mutex); + return this->getCurrentMemoryInfo().max_bytes; + } + + unsigned getMaxBuffers() + { + return this->max_buffers; + } + + void setMemStepSize(size_t new_step_size) + { + lock_guard_t lock(this->memory_mutex); + this->mem_step_size = new_step_size; + } + + inline void *nativeAlloc(const size_t bytes) + { + return static_cast(this)->nativeAlloc(bytes); + } + + inline void nativeFree(void *ptr) + { + static_cast(this)->nativeFree(ptr); + } + + virtual ~MemoryManager() {} + + bool checkMemoryLimit() + { + const memory_info& current = this->getCurrentMemoryInfo(); + return current.lock_bytes >= current.max_bytes || current.total_buffers >= this->max_buffers; + } + + protected: + mutex_t memory_mutex; +}; + +} diff --git a/src/backend/common/types.hpp b/src/backend/common/types.hpp new file mode 100644 index 0000000000..9d08b04d48 --- /dev/null +++ b/src/backend/common/types.hpp @@ -0,0 +1,17 @@ +/******************************************************* + * Copyright (c) 2014, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once +#include + +namespace common +{ +typedef std::recursive_mutex mutex_t; +typedef std::lock_guard lock_guard_t; +} diff --git a/src/backend/cpu/Array.cpp b/src/backend/cpu/Array.cpp index 343b70f5d1..781521bf41 100644 --- a/src/backend/cpu/Array.cpp +++ b/src/backend/cpu/Array.cpp @@ -19,7 +19,6 @@ #include #include #include -#include namespace cpu { @@ -31,18 +30,25 @@ using TNJ::Node_ptr; using af::dim4; + +template +Node_ptr bufferNodePtr() +{ + return Node_ptr(reinterpret_cast(new BufferNode())); +} + template Array::Array(dim4 dims): info(getActiveDeviceId(), dims, 0, calcStrides(dims), (af_dtype)dtype_traits::af_type), data(memAlloc(dims.elements()), memFree), data_dims(dims), - node(), ready(true), owner(true) + node(bufferNodePtr()), ready(true), owner(true) { } template Array::Array(dim4 dims, const T * const in_data, bool is_device, bool copy_device): info(getActiveDeviceId(), dims, 0, calcStrides(dims), (af_dtype)dtype_traits::af_type), data((is_device & !copy_device) ? (T*)in_data : memAlloc(dims.elements()), memFree), data_dims(dims), - node(), ready(true), owner(true) + node(bufferNodePtr()), ready(true), owner(true) { static_assert(std::is_standard_layout>::value, "Array must be a standard layout type"); static_assert(offsetof(Array, info) == 0, "Array::info must be the first member variable of Array"); @@ -63,7 +69,7 @@ template Array::Array(const Array& parent, const dim4 &dims, const dim_t &offset_, const dim4 &strides) : info(parent.getDevId(), dims, offset_, strides, (af_dtype)dtype_traits::af_type), data(parent.getData()), data_dims(parent.getDataDims()), - node(), + node(bufferNodePtr()), ready(true), owner(false) { } @@ -73,7 +79,7 @@ Array::Array(af::dim4 dims, af::dim4 strides, dim_t offset_, info(getActiveDeviceId(), dims, offset_, strides, (af_dtype)dtype_traits::af_type), data(is_device ? (T*)in_data : memAlloc(info.total()), memFree), data_dims(dims), - node(), + node(bufferNodePtr()), ready(true), owner(true) { @@ -94,7 +100,7 @@ void Array::eval() getQueue().enqueue(kernel::evalArray, *this); // Reset shared_ptr - this->node.reset(); + this->node = bufferNodePtr(); ready = true; } @@ -116,12 +122,25 @@ T* Array::device() } template -void evalMultiple(std::vector*> arrays) +void evalMultiple(std::vector*> array_ptrs) { - //FIXME: implement this correctly - //Using fallback for now - for (auto array : arrays) { - array->eval(); + std::vector> arrays; + bool isWorker = getQueue().is_worker(); + for (auto &array : array_ptrs) { + if (array->ready) continue; + if (isWorker) AF_ERROR("Array not evaluated", AF_ERR_INTERNAL); + array->setId(getActiveDeviceId()); + array->data = std::shared_ptr(memAlloc(array->elements()), memFree); + arrays.push_back(*array); + } + + if (arrays.size() > 0) { + getQueue().enqueue(kernel::evalMultiple, arrays); + for (auto &array : array_ptrs) { + if (array->ready) continue; + array->ready = true; + array->node = bufferNodePtr(); + } } return; } @@ -129,20 +148,16 @@ void evalMultiple(std::vector*> arrays) template Node_ptr Array::getNode() const { - if (!node) { - + if (node->isBuffer()) { + BufferNode *bufNode = reinterpret_cast *>(node.get()); unsigned bytes = this->getDataDims().elements() * sizeof(T); - - BufferNode *buf_node = new BufferNode(data, - bytes, - getOffset(), - dims().get(), - strides().get(), - isLinear()); - - const_cast *>(this)->node = Node_ptr(reinterpret_cast(buf_node)); + bufNode->setData(data, + bytes, + getOffset(), + dims().get(), + strides().get(), + isLinear()); } - return node; } @@ -199,15 +214,15 @@ createNodeArray(const dim4 &dims, Node_ptr node) if (lock_bytes > getMaxBytes() || lock_buffers > getMaxBuffers()) { - // Calling sync to ensure the TNJ calls below - // don't overwrite the same nodes being evaluated - // FIXME: This should ideally be JIT specific mutex - getQueue().sync(); + Node *n = node.get(); + TNJ::Node_map_t nodes_map; + n->getNodesMap(nodes_map); unsigned length =0, buf_count = 0, bytes = 0; - Node *n = node.get(); - n->getInfo(length, buf_count, bytes); - n->reset(); + for(auto &entry : nodes_map) { + Node *node = entry.first; + node->getInfo(length, buf_count, bytes); + } if (2 * bytes > lock_bytes) { out.eval(); diff --git a/src/backend/cpu/Array.hpp b/src/backend/cpu/Array.hpp index 65ef5f1434..c3a1286708 100644 --- a/src/backend/cpu/Array.hpp +++ b/src/backend/cpu/Array.hpp @@ -30,6 +30,10 @@ template class Array; namespace kernel { template void evalArray(cpu::Array in); + +template +void evalMultiple(std::vector> arrays); + } } @@ -234,6 +238,7 @@ namespace cpu bool copy); friend void kernel::evalArray(Array in); + friend void kernel::evalMultiple(std::vector> arrays); friend void destroyArray(Array *arr); friend void *getDevicePtr(const Array& arr); diff --git a/src/backend/cpu/CMakeLists.txt b/src/backend/cpu/CMakeLists.txt index 79c80cc26d..ac94f897ea 100644 --- a/src/backend/cpu/CMakeLists.txt +++ b/src/backend/cpu/CMakeLists.txt @@ -126,6 +126,7 @@ FILE(GLOB backend_headers ) FILE(GLOB backend_sources + "../common/*.cpp" "../*.cpp" ) diff --git a/src/backend/cpu/TNJ/BinaryNode.hpp b/src/backend/cpu/TNJ/BinaryNode.hpp index f06581a395..78c7c6df66 100644 --- a/src/backend/cpu/TNJ/BinaryNode.hpp +++ b/src/backend/cpu/TNJ/BinaryNode.hpp @@ -29,68 +29,29 @@ namespace TNJ { template - class BinaryNode : public Node + class BinaryNode : public TNode { protected: - Node_ptr m_lhs; - Node_ptr m_rhs; BinOp m_op; - To m_val; + TNode *m_lhs, *m_rhs; public: BinaryNode(Node_ptr lhs, Node_ptr rhs) : - Node(std::max(lhs->getHeight(), rhs->getHeight()) + 1), - m_lhs(lhs), - m_rhs(rhs), - m_val(0) + TNode(0, std::max(lhs->getHeight(), rhs->getHeight()) + 1, {lhs, rhs}), + m_lhs(reinterpret_cast *>(lhs.get())), + m_rhs(reinterpret_cast *>(rhs.get())) { } - void *calc(int x, int y, int z, int w) + void calc(int x, int y, int z, int w) { - if (calcCurrent(x, y, z, w)) { - m_val = m_op.eval(*(Ti *)m_lhs->calc(x, y, z, w), - *(Ti *)m_rhs->calc(x, y, z, w)); - } - return (void *)&m_val; + this->m_val = m_op.eval(m_lhs->m_val, m_rhs->m_val); } - void *calc(int idx) + void calc(int idx) { - if (calcCurrent(idx)) { - m_val = m_op.eval(*(Ti *)m_lhs->calc(idx), - *(Ti *)m_rhs->calc(idx)); - } - return (void *)&m_val; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) - { - if (m_is_eval) return; - - m_lhs->getInfo(len, buf_count, bytes); - m_rhs->getInfo(len, buf_count, bytes); - len++; - - m_is_eval = true; - return; - } - - void reset() - { - resetCommonFlags(); - m_lhs->reset(); - m_rhs->reset(); - } - - bool isLinear(const dim_t *dims) - { - if (!m_set_is_linear) { - m_linear = m_lhs->isLinear(dims) && m_rhs->isLinear(dims); - m_set_is_linear = true; - } - return m_linear; + this->m_val = m_op.eval(m_lhs->m_val, m_rhs->m_val); } }; diff --git a/src/backend/cpu/TNJ/BufferNode.hpp b/src/backend/cpu/TNJ/BufferNode.hpp index d995314ae7..bdd7c7e700 100644 --- a/src/backend/cpu/TNJ/BufferNode.hpp +++ b/src/backend/cpu/TNJ/BufferNode.hpp @@ -11,7 +11,7 @@ #include #include #include "Node.hpp" - +#include namespace cpu { @@ -20,87 +20,78 @@ namespace TNJ using std::shared_ptr; template - class BufferNode : public Node + class BufferNode : public TNode { protected: - shared_ptr ptr; + shared_ptr m_sptr; + T *m_ptr; unsigned m_bytes; bool m_linear_buffer; - dim_t m_off; dim_t m_strides[4]; dim_t m_dims[4]; - T m_val; + std::once_flag m_set_data_flag; public: - BufferNode(shared_ptr data, - unsigned bytes, - dim_t data_off, - const dim_t *dms, - const dim_t *strs, - const bool is_linear) : - Node(0), - ptr(data), - m_bytes(bytes), - m_linear_buffer(is_linear), - m_off(data_off), - m_val(0) + BufferNode() : TNode(0, 0, {}) + {} + + void setData(shared_ptr data, + unsigned bytes, + dim_t data_off, + const dim_t *dims, + const dim_t *strides, + const bool is_linear) { - for (int i = 0; i < 4; i++) { - m_strides[i] = strs[i]; - m_dims[i] = dms[i]; - } + std::call_once(m_set_data_flag, + [this, data, bytes, + data_off, dims, strides, is_linear]() + { + m_sptr = data; + m_ptr = data.get() + data_off; + m_bytes = bytes; + m_linear_buffer = is_linear; + for (int i = 0; i < 4; i++) { + m_strides[i] = strides[i]; + m_dims[i] = dims[i]; + } + }); } - void *calc(int x, int y, int z, int w) + void calc(int x, int y, int z, int w) { - if (calcCurrent(x, y, z, w)) { - dim_t l_off = 0; - l_off += (w < (int)m_dims[3]) * w * m_strides[3]; - l_off += (z < (int)m_dims[2]) * z * m_strides[2]; - l_off += (y < (int)m_dims[1]) * y * m_strides[1]; - l_off += (x < (int)m_dims[0]) * x; - m_val = *(ptr.get() + m_off + l_off); - } - return (void *)&m_val; + dim_t l_off = 0; + l_off += (w < (int)m_dims[3]) * w * m_strides[3]; + l_off += (z < (int)m_dims[2]) * z * m_strides[2]; + l_off += (y < (int)m_dims[1]) * y * m_strides[1]; + l_off += (x < (int)m_dims[0]) * x; + this->m_val = m_ptr[l_off]; } - void *calc(int idx) + void calc(int idx) { - if (calcCurrent(idx)) { - m_val = *(ptr.get() + idx + m_off); - } - return (void *)&m_val; + this->m_val = m_ptr[idx]; } void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) { - if (m_is_eval) return; - len++; buf_count++; bytes += m_bytes; - m_is_eval = true; return; } - void reset() - { - resetCommonFlags(); - } - bool isLinear(const dim_t *dims) { - if (!m_set_is_linear) { - m_linear = m_linear_buffer && - dims[0] == m_dims[0] && - dims[1] == m_dims[1] && - dims[2] == m_dims[2] && - dims[3] == m_dims[3]; - m_set_is_linear = true; - } - return m_linear; + return m_linear_buffer && + dims[0] == m_dims[0] && + dims[1] == m_dims[1] && + dims[2] == m_dims[2] && + dims[3] == m_dims[3]; } + + bool isBuffer() { return true; } + }; } diff --git a/src/backend/cpu/TNJ/Node.hpp b/src/backend/cpu/TNJ/Node.hpp index f4ed00bd5c..0d3e6c9896 100644 --- a/src/backend/cpu/TNJ/Node.hpp +++ b/src/backend/cpu/TNJ/Node.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace cpu { @@ -18,86 +19,74 @@ namespace cpu namespace TNJ { + class Node; + using std::shared_ptr; + typedef shared_ptr Node_ptr; + + typedef std::unordered_map Node_map_t; + typedef Node_map_t::iterator Node_map_iter; + class Node { protected: const int m_height; - int x, y, z, w; - bool m_is_eval; - bool m_linear; - bool m_set_is_linear; - - - void resetCommonFlags() - { - x = -1; - y = -1; - z = -1; - w = -1; - m_is_eval = false; - m_linear = false; - m_set_is_linear = false; - } - - bool calcCurrent(int xc) - { - bool res = (x == xc); - x = xc; - return !res; - } - - bool calcCurrent(int xc, int yc, int zc, int wc) - { - bool res = (xc == x) && (yc == y) && (zc == z) && (wc == w); - x = xc; - y = yc; - z = zc; - w = wc; - return !res; - } + const std::vector m_children; public: - Node(const int height) : + Node(const int height, const std::vector children) : m_height(height), - x(-1), - y(-1), - z(-1), - w(-1), - m_is_eval(false), - m_linear(false), - m_set_is_linear(false) + m_children(children) {} + void getNodesMap(Node_map_t &node_map) + { + if (node_map.find(this) == node_map.end()) { + for (const auto &child : m_children) { + child->getNodesMap(node_map); + } + int id = node_map.size(); + node_map[this] = id; + } + } + int getHeight() { return m_height; } - virtual void *calc(int x, int y, int z, int w) + virtual void calc(int x, int y, int z, int w) { - m_is_eval = true; - return NULL; } - virtual void *calc(int idx) + virtual void calc(int idx) { - m_is_eval = true; - return NULL; } virtual void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) { - len = 0; - buf_count = 0; - bytes = 0; + len++; } virtual bool isLinear(const dim_t *dims) { return true; } - virtual void reset() { resetCommonFlags(); } - + virtual bool isBuffer() { return false; } virtual ~Node() {} + + }; + + template + class TNode : public Node + { + public: + T m_val; + public: + TNode(T val, const int height, const std::vector children) : + Node(height, children), + m_val(val) + { + } }; - typedef std::shared_ptr Node_ptr; + template + using TNode_ptr = std::shared_ptr>; } } diff --git a/src/backend/cpu/TNJ/ScalarNode.hpp b/src/backend/cpu/TNJ/ScalarNode.hpp index bda529f8d4..716c5964a9 100644 --- a/src/backend/cpu/TNJ/ScalarNode.hpp +++ b/src/backend/cpu/TNJ/ScalarNode.hpp @@ -19,38 +19,13 @@ namespace TNJ { template - class ScalarNode : public Node + class ScalarNode : public TNode { - protected: - T m_val; - public: - ScalarNode(T val) : Node(0), m_val(val) - { - } - - void *calc(int x, int y, int z, int w) - { - return (void *)(&m_val); - } - - void *calc(int idx) - { - return (void *)&m_val; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) + ScalarNode(T val) : TNode(val, 0, {}) { - if (m_is_eval) return; - len++; - m_is_eval = true; - return; } - - void reset() { resetCommonFlags(); } - - bool isLinear(const dim_t *dims) { return true; } }; } diff --git a/src/backend/cpu/TNJ/UnaryNode.hpp b/src/backend/cpu/TNJ/UnaryNode.hpp index 047151f3f7..2f9d121c20 100644 --- a/src/backend/cpu/TNJ/UnaryNode.hpp +++ b/src/backend/cpu/TNJ/UnaryNode.hpp @@ -29,63 +29,31 @@ namespace TNJ { template - class UnaryNode : public Node + class UnaryNode : public TNode { protected: - Node_ptr m_child; UnOp m_op; - To m_val; + TNode *m_child; public: UnaryNode(Node_ptr child) : - Node(child->getHeight() + 1), - m_child(child), - m_val(0) + TNode(0, child->getHeight() + 1, {child}), + m_child(reinterpret_cast *>(child.get())) { } - void *calc(int x, int y, int z, int w) - { - if (calcCurrent(x, y, z, w)) { - m_val = m_op.eval(*(Ti *)m_child->calc(x, y, z, w)); - } - return (void *)(&m_val); - } - void *calc(int idx) + void calc(int x, int y, int z, int w) { - if (calcCurrent(idx)) { - m_val = m_op.eval(*(Ti *)m_child->calc(idx)); - } - return (void *)&m_val; + this->m_val = m_op.eval(m_child->m_val); } - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) + void calc(int idx) { - if (m_is_eval) return; - - m_child->getInfo(len, buf_count, bytes); - len++; - - m_is_eval = true; - return; + this->m_val = m_op.eval(m_child->m_val); } - void reset() - { - resetCommonFlags(); - m_child->reset(); - } - - bool isLinear(const dim_t *dims) - { - if (!m_set_is_linear) { - m_linear = m_child->isLinear(dims); - m_set_is_linear = true; - } - return m_linear; - } }; } diff --git a/src/backend/cpu/kernel/Array.hpp b/src/backend/cpu/kernel/Array.hpp index 3c4a736298..7b7504064c 100644 --- a/src/backend/cpu/kernel/Array.hpp +++ b/src/backend/cpu/kernel/Array.hpp @@ -10,6 +10,8 @@ #pragma once #include #include +#include +#include namespace cpu { @@ -17,20 +19,39 @@ namespace kernel { template -void evalArray(Array in) +void evalMultiple(std::vector> arrays) { - in.setId(cpu::getActiveDeviceId()); - T *ptr = in.data.get(); + af::dim4 odims = arrays[0].dims(); + af::dim4 ostrs = arrays[0].strides(); - af::dim4 odims = in.dims(); - af::dim4 ostrs = in.strides(); + int devId = cpu::getActiveDeviceId(); + TNJ::Node_map_t nodes; + std::vector ptrs; + std::vector *> output_nodes; - bool is_linear = in.node->isLinear(odims.get()); + for (auto &arr : arrays) { + arr.setId(devId); + ptrs.push_back(arr.data.get()); + output_nodes.push_back(reinterpret_cast *>(arr.node.get())); + arr.node->getNodesMap(nodes); + } + + bool is_linear = true; + std::vector full_nodes(nodes.size()); + for(const auto &map_entry : nodes) { + full_nodes[map_entry.second] = map_entry.first; + is_linear &= map_entry.first->isLinear(odims.get()); + } if (is_linear) { - int num = in.elements(); + int num = arrays[0].elements(); for (int i = 0; i < num; i++) { - ptr[i] = *(T *)in.node->calc(i); + for (int n = 0; n < (int)full_nodes.size(); n++) { + full_nodes[n]->calc(i); + } + for (int n = 0; n < (int)output_nodes.size(); n++) { + ptrs[n][i] = output_nodes[n]->m_val; + } } } else { for (int w = 0; w < (int)odims[3]; w++) { @@ -45,15 +66,70 @@ void evalArray(Array in) for (int x = 0; x < (int)odims[0]; x++) { dim_t id = x + offy; - ptr[id] = *(T *)in.node->calc(x, y, z, w); + for (int n = 0; n < (int)full_nodes.size(); n++) { + full_nodes[n]->calc(x, y, z, w); + } + for (int n = 0; n < (int)output_nodes.size(); n++) { + ptrs[n][id] = output_nodes[n]->m_val; + } } } } } } +} + +template +void evalArray(Array arr) +{ + arr.setId(cpu::getActiveDeviceId()); + T *ptr = arr.data.get(); + + af::dim4 odims = arr.dims(); + af::dim4 ostrs = arr.strides(); + + TNJ::Node_map_t nodes; + arr.node->getNodesMap(nodes); + + bool is_linear = true; + std::vector full_nodes(nodes.size()); + + for(const auto &map_entry : nodes) { + full_nodes[map_entry.second] = map_entry.first; + is_linear &= map_entry.first->isLinear(odims.get()); + } + + TNJ::TNode *output_node = reinterpret_cast *>(full_nodes.back()); + if (is_linear) { + int num = arr.elements(); + for (int i = 0; i < num; i++) { + for (int n = 0; n < (int)full_nodes.size(); n++) { + full_nodes[n]->calc(i); + } + ptr[i] = output_node->m_val; + } + } else { + for (int w = 0; w < (int)odims[3]; w++) { + dim_t offw = w * ostrs[3]; + + for (int z = 0; z < (int)odims[2]; z++) { + dim_t offz = z * ostrs[2] + offw; + + for (int y = 0; y < (int)odims[1]; y++) { + dim_t offy = y * ostrs[1] + offz; - // Reset TNJ flags - in.node->reset(); + for (int x = 0; x < (int)odims[0]; x++) { + dim_t id = x + offy; + + for (int n = 0; n < (int)full_nodes.size(); n++) { + full_nodes[n]->calc(x, y, z, w); + } + ptr[id] = output_node->m_val; + } + } + } + } + } } } diff --git a/src/backend/cpu/memory.cpp b/src/backend/cpu/memory.cpp index 25df00f4e2..8c246289a0 100644 --- a/src/backend/cpu/memory.cpp +++ b/src/backend/cpu/memory.cpp @@ -12,8 +12,6 @@ #include #include #include -#include -#include #ifndef AF_MEM_DEBUG #define AF_MEM_DEBUG 0 @@ -25,88 +23,34 @@ namespace cpu { - -class MemoryManager : public common::MemoryManager -{ - int getActiveDeviceId(); - size_t getMaxMemorySize(int id); -public: - MemoryManager(); - void *nativeAlloc(const size_t bytes); - void nativeFree(void *ptr); - ~MemoryManager() - { - common::lock_guard_t lock(this->memory_mutex); - for (int n = 0; n < getDeviceCount(); n++) { - cpu::setDevice(n); - this->garbageCollect(); - } - } -}; - -int MemoryManager::getActiveDeviceId() -{ - return cpu::getActiveDeviceId(); -} - -size_t MemoryManager::getMaxMemorySize(int id) -{ - return cpu::getDeviceMemorySize(id); -} - -MemoryManager::MemoryManager() : - common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, AF_MEM_DEBUG || AF_CPU_MEM_DEBUG) -{ - this->setMaxMemorySize(); -} - - -void *MemoryManager::nativeAlloc(const size_t bytes) -{ - void *ptr = malloc(bytes); - if (!ptr) AF_ERROR("Unable to allocate memory", AF_ERR_NO_MEM); - return ptr; -} - -void MemoryManager::nativeFree(void *ptr) -{ - return free((void *)ptr); -} - -static MemoryManager &getMemoryManager() -{ - static MemoryManager instance; - return instance; -} - void setMemStepSize(size_t step_bytes) { - getMemoryManager().setMemStepSize(step_bytes); + memoryManager().setMemStepSize(step_bytes); } size_t getMemStepSize(void) { - return getMemoryManager().getMemStepSize(); + return memoryManager().getMemStepSize(); } size_t getMaxBytes() { - return getMemoryManager().getMaxBytes(); + return memoryManager().getMaxBytes(); } unsigned getMaxBuffers() { - return getMemoryManager().getMaxBuffers(); + return memoryManager().getMaxBuffers(); } void garbageCollect() { - getMemoryManager().garbageCollect(); + memoryManager().garbageCollect(); } void printMemInfo(const char *msg, const int device) { - getMemoryManager().printInfo(msg, device); + memoryManager().printInfo(msg, device); } template @@ -115,10 +59,10 @@ T* memAlloc(const size_t &elements) T *ptr = nullptr; try { - ptr = (T *)getMemoryManager().alloc(elements * sizeof(T), false); + ptr = (T *)memoryManager().alloc(elements * sizeof(T), false); } catch(...) { getQueue().sync(); - ptr = (T *)getMemoryManager().alloc(elements * sizeof(T), false); + ptr = (T *)memoryManager().alloc(elements * sizeof(T), false); } return ptr; } @@ -128,10 +72,10 @@ void* memAllocUser(const size_t &bytes) void *ptr = nullptr; try { - ptr = getMemoryManager().alloc(bytes, true); + ptr = memoryManager().alloc(bytes, true); } catch(...) { getQueue().sync(); - ptr = getMemoryManager().alloc(bytes, true); + ptr = memoryManager().alloc(bytes, true); } return ptr; } @@ -139,52 +83,52 @@ void* memAllocUser(const size_t &bytes) template void memFree(T *ptr) { - return getMemoryManager().unlock((void *)ptr, false); + return memoryManager().unlock((void *)ptr, false); } void memFreeUser(void *ptr) { - getMemoryManager().unlock((void *)ptr, true); + memoryManager().unlock((void *)ptr, true); } void memLock(const void *ptr) { - getMemoryManager().userLock((void *)ptr); + memoryManager().userLock((void *)ptr); } bool isLocked(const void *ptr) { - return getMemoryManager().isUserLocked((void *)ptr); + return memoryManager().isUserLocked((void *)ptr); } void memUnlock(const void *ptr) { - getMemoryManager().userUnlock((void *)ptr); + memoryManager().userUnlock((void *)ptr); } void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, size_t *lock_bytes, size_t *lock_buffers) { getQueue().sync(); - getMemoryManager().bufferInfo(alloc_bytes, alloc_buffers, + memoryManager().bufferInfo(alloc_bytes, alloc_buffers, lock_bytes, lock_buffers); } template T* pinnedAlloc(const size_t &elements) { - return (T *)getMemoryManager().alloc(elements * sizeof(T), false); + return (T *)memoryManager().alloc(elements * sizeof(T), false); } template void pinnedFree(T* ptr) { - return getMemoryManager().unlock((void *)ptr, false); + return memoryManager().unlock((void *)ptr, false); } bool checkMemoryLimit() { - return getMemoryManager().checkMemoryLimit(); + return memoryManager().checkMemoryLimit(); } #define INSTANTIATE(T) \ @@ -206,4 +150,45 @@ INSTANTIATE(uintl) INSTANTIATE(ushort) INSTANTIATE(short ) +MemoryManager::MemoryManager() + : common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, + AF_MEM_DEBUG || AF_CPU_MEM_DEBUG) +{ + this->setMaxMemorySize(); +} + +MemoryManager::~MemoryManager() +{ + common::lock_guard_t lock(this->memory_mutex); + for (int n = 0; n < cpu::getDeviceCount(); n++) { + try { + cpu::setDevice(n); + garbageCollect(); + } catch(AfError err) { + continue; // Do not throw any errors while shutting down + } + } +} + +int MemoryManager::getActiveDeviceId() +{ + return cpu::getActiveDeviceId(); +} + +size_t MemoryManager::getMaxMemorySize(int id) +{ + return cpu::getDeviceMemorySize(id); +} + +void *MemoryManager::nativeAlloc(const size_t bytes) +{ + void *ptr = malloc(bytes); + if (!ptr) AF_ERROR("Unable to allocate memory", AF_ERR_NO_MEM); + return ptr; +} + +void MemoryManager::nativeFree(void *ptr) +{ + return free((void *)ptr); +} } diff --git a/src/backend/cpu/memory.hpp b/src/backend/cpu/memory.hpp index b2105c5af4..e85c89fc94 100644 --- a/src/backend/cpu/memory.hpp +++ b/src/backend/cpu/memory.hpp @@ -9,36 +9,48 @@ #pragma once #include +#include namespace cpu { - template T* memAlloc(const size_t &elements); - void *memAllocUser(const size_t &bytes); +template T* memAlloc(const size_t &elements); +void *memAllocUser(const size_t &bytes); - // Need these as 2 separate function and not a default argument - // This is because it is used as the deleter in shared pointer - // which cannot support default arguments - template void memFree(T* ptr); - void memFreeUser(void* ptr); +// Need these as 2 separate function and not a default argument +// This is because it is used as the deleter in shared pointer +// which cannot support default arguments +template void memFree(T* ptr); +void memFreeUser(void* ptr); - void memLock(const void *ptr); - void memUnlock(const void *ptr); - bool isLocked(const void *ptr); +void memLock(const void *ptr); +void memUnlock(const void *ptr); +bool isLocked(const void *ptr); - template T* pinnedAlloc(const size_t &elements); - template void pinnedFree(T* ptr); +template T* pinnedAlloc(const size_t &elements); +template void pinnedFree(T* ptr); - size_t getMaxBytes(); - unsigned getMaxBuffers(); +size_t getMaxBytes(); +unsigned getMaxBuffers(); - void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, - size_t *lock_bytes, size_t *lock_buffers); - void garbageCollect(); - void pinnedGarbageCollect(); +void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, + size_t *lock_bytes, size_t *lock_buffers); +void garbageCollect(); +void pinnedGarbageCollect(); - void printMemInfo(const char *msg, const int device); +void printMemInfo(const char *msg, const int device); - void setMemStepSize(size_t step_bytes); - size_t getMemStepSize(void); - bool checkMemoryLimit(); +void setMemStepSize(size_t step_bytes); +size_t getMemStepSize(void); +bool checkMemoryLimit(); + +class MemoryManager : public common::MemoryManager +{ + public: + MemoryManager(); + ~MemoryManager(); + int getActiveDeviceId(); + size_t getMaxMemorySize(int id); + void *nativeAlloc(const size_t bytes); + void nativeFree(void *ptr); +}; } diff --git a/src/backend/cpu/platform.cpp b/src/backend/cpu/platform.cpp index 5afd8316bf..af694f90e1 100644 --- a/src/backend/cpu/platform.cpp +++ b/src/backend/cpu/platform.cpp @@ -9,85 +9,15 @@ #include #include -#include -#include -#include -#include -#include -#include #include #include -#include #include -#include - - -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(_WIN64) -#define CPUID_CAPABLE USE_CPUID -#else -#define CPUID_CAPABLE 0 -#endif -#ifdef _WIN32 -#include -#include -typedef unsigned __int32 uint32_t; -#else -#include -#endif +#include +#include using namespace std; -#if CPUID_CAPABLE - -#define MAX_INTEL_TOP_LVL 4 - -class CPUID { - uint32_t regs[4]; - - public: - explicit CPUID(unsigned funcId, unsigned subFuncId) { -#ifdef _WIN32 - __cpuidex((int *)regs, (int)funcId, (int)subFuncId); - -#else - asm volatile - ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) - : "a" (funcId), "c" (subFuncId)); -#endif - } - - inline const uint32_t &EAX() const { return regs[0]; } - inline const uint32_t &EBX() const { return regs[1]; } - inline const uint32_t &ECX() const { return regs[2]; } - inline const uint32_t &EDX() const { return regs[3]; } -}; - -#endif - -class CPUInfo { - public: - CPUInfo(); - string vendor() const { return mVendorId; } - string model() const { return mModelName; } - int threads() const { return mNumLogCpus; } - - private: - // Bit positions for data extractions - static const uint32_t LVL_NUM = 0x000000FF; - static const uint32_t LVL_TYPE = 0x0000FF00; - static const uint32_t LVL_CORES = 0x0000FFFF; - static const uint32_t HTT_POS = 0x10000000; - - // Attributes - string mVendorId; - string mModelName; - int mNumSMT; - int mNumCores; - int mNumLogCpus; - bool mIsHTT; -}; - #if !CPUID_CAPABLE CPUInfo::CPUInfo() @@ -116,7 +46,9 @@ CPUInfo::CPUInfo() mVendorId += string((const char *)&cpuID0.ECX(), 4); string upVId = mVendorId; + for_each(upVId.begin(), upVId.end(), [](char& in) { in = ::toupper(in); }); + // Get num of cores if (upVId.find("INTEL") != std::string::npos) { mVendorId = "Intel"; @@ -166,8 +98,7 @@ CPUInfo::CPUInfo() mNumCores = mNumLogCpus = 1; } } else { - mVendorId = "Unkown, probably ARM"; - cout<< "Unexpected vendor id" < queues; - return queues[idx]; +CPUInfo DeviceManager::getCPUInfo() const +{ + return cinfo; } void sync(int device) { - getQueue().sync(); + getQueue(device).sync(); } bool& evalFlag() @@ -317,5 +257,21 @@ bool& evalFlag() return flag; } +MemoryManager& memoryManager() +{ + static std::once_flag flag; + + DeviceManager& inst = DeviceManager::getInstance(); + + std::call_once(flag, [&]() { inst.memManager.reset(new MemoryManager()); }); + + return *(inst.memManager.get()); +} + +DeviceManager& DeviceManager::getInstance() +{ + static DeviceManager my_instance; + return my_instance; +} } diff --git a/src/backend/cpu/platform.hpp b/src/backend/cpu/platform.hpp index edf7a072fc..eeefbbec88 100644 --- a/src/backend/cpu/platform.hpp +++ b/src/backend/cpu/platform.hpp @@ -9,34 +9,139 @@ #pragma once +#include +#include +#include +#include #include +#include +#include -namespace cpu { - class queue; +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(_WIN64) +#define CPUID_CAPABLE USE_CPUID +#else +#define CPUID_CAPABLE 0 +#endif - int getBackend(); +#ifdef _WIN32 +#include +#include +typedef unsigned __int32 uint32_t; +#else +#include +#endif - std::string getDeviceInfo(); +#if CPUID_CAPABLE - bool isDoubleSupported(int device); +#define MAX_INTEL_TOP_LVL 4 - void devprop(char* d_name, char* d_platform, char *d_toolkit, char* d_compute); +class CPUID { + uint32_t regs[4]; - int getDeviceCount(); + public: + explicit CPUID(unsigned funcId, unsigned subFuncId) { +#ifdef _WIN32 + __cpuidex((int *)regs, (int)funcId, (int)subFuncId); - int setDevice(int device); +#else + asm volatile + ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) + : "a" (funcId), "c" (subFuncId)); +#endif + } - int getActiveDeviceId(); + inline const uint32_t &EAX() const { return regs[0]; } + inline const uint32_t &EBX() const { return regs[1]; } + inline const uint32_t &ECX() const { return regs[2]; } + inline const uint32_t &EDX() const { return regs[3]; } +}; - size_t getDeviceMemorySize(int device); +#endif - size_t getHostMemorySize(); +class CPUInfo { + public: + CPUInfo(); + std::string vendor() const { return mVendorId; } + std::string model() const { return mModelName; } + int threads() const { return mNumLogCpus; } - void sync(int device); + private: + // Bit positions for data extractions + static const uint32_t LVL_NUM = 0x000000FF; + static const uint32_t LVL_TYPE = 0x0000FF00; + static const uint32_t LVL_CORES = 0x0000FFFF; + static const uint32_t HTT_POS = 0x10000000; - queue& getQueue(int idx = 0); + // Attributes + std::string mVendorId; + std::string mModelName; + int mNumSMT; + int mNumCores; + int mNumLogCpus; + bool mIsHTT; +}; - unsigned getMaxJitSize(); +namespace cpu +{ +int getBackend(); - bool& evalFlag(); +std::string getDeviceInfo(); + +bool isDoubleSupported(int device); + +void devprop(char* d_name, char* d_platform, char *d_toolkit, char* d_compute); + +unsigned getMaxJitSize(); + +int getDeviceCount(); + +int getActiveDeviceId(); + +size_t getDeviceMemorySize(int device); + +size_t getHostMemorySize(); + +int setDevice(int device); + +queue& getQueue(int device=0); + +void sync(int device); + +bool& evalFlag(); + +MemoryManager& memoryManager(); + +class DeviceManager +{ + public: + static const int MAX_QUEUES = 1; + static const int NUM_DEVICES = 1; + static const int ACTIVE_DEVICE_ID = 0; + static const bool IS_DOUBLE_SUPPORTED = true; + + static DeviceManager& getInstance(); + + friend queue& getQueue(int device); + + friend MemoryManager& memoryManager(); + + CPUInfo getCPUInfo() const; + + private: + DeviceManager() { + } + + // Following two declarations are required to + // avoid copying accidental copy/assignment + // of instance returned by getInstance to other + // variables + DeviceManager(DeviceManager const&); + void operator=(DeviceManager const&); + + // Attributes + const CPUInfo cinfo; + std::array queues; + + std::unique_ptr memManager; +}; } diff --git a/src/backend/cpu/types.hpp b/src/backend/cpu/types.hpp index 0776df783c..84e62f8286 100644 --- a/src/backend/cpu/types.hpp +++ b/src/backend/cpu/types.hpp @@ -12,14 +12,13 @@ namespace cpu { - typedef std::complex cfloat; - typedef std::complex cdouble; - typedef unsigned int uint; - typedef unsigned char uchar; - typedef unsigned short ushort; - - template struct is_complex { static const bool value = false; }; - template<> struct is_complex { static const bool value = true; }; - template<> struct is_complex { static const bool value = true; }; +typedef std::complex cfloat; +typedef std::complex cdouble; +typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned short ushort; +template struct is_complex { static const bool value = false; }; +template<> struct is_complex { static const bool value = true; }; +template<> struct is_complex { static const bool value = true; }; } diff --git a/src/backend/cuda/Array.cpp b/src/backend/cuda/Array.cpp index 90b9f8d8d1..495f35807e 100644 --- a/src/backend/cuda/Array.cpp +++ b/src/backend/cuda/Array.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -58,12 +57,12 @@ namespace cuda #endif if (!is_device) { CUDA_CHECK(cudaMemcpyAsync(data.get(), in_data, dims.elements() * sizeof(T), - cudaMemcpyHostToDevice, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyHostToDevice, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); } else if (copy_device) { CUDA_CHECK(cudaMemcpyAsync(data.get(), in_data, dims.elements() * sizeof(T), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); } } @@ -107,7 +106,7 @@ namespace cuda owner(true) { if (!is_device) { - cudaStream_t stream = getStream(getActiveDeviceId()); + cudaStream_t stream = getActiveStream(); CUDA_CHECK(cudaMemcpyAsync(data.get(), in_data, info.total() * sizeof(T), cudaMemcpyHostToDevice, stream)); CUDA_CHECK(cudaStreamSynchronize(stream)); @@ -241,8 +240,13 @@ namespace cuda unsigned length =0, buf_count = 0, bytes = 0; Node *n = node.get(); - n->getInfo(length, buf_count, bytes); - n->resetFlags(); + JIT::Node_map_t nodes_map; + n->getNodesMap(nodes_map); + + for(auto &entry : nodes_map) { + Node *node = entry.first; + node->getInfo(length, buf_count, bytes); + } if (2 * bytes > lock_bytes) { out.eval(); @@ -340,9 +344,8 @@ namespace cuda T *ptr = arr.get(); - CUDA_CHECK(cudaMemcpyAsync(ptr, data, bytes, cudaMemcpyHostToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + CUDA_CHECK(cudaMemcpyAsync(ptr, data, bytes, cudaMemcpyHostToDevice, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); return; } @@ -357,13 +360,23 @@ namespace cuda T *ptr = arr.get(); - CUDA_CHECK(cudaMemcpyAsync(ptr, data, - bytes, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + CUDA_CHECK(cudaMemcpyAsync(ptr, data, bytes, cudaMemcpyDeviceToDevice, cuda::getActiveStream())); return; } + template + void + Array::setDataDims(const dim4 &new_dims) + { + modDims(new_dims); + data_dims = new_dims; + if (node->isBuffer()) { + node = bufferNodePtr(); + } + } + + #define INSTANTIATE(T) \ template Array createHostDataArray (const dim4 &size, const T * const data); \ template Array createDeviceDataArray (const dim4 &size, const void *data); \ @@ -386,9 +399,12 @@ namespace cuda template void Array::eval(); \ template void Array::eval() const; \ template T* Array::device(); \ - template void writeHostDataArray (Array &arr, const T * const data, const size_t bytes); \ - template void writeDeviceDataArray (Array &arr, const void * const data, const size_t bytes); \ + template void writeHostDataArray (Array &arr, const T * const data, \ + const size_t bytes); \ + template void writeDeviceDataArray (Array &arr, const void * const data, \ + const size_t bytes); \ template void evalMultiple (std::vector*> arrays); \ + template void Array::setDataDims(const dim4 &new_dims); \ INSTANTIATE(float) INSTANTIATE(double) diff --git a/src/backend/cuda/Array.hpp b/src/backend/cuda/Array.hpp index b7092ad867..1136cdaea4 100644 --- a/src/backend/cuda/Array.hpp +++ b/src/backend/cuda/Array.hpp @@ -169,11 +169,7 @@ namespace cuda return data_dims; } - void setDataDims(const dim4 &new_dims) - { - modDims(new_dims); - data_dims = new_dims; - } + void setDataDims(const dim4 &new_dims); size_t getAllocatedBytes() const { @@ -208,19 +204,12 @@ namespace cuda operator Param() { - Param out; - out.ptr = this->get(); - for (int i = 0; i < 4; i++) { - out.dims[i] = dims()[i]; - out.strides[i] = strides()[i]; - } - return out; + return Param(this->get(), this->dims().get(), this->strides().get()); } operator CParam() const { - CParam out(this->get(), this->dims().get(), this->strides().get()); - return out; + return CParam(this->get(), this->dims().get(), this->strides().get()); } JIT::Node_ptr getNode(); diff --git a/src/backend/cuda/CMakeLists.txt b/src/backend/cuda/CMakeLists.txt index 4d134afdae..eb4b27b44b 100644 --- a/src/backend/cuda/CMakeLists.txt +++ b/src/backend/cuda/CMakeLists.txt @@ -188,6 +188,7 @@ FILE(GLOB backend_headers ) FILE(GLOB backend_sources + "../common/*.cpp" "../*.cpp" ) @@ -430,14 +431,16 @@ IF (${libdevice_bc_len} GREATER 0) ADD_DEPENDENCIES(afcuda ${libdevice_targets}) ENDIF() -TARGET_LINK_LIBRARIES(afcuda PRIVATE ${CUDA_CUBLAS_LIBRARIES} +TARGET_LINK_LIBRARIES(afcuda + PRIVATE ${CUDA_CUBLAS_LIBRARIES} PRIVATE ${CUDA_LIBRARIES} PRIVATE ${FreeImage_LIBS} PRIVATE ${CUDA_CUFFT_LIBRARIES} PRIVATE ${CUDA_cusparse_LIBRARY} PRIVATE ${CUDA_cusolver_LIBRARY} PRIVATE ${CUDA_nvvm_LIBRARY} - PRIVATE ${CUDA_CUDA_LIBRARY}) + PRIVATE ${CUDA_CUDA_LIBRARY} + ) LIST(LENGTH GRAPHICS_DEPENDENCIES GRAPHICS_DEPENDENCIES_LEN) IF(${GRAPHICS_DEPENDENCIES_LEN} GREATER 0) diff --git a/src/backend/cuda/GraphicsResourceManager.cpp b/src/backend/cuda/GraphicsResourceManager.cpp new file mode 100644 index 0000000000..b27da053ad --- /dev/null +++ b/src/backend/cuda/GraphicsResourceManager.cpp @@ -0,0 +1,34 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#if defined(WITH_GRAPHICS) +#include +#include + +namespace cuda +{ +ShrdResVector GraphicsResourceManager::registerResources(std::vector resources) +{ + ShrdResVector output; + + auto deleter = [](CGR_t* handle) { + CUDA_CHECK(cudaGraphicsUnregisterResource(*handle)); + delete handle; + }; + + for (auto id: resources) { + CGR_t r; + CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&r, id, cudaGraphicsMapFlagsWriteDiscard)); + output.emplace_back(new CGR_t(r), deleter); + } + + return output; +} +} +#endif diff --git a/src/backend/cuda/GraphicsResourceManager.hpp b/src/backend/cuda/GraphicsResourceManager.hpp new file mode 100644 index 0000000000..fb0700d441 --- /dev/null +++ b/src/backend/cuda/GraphicsResourceManager.hpp @@ -0,0 +1,46 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once + +#if defined(WITH_GRAPHICS) +#if defined(OS_WIN) +#include +#endif + +// cuda_gl_interop.h does not include OpenGL headers for ARM +#include +using namespace gl; +#define GL_VERSION gl::GL_VERSION +#define __gl_h_ //FIXME Hack to avoid gl.h inclusion by cuda_gl_interop.h +#include +#include +#include +#include +#include +#include + +namespace cuda +{ +typedef cudaGraphicsResource_t CGR_t; +typedef std::shared_ptr SharedResource; +typedef std::vector ShrdResVector; + +class GraphicsResourceManager : public common::InteropManager +{ + public: + GraphicsResourceManager() {} + ShrdResVector registerResources(std::vector resources); + + protected: + GraphicsResourceManager(GraphicsResourceManager const&); + void operator=(GraphicsResourceManager const&); +}; +} +#endif diff --git a/src/backend/cuda/JIT/BinaryNode.hpp b/src/backend/cuda/JIT/BinaryNode.hpp index a07d83d111..115c892119 100644 --- a/src/backend/cuda/JIT/BinaryNode.hpp +++ b/src/backend/cuda/JIT/BinaryNode.hpp @@ -20,148 +20,68 @@ namespace JIT class BinaryNode : public Node { private: - std::string m_op_str; - Node_ptr m_lhs, m_rhs; - int m_op; - int m_call_type; + const std::string m_op_str; + const int m_op; + const int m_call_type; public: BinaryNode(const char *out_type_str, const char *name_str, const std::string &op_str, Node_ptr lhs, Node_ptr rhs, int op, int call_type) - : Node(out_type_str, name_str, std::max(lhs->getHeight(), rhs->getHeight()) + 1), + : Node(out_type_str, name_str, std::max(lhs->getHeight(), rhs->getHeight()) + 1, {lhs, rhs}), m_op_str(op_str), - m_lhs(lhs), - m_rhs(rhs), m_op(op), m_call_type(call_type) { } - bool isLinear(dim_t dims[4]) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - if (!m_set_is_linear) { - m_linear = m_lhs->isLinear(dims) && m_rhs->isLinear(dims); - m_set_is_linear = true; - } - return m_linear; - } - - void genParams(std::stringstream &kerStream, - std::stringstream &annStream, bool is_linear) - { - if (m_gen_param) return; - if (!(m_lhs->isGenParam())) m_lhs->genParams(kerStream, annStream, is_linear); - if (!(m_rhs->isGenParam())) m_rhs->genParams(kerStream, annStream, is_linear); - m_gen_param = true; - } - - void genOffsets(std::stringstream &kerStream, bool is_linear) - { - if (m_gen_offset) return; - if (!(m_lhs->isGenOffset())) m_lhs->genOffsets(kerStream, is_linear); - if (!(m_rhs->isGenOffset())) m_rhs->genOffsets(kerStream, is_linear); - m_gen_offset = true; - } - - void genKerName(std::stringstream &kerStream) - { - if (m_gen_name) return; - m_lhs->genKerName(kerStream); - m_rhs->genKerName(kerStream); - // Make the hex representation of enum part of the Kernel name kerStream << "_" << std::setw(2) << std::setfill('0') << std::hex << m_op; - kerStream << std::setw(2) << std::setfill('0') << std::hex << m_lhs->getId(); - kerStream << std::setw(2) << std::setfill('0') << std::hex << m_rhs->getId(); - kerStream << std::setw(2) << std::setfill('0') << std::hex << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(2) << std::setfill('0') << std::hex << ids.child_ids[0]; + kerStream << std::setw(2) << std::setfill('0') << std::hex << ids.child_ids[1]; + kerStream << std::setw(2) << std::setfill('0') << std::hex << ids.id << std::dec; } - void genFuncs(std::stringstream &kerStream, str_map_t &declStrs, bool is_linear) + void genFuncs(std::stringstream &kerStream, str_map_t &declStrs, Node_ids ids, bool is_linear) { - if (m_gen_func) return; - - if (!(m_lhs->isGenFunc())) m_lhs->genFuncs(kerStream, declStrs, is_linear); - if (!(m_rhs->isGenFunc())) m_rhs->genFuncs(kerStream, declStrs, is_linear); - if (m_call_type == 0) { std::stringstream declStream; declStream << "declare " << m_type_str << " " << m_op_str - << "(" << m_lhs->getTypeStr() << " , " << m_rhs->getTypeStr() << ")\n"; - - str_map_iter loc = declStrs.find(declStream.str()); - if (loc == declStrs.end()) { - declStrs[declStream.str()] = true; - } + << "(" << m_children[0]->getTypeStr() << " , " + << m_children[1]->getTypeStr() << ")\n"; + declStrs[declStream.str()] = true; - kerStream << "%val" << m_id << " = call " + kerStream << "%val" << ids.id << " = call " << m_type_str << " " << m_op_str << "(" - << m_lhs->getTypeStr() << " " - << "%val" << m_lhs->getId() << ", " - << m_rhs->getTypeStr() << " " - << "%val" << m_rhs->getId() << ")\n"; + << m_children[0]->getTypeStr() << " " + << "%val" << ids.child_ids[0] << ", " + << m_children[1]->getTypeStr() << " " + << "%val" << ids.child_ids[1] << ")\n"; } else { if (m_call_type == 1) { // arithmetic operations - kerStream << "%val" << m_id << " = " + kerStream << "%val" << ids.id << " = " << m_op_str << " " << m_type_str << " " - << "%val" << m_lhs->getId() << ", " - << "%val" << m_rhs->getId() << "\n"; + << "%val" << ids.child_ids[0] << ", " + << "%val" << ids.child_ids[1] << "\n"; } else { // logical operators - kerStream << "%tmp" << m_id << " = " + kerStream << "%tmp" << ids.id << " = " << m_op_str << " " - << m_lhs->getTypeStr() << " " - << "%val" << m_lhs->getId() << ", " - << "%val" << m_rhs->getId() << "\n"; + << m_children[0]->getTypeStr() << " " + << "%val" << ids.child_ids[0] << ", " + << "%val" << ids.child_ids[1] << "\n"; - kerStream << "%val" << m_id << " = " - << "zext i1 %tmp" << m_id << " to i8\n"; + kerStream << "%val" << ids.id << " = " + << "zext i1 %tmp" << ids.id << " to i8\n"; } } - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - id = m_lhs->setId(id); - id = m_rhs->setId(id); - m_id = id; - m_set_id = true; - return m_id + 1; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) - { - if (m_set_id) return; - m_lhs->getInfo(len, buf_count, bytes); - m_rhs->getInfo(len, buf_count, bytes); - len++; - m_set_id = true; - return; - } - - void resetFlags() - { - if (m_set_id) { - resetCommonFlags(); - m_lhs->resetFlags(); - m_rhs->resetFlags(); - } - } - - void setArgs(std::vector &args, bool is_linear) - { - if (m_set_arg) return; - m_lhs->setArgs(args, is_linear); - m_rhs->setArgs(args, is_linear); - m_set_arg = true; } }; diff --git a/src/backend/cuda/JIT/BufferNode.hpp b/src/backend/cuda/JIT/BufferNode.hpp index bdc692b447..c6a03d2899 100644 --- a/src/backend/cuda/JIT/BufferNode.hpp +++ b/src/backend/cuda/JIT/BufferNode.hpp @@ -11,6 +11,7 @@ #include "Node.hpp" #include #include +#include namespace cuda { @@ -34,163 +35,132 @@ namespace JIT std::shared_ptr m_data; Param m_param; unsigned m_bytes; + std::once_flag m_set_data_flag; bool m_linear_buffer; public: BufferNode(const char *type_str, const char *name_str) - : Node(type_str, name_str, 0) + : Node(type_str, name_str, 0, {}) { } void setData(Param param, std::shared_ptr data, const unsigned bytes, bool is_linear) { - m_param = param; - m_data = data; - m_bytes = bytes; - m_linear_buffer = is_linear; + std::call_once(m_set_data_flag, [this, param, data, bytes, is_linear]() { + m_param = param; + m_data = data; + m_bytes = bytes; + m_linear_buffer = is_linear; + }); } bool isLinear(dim_t dims[4]) { - if (!m_set_is_linear) { - bool same_dims = true; - for (int i = 0; same_dims && i < 4; i++) { - same_dims &= (dims[i] == m_param.dims[i]); - } - m_linear = m_linear_buffer && same_dims; - m_set_is_linear = true; + bool same_dims = true; + for (int i = 0; same_dims && i < 4; i++) { + same_dims &= (dims[i] == m_param.dims[i]); } - return m_linear; + return m_linear_buffer && same_dims; } bool isBuffer() { return true; } - void genKerName(std::stringstream &kerStream) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - if (m_gen_name) return; - kerStream << "_" << m_name_str; - kerStream << std::setw(2) << std::setfill('0') << std::hex << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(2) << std::setfill('0') << std::hex << ids.id << std::dec; } void genParams(std::stringstream &kerStream, - std::stringstream &annStream, bool is_linear) + std::stringstream &annStream, + int id, + bool is_linear) { - if (m_gen_param) return; - kerStream << m_type_str << "* %in" << m_id << ",\n"; + kerStream << m_type_str << "* %in" << id << ",\n"; annStream << m_type_str << "*,\n"; if (!is_linear) { - kerStream << "i32 %dim0" << m_id << "," - << "i32 %dim1" << m_id << "," - << "i32 %dim2" << m_id << "," - << "i32 %dim3" << m_id << "," + kerStream << "i32 %dim0" << id << "," + << "i32 %dim1" << id << "," + << "i32 %dim2" << id << "," + << "i32 %dim3" << id << "," << "\n" - << "i32 %str1" << m_id << "," - << "i32 %str2" << m_id << "," - << "i32 %str3" << m_id << "," + << "i32 %str1" << id << "," + << "i32 %str2" << id << "," + << "i32 %str3" << id << "," << "\n"; annStream << "i32, i32, i32, i32,\n" << "i32, i32, i32,\n"; } - - m_gen_param = true; } - void genOffsets(std::stringstream &kerStream, bool is_linear) + void genOffsets(std::stringstream &kerStream, int id, bool is_linear) { - if (m_gen_offset) return; - if (!is_linear) { - kerStream << "%b3" << m_id << " = icmp slt i32 %id3, %dim3" << m_id << "\n"; - kerStream << "%b2" << m_id << " = icmp slt i32 %id2, %dim2" << m_id << "\n"; - kerStream << "%b1" << m_id << " = icmp slt i32 %id1, %dim1" << m_id << "\n"; - kerStream << "%b0" << m_id << " = icmp slt i32 %id0, %dim0" << m_id << "\n"; + kerStream << "%b3" << id << " = icmp slt i32 %id3, %dim3" << id << "\n"; + kerStream << "%b2" << id << " = icmp slt i32 %id2, %dim2" << id << "\n"; + kerStream << "%b1" << id << " = icmp slt i32 %id1, %dim1" << id << "\n"; + kerStream << "%b0" << id << " = icmp slt i32 %id0, %dim0" << id << "\n"; - kerStream << "%c3" << m_id << " = zext i1 %b3" << m_id << " to i32\n"; - kerStream << "%c2" << m_id << " = zext i1 %b2" << m_id << " to i32\n"; - kerStream << "%c1" << m_id << " = zext i1 %b1" << m_id << " to i32\n"; - kerStream << "%c0" << m_id << " = zext i1 %b0" << m_id << " to i32\n"; + kerStream << "%c3" << id << " = zext i1 %b3" << id << " to i32\n"; + kerStream << "%c2" << id << " = zext i1 %b2" << id << " to i32\n"; + kerStream << "%c1" << id << " = zext i1 %b1" << id << " to i32\n"; + kerStream << "%c0" << id << " = zext i1 %b0" << id << " to i32\n"; - kerStream << "%d3" << m_id << " = mul i32 %c3" << m_id << ", %id3\n"; - kerStream << "%d2" << m_id << " = mul i32 %c2" << m_id << ", %id2\n"; - kerStream << "%d1" << m_id << " = mul i32 %c1" << m_id << ", %id1\n"; - kerStream << "%d0" << m_id << " = mul i32 %c0" << m_id << ", %id0\n"; + kerStream << "%d3" << id << " = mul i32 %c3" << id << ", %id3\n"; + kerStream << "%d2" << id << " = mul i32 %c2" << id << ", %id2\n"; + kerStream << "%d1" << id << " = mul i32 %c1" << id << ", %id1\n"; + kerStream << "%d0" << id << " = mul i32 %c0" << id << ", %id0\n"; - kerStream << "%off3i" << m_id << " = mul i32 %d3" << m_id - << ", %str3" << m_id << "\n"; + kerStream << "%off3i" << id << " = mul i32 %d3" << id + << ", %str3" << id << "\n"; - kerStream << "%off2i" << m_id << " = mul i32 %d2" << m_id - << ", %str2" << m_id << "\n"; + kerStream << "%off2i" << id << " = mul i32 %d2" << id + << ", %str2" << id << "\n"; - kerStream << "%off1i" << m_id << " = mul i32 %d1" << m_id - << ", %str1" << m_id << "\n"; + kerStream << "%off1i" << id << " = mul i32 %d1" << id + << ", %str1" << id << "\n"; - kerStream << "%off23i" << m_id << " = add i32 %off2i" - << m_id << ", %off3i" << m_id << "\n"; + kerStream << "%off23i" << id << " = add i32 %off2i" + << id << ", %off3i" << id << "\n"; - kerStream << "%off123i" << m_id << " = add i32 %off23i" - << m_id << ", %off1i" << m_id << "\n"; + kerStream << "%off123i" << id << " = add i32 %off23i" + << id << ", %off1i" << id << "\n"; - kerStream << "%idxa" << m_id << " = add i32 %off123i" - << m_id << ", %d0" << m_id << "\n"; + kerStream << "%idxa" << id << " = add i32 %off123i" + << id << ", %d0" << id << "\n"; - kerStream << "%idx" << m_id << " = sext i32 %idxa" << m_id <<" to i64\n\n"; + kerStream << "%idx" << id << " = sext i32 %idxa" << id <<" to i64\n\n"; } - - m_gen_offset = true; } - void genFuncs(std::stringstream &kerStream, str_map_t &declStrs, bool is_linear) + void genFuncs(std::stringstream &kerStream, str_map_t &declStrs, Node_ids ids, bool is_linear) { - if (m_gen_func) return; - - kerStream << "%inIdx" << m_id << " = " - << "getelementptr inbounds " << m_type_str << "* %in" << m_id + kerStream << "%inIdx" << ids.id << " = " + << "getelementptr inbounds " << m_type_str << "* %in" << ids.id << ", i64 %idx"; - if (!is_linear) kerStream << m_id; + if (!is_linear) kerStream << ids.id; kerStream << "\n"; - kerStream << "%val" << m_id << " = " << "load " - << m_type_str << "* %inIdx" << m_id << "\n\n"; - - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - - m_id = id; - m_set_id = true; + kerStream << "%val" << ids.id << " = " << "load " + << m_type_str << "* %inIdx" << ids.id << "\n\n"; - return m_id + 1; } void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) { - if (m_set_id) return; - len++; buf_count++; bytes += m_bytes; - m_set_id = true; return; } - void resetFlags() - { - if (m_set_id) resetCommonFlags(); - } - void setArgs(std::vector &args, bool is_linear) { - if (m_set_arg) return; args.push_back((void *)&(m_param.ptr)); if (!is_linear) { @@ -202,7 +172,6 @@ namespace JIT args.push_back((void *)&m_param.strides[2]); args.push_back((void *)&m_param.strides[3]); } - m_set_arg = true; } }; diff --git a/src/backend/cuda/JIT/Node.hpp b/src/backend/cuda/JIT/Node.hpp index bd4307dd1e..035692550d 100644 --- a/src/backend/cuda/JIT/Node.hpp +++ b/src/backend/cuda/JIT/Node.hpp @@ -10,7 +10,7 @@ #pragma once #include -#include +#include #include #include #include @@ -20,97 +20,77 @@ namespace cuda namespace JIT { - typedef std::map str_map_t; + class Node; + + typedef struct + { + int id; + std::vector child_ids; + } Node_ids; + + typedef std::unordered_map str_map_t; typedef str_map_t::iterator str_map_iter; + typedef std::shared_ptr Node_ptr; + typedef std::unordered_map Node_map_t; + typedef Node_map_t::iterator Node_map_iter; class Node { protected: const std::string m_type_str; const std::string m_name_str; - int m_id; const int m_height; - bool m_set_id; - bool m_gen_func; - bool m_gen_param; - bool m_gen_offset; - bool m_set_arg; - bool m_gen_name; - bool m_linear; - bool m_set_is_linear; - - protected: - - void resetCommonFlags() - { - m_set_id = false; - m_gen_func = false; - m_gen_param = false; - m_gen_offset = false; - m_set_arg = false; - m_gen_name = false; - m_linear = false; - m_set_is_linear = false; - } - + const std::vector m_children; public: - Node(const char *type_str, const char *name_str, const int height) + Node(const char *type_str, const char *name_str, const int height, + const std::vector children) : m_type_str(type_str), m_name_str(name_str), - m_id(-1), m_height(height), - m_set_id(false), - m_gen_func(false), - m_gen_param(false), - m_gen_offset(false), - m_set_arg(false), - m_gen_name(false), - m_linear(false), - m_set_is_linear(false) + m_children(children) {} - virtual void genKerName(std::stringstream &kerStream) {} + void getNodesMap(Node_map_t &node_map) + { + if (node_map.find(this) == node_map.end()) { + Node_ids ids; + for (const auto &child : m_children) { + child->getNodesMap(node_map); + ids.child_ids.push_back(node_map[child.get()].id); + } + ids.id = node_map.size(); + node_map[this] = ids; + } + } + + virtual void genKerName(std::stringstream &kerStream, Node_ids ids) {} virtual void genParams (std::stringstream &kerStream, - std::stringstream &annStream, bool is_linear) {} - virtual void genOffsets (std::stringstream &kerStream, bool is_linear) {} - virtual void genFuncs (std::stringstream &kerStream, str_map_t &declStrs, bool is_linear) - { m_gen_func = true;} + std::stringstream &annStream, + int id, bool is_linear) {} + virtual void genOffsets (std::stringstream &kerStream, int id, bool is_linear) {} + virtual void genFuncs (std::stringstream &kerStream, str_map_t &declStrs, + Node_ids id, bool is_linear) + {} - virtual int setId(int id) { m_set_id = true; return id; } - virtual void setArgs(std::vector &args, bool is_linear) { m_set_arg = true; } + virtual void setArgs(std::vector &args, bool is_linear) {} virtual bool isLinear(dim_t dims[4]) { return true; } - virtual void resetFlags() - { - resetCommonFlags(); - } - virtual void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) { - len = 0; - buf_count = 0; - bytes = 0; + len++; } virtual bool isBuffer() { return false; } std::string getTypeStr() { return m_type_str; } - bool isGenFunc() { return m_gen_func; } - bool isGenParam() { return m_gen_param; } - bool isGenOffset() { return m_gen_offset; } - - int getId() { return m_id; } int getHeight() { return m_height; } std::string getNameStr() { return m_name_str; } virtual ~Node() {} }; - - typedef std::shared_ptr Node_ptr; - } } diff --git a/src/backend/cuda/JIT/ScalarNode.hpp b/src/backend/cuda/JIT/ScalarNode.hpp index f7a1e33d99..264b7be61e 100644 --- a/src/backend/cuda/JIT/ScalarNode.hpp +++ b/src/backend/cuda/JIT/ScalarNode.hpp @@ -26,73 +26,29 @@ namespace JIT public: ScalarNode(T val) - : Node(irname(), afShortName(false), 0), + : Node(irname(), afShortName(false), 0, {}), m_val(val) { } - bool isLinear(dim_t dims[4]) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - return true; - } - - void genKerName(std::stringstream &kerStream) - { - if (m_gen_name) return; - kerStream << "_" << m_name_str; - kerStream << std::setw(2) << std::setfill('0') << std::hex << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(2) << std::setfill('0') << std::hex << ids.id << std::dec; } void genParams(std::stringstream &kerStream, std::stringstream &annStream, + int id, bool is_linear) { - if (m_gen_param) return; - kerStream << m_type_str << " %val" << m_id << ", " << std::endl; + kerStream << m_type_str << " %val" << id << ", " << std::endl; annStream << m_type_str << ",\n"; - m_gen_param = true; - } - - void genOffsets(std::stringstream &kerStream, bool is_linear) - { - if (m_gen_offset) return; - m_gen_offset = true; - } - - void genFuncs(std::stringstream &kerStream, str_map_t &declStrs, bool is_linear) - { - if (m_gen_func) return; - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - m_id = id; - m_set_id = true; - return m_id + 1; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) - { - if (m_set_id) return; - len++; - m_set_id = true; - return; - } - - void resetFlags() - { - if (m_set_id) resetCommonFlags(); } void setArgs(std::vector &args, bool is_linear) { - if (m_set_arg) return; args.push_back((void *)&m_val); - m_set_arg = true; } }; } diff --git a/src/backend/cuda/JIT/UnaryNode.hpp b/src/backend/cuda/JIT/UnaryNode.hpp index a85e05371c..ac334c14b8 100644 --- a/src/backend/cuda/JIT/UnaryNode.hpp +++ b/src/backend/cuda/JIT/UnaryNode.hpp @@ -20,139 +20,64 @@ namespace JIT class UnaryNode : public Node { private: - std::string m_op_str; - Node_ptr m_child; - int m_op; - bool m_is_check; + const std::string m_op_str; + const int m_op; + const bool m_is_check; public: UnaryNode(const char *out_type_str, const char *name_str, const std::string &op_str, Node_ptr child, int op, bool is_check=false) - : Node(out_type_str, name_str, child->getHeight() + 1), + : Node(out_type_str, name_str, child->getHeight() + 1, {child}), m_op_str(op_str), - m_child(child), m_op(op), m_is_check(is_check) { } - bool isLinear(dim_t dims[4]) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - if (!m_set_is_linear) { - m_linear = m_child->isLinear(dims); - m_set_is_linear = true; - } - return m_linear; - } - - void genParams(std::stringstream &kerStream, - std::stringstream &annStream, bool is_linear) - { - if (m_gen_param) return; - if (!(m_child->isGenParam())) m_child->genParams(kerStream, annStream, is_linear); - m_gen_param = true; - } - - - void genOffsets(std::stringstream &kerStream, bool is_linear) - { - if (m_gen_offset) return; - if (!(m_child->isGenOffset())) m_child->genOffsets(kerStream, is_linear); - m_gen_offset = true; - } - - void genKerName(std::stringstream &kerStream) - { - if (m_gen_name) return; - - m_child->genKerName(kerStream); - // Make the hex representation of enum part of the Kernel name kerStream << "_" << std::setw(2) << std::setfill('0') << std::hex << m_op; - kerStream << std::setw(2) << std::setfill('0') << std::hex << m_child->getId(); - kerStream << std::setw(2) << std::setfill('0') << std::hex << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(2) << std::setfill('0') << std::hex << ids.child_ids[0]; + kerStream << std::setw(2) << std::setfill('0') << std::hex << ids.id << std::dec; } - void genFuncs(std::stringstream &kerStream, str_map_t &declStrs, bool is_linear) + void genFuncs(std::stringstream &kerStream, str_map_t &declStrs, Node_ids ids, bool is_linear) { - if (m_gen_func) return; - - if (!(m_child->isGenFunc())) m_child->genFuncs(kerStream, declStrs, is_linear); - std::stringstream declStream; if (m_is_check) { declStream << "declare " << "i32 " << m_op_str - << "(" << m_child->getTypeStr() << ")\n"; + << "(" << m_children[0]->getTypeStr() << ")\n"; } else { declStream << "declare " << m_type_str << " " << m_op_str - << "(" << m_child->getTypeStr() << ")\n"; + << "(" << m_children[0]->getTypeStr() << ")\n"; } - str_map_iter loc = declStrs.find(declStream.str()); - if (loc == declStrs.end()) { - declStrs[declStream.str()] = true; - } + declStrs[declStream.str()] = true; if (m_is_check) { - kerStream << "%tmp" << m_id << " = call i32 " + kerStream << "%tmp" << ids.id << " = call i32 " << m_op_str << "(" - << m_child->getTypeStr() << " " - << "%val" << m_child->getId() << ")\n"; + << m_children[0]->getTypeStr() << " " + << "%val" << ids.child_ids[0] << ")\n"; if (m_type_str[0] == 'i') { - kerStream << "%val" << m_id << " = " - << "trunc i32 %tmp" << m_id << " to " << m_type_str << "\n"; + kerStream << "%val" << ids.id << " = " + << "trunc i32 %tmp" << ids.id << " to " << m_type_str << "\n"; } else { - kerStream << "%val" << m_id << " = " - << "sitofp i32 %tmp" << m_id << " to " << m_type_str << "\n"; + kerStream << "%val" << ids.id << " = " + << "sitofp i32 %tmp" << ids.id << " to " << m_type_str << "\n"; } } else { - kerStream << "%val" << m_id << " = call " + kerStream << "%val" << ids.id << " = call " << m_type_str << " " << m_op_str << "(" - << m_child->getTypeStr() << " " - << "%val" << m_child->getId() << ")\n"; + << m_children[0]->getTypeStr() << " " + << "%val" << ids.child_ids[0] << ")\n"; } - - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - id = m_child->setId(id); - m_id = id; - m_set_id = true; - return m_id + 1; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) - { - if (m_set_id) return; - m_child->getInfo(len, buf_count, bytes); - len++; - m_set_id = true; - return; - } - - void resetFlags() - { - if (m_set_id) { - m_is_check = false; - resetCommonFlags(); - m_child->resetFlags(); - } - } - - void setArgs(std::vector &args, bool is_linear) - { - if (m_set_arg) return; - m_child->setArgs(args, is_linear); - m_set_arg = true; } }; diff --git a/src/backend/cuda/Param.hpp b/src/backend/cuda/Param.hpp index c07aaa72b5..6bee8a5106 100644 --- a/src/backend/cuda/Param.hpp +++ b/src/backend/cuda/Param.hpp @@ -15,11 +15,25 @@ namespace cuda { template -struct Param +class Param { +public: T *ptr; dim_t dims[4]; dim_t strides[4]; + + __DH__ Param() : ptr(nullptr) + { + } + + __DH__ Param(T *iptr, const dim_t *idims, const dim_t *istrides) : + ptr(iptr) + { + for (int i = 0; i < 4; i++) { + dims[i] = idims[i]; + strides[i] = istrides[i]; + } + } }; template diff --git a/src/backend/cuda/ThrustAllocator.cuh b/src/backend/cuda/ThrustAllocator.cuh new file mode 100644 index 0000000000..40cb899e01 --- /dev/null +++ b/src/backend/cuda/ThrustAllocator.cuh @@ -0,0 +1,44 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once + +#include +#include +#include + +//Below Class definition is found at the following URL +//http://stackoverflow.com/questions/9007343/mix-custom-memory-managment-and-thrust-in-cuda + +namespace cuda +{ +template +struct ThrustAllocator : thrust::device_malloc_allocator +{ + // shorthand for the name of the base class + typedef thrust::device_malloc_allocator super_t; + + // get access to some of the base class's typedefs + // note that because we inherited from device_malloc_allocator, + // pointer is actually thrust::device_ptr + typedef typename super_t::pointer pointer; + + typedef typename super_t::size_type size_type; + + pointer allocate(size_type elements) + { + return thrust::device_ptr(memAlloc(elements));// delegate to ArrayFire allocator + } + + void deallocate(pointer p, size_type n) + { + memFree(p.get());// delegate to ArrayFire allocator + } +}; +} diff --git a/src/backend/cuda/blas.cpp b/src/backend/cuda/blas.cpp index 9d3b9ca7b7..921fe685ea 100644 --- a/src/backend/cuda/blas.cpp +++ b/src/backend/cuda/blas.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -25,8 +25,6 @@ namespace cuda { -using cublas::getHandle; - cublasOperation_t toCblasTranspose(af_mat_prop opt) { @@ -173,7 +171,7 @@ Array matmul(const Array &lhs, const Array &rhs, if(rDims[bColDim] == 1) { N = lDims[aColDim]; CUBLAS_CHECK(gemv_func()( - getHandle(), + blasHandle(), lOpts, lDims[0], lDims[1], @@ -184,7 +182,7 @@ Array matmul(const Array &lhs, const Array &rhs, out.get(), 1)); } else { CUBLAS_CHECK(gemm_func()( - getHandle(), + blasHandle(), lOpts, rOpts, M, N, K, @@ -226,7 +224,7 @@ void trsm(const Array &lhs, Array &rhs, af_mat_prop trans, dim4 rStrides = rhs.strides(); CUBLAS_CHECK(trsm_func()( - getHandle(), + blasHandle(), is_left ? CUBLAS_SIDE_LEFT : CUBLAS_SIDE_RIGHT, is_upper ? CUBLAS_FILL_MODE_UPPER : CUBLAS_FILL_MODE_LOWER, toCblasTranspose(trans), diff --git a/src/backend/cuda/cholesky.cu b/src/backend/cuda/cholesky.cu index 8a9ea550e5..dc7a239f8b 100644 --- a/src/backend/cuda/cholesky.cu +++ b/src/backend/cuda/cholesky.cu @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -24,8 +24,6 @@ namespace cuda { -using cusolver::getDnHandle; - //cusolverStatus_t cusolverDn<>potrf_bufferSize( // cusolverDnHandle_t handle, // cublasFillMode_t uplo, @@ -112,7 +110,7 @@ int cholesky_inplace(Array &in, const bool is_upper) if(is_upper) uplo = CUBLAS_FILL_MODE_UPPER; - CUSOLVER_CHECK(potrf_buf_func()(getDnHandle(), + CUSOLVER_CHECK(potrf_buf_func()(solverDnHandle(), uplo, N, in.get(), in.strides()[1], @@ -121,7 +119,7 @@ int cholesky_inplace(Array &in, const bool is_upper) T *workspace = memAlloc(lwork); int *d_info = memAlloc(1); - CUSOLVER_CHECK(potrf_func()(getDnHandle(), + CUSOLVER_CHECK(potrf_func()(solverDnHandle(), uplo, N, in.get(), in.strides()[1], diff --git a/src/backend/cuda/copy.cu b/src/backend/cuda/copy.cu index 7164d63635..6a92051490 100644 --- a/src/backend/cuda/copy.cu +++ b/src/backend/cuda/copy.cu @@ -54,7 +54,7 @@ namespace cuda CUDA_CHECK(cudaMemcpyAsync(out.get(), A.get(), A.elements() * sizeof(T), cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); } else { // FIXME: Seems to fail when using Param kernel::memcopy(out.get(), out.strides().get(), A.get(), A.dims().get(), @@ -97,7 +97,7 @@ namespace cuda CUDA_CHECK(cudaMemcpyAsync(out.get(), in.get(), in.elements() * sizeof(T), cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); } else { kernel::copy(out, in, in.ndims(), scalar(0), 1); } diff --git a/src/backend/cuda/cublas.cpp b/src/backend/cuda/cublas.cpp new file mode 100644 index 0000000000..26d6211c39 --- /dev/null +++ b/src/backend/cuda/cublas.cpp @@ -0,0 +1,37 @@ +/******************************************************* + * Copyright (c) 2014, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#include +#include +#include + +namespace cuda +{ +const char *errorString(cublasStatus_t err) +{ + switch(err) + { + case CUBLAS_STATUS_SUCCESS : return "CUBLAS_STATUS_SUCCESS" ; + case CUBLAS_STATUS_NOT_INITIALIZED : return "CUBLAS_STATUS_NOT_INITIALIZED" ; + case CUBLAS_STATUS_ALLOC_FAILED : return "CUBLAS_STATUS_ALLOC_FAILED" ; + case CUBLAS_STATUS_INVALID_VALUE : return "CUBLAS_STATUS_INVALID_VALUE" ; + case CUBLAS_STATUS_ARCH_MISMATCH : return "CUBLAS_STATUS_ARCH_MISMATCH" ; + case CUBLAS_STATUS_MAPPING_ERROR : return "CUBLAS_STATUS_MAPPING_ERROR" ; + case CUBLAS_STATUS_EXECUTION_FAILED: return "CUBLAS_STATUS_EXECUTION_FAILED"; + case CUBLAS_STATUS_INTERNAL_ERROR : return "CUBLAS_STATUS_INTERNAL_ERROR" ; + case CUBLAS_STATUS_NOT_SUPPORTED : return "CUBLAS_STATUS_NOT_SUPPORTED" ; + default: return "UNKNOWN"; + } +} + +void cublasHandle::createHandle(BlasHandle* handle) +{ + CUBLAS_CHECK(cublasCreate(handle)); +} +} diff --git a/src/backend/cuda/cublasManager.hpp b/src/backend/cuda/cublas.hpp similarity index 70% rename from src/backend/cuda/cublasManager.hpp rename to src/backend/cuda/cublas.hpp index 52a2793342..994cb92023 100644 --- a/src/backend/cuda/cublasManager.hpp +++ b/src/backend/cuda/cublas.hpp @@ -8,18 +8,15 @@ ********************************************************/ #pragma once -#include -#include -#include #include +#include +#include +namespace cuda +{ +typedef cublasHandle_t BlasHandle; -namespace cublas { - - const char * errorString(cublasStatus_t err); - cublasHandle_t getHandle(); -} - +const char * errorString(cublasStatus_t err); #define CUBLAS_CHECK(fn) do { \ cublasStatus_t _error = fn; \ @@ -29,10 +26,19 @@ namespace cublas { sizeof(_err_msg), \ "CUBLAS Error (%d): %s\n", \ (int)(_error), \ - cublas::errorString( \ - _error)); \ + errorString(_error)); \ \ AF_ERROR(_err_msg, \ AF_ERR_INTERNAL); \ } \ } while(0) + +class cublasHandle : public common::MatrixAlgebraHandle +{ + public: + void createHandle(BlasHandle* handle); + void destroyHandle(BlasHandle handle) { + cublasDestroy(handle); + } +}; +} diff --git a/src/backend/cuda/cublasManager.cpp b/src/backend/cuda/cublasManager.cpp deleted file mode 100644 index f525e42a30..0000000000 --- a/src/backend/cuda/cublasManager.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#include -#include -#include -#include -#include -#include - -namespace cublas { - - const char *errorString(cublasStatus_t err) - { - - switch(err) - { - case CUBLAS_STATUS_SUCCESS: return "CUBLAS_STATUS_SUCCESS"; - case CUBLAS_STATUS_NOT_INITIALIZED: return "CUBLAS_STATUS_NOT_INITIALIZED"; - case CUBLAS_STATUS_ALLOC_FAILED: return "CUBLAS_STATUS_ALLOC_FAILED"; - case CUBLAS_STATUS_INVALID_VALUE: return "CUBLAS_STATUS_INVALID_VALUE"; - case CUBLAS_STATUS_ARCH_MISMATCH: return "CUBLAS_STATUS_ARCH_MISMATCH"; - case CUBLAS_STATUS_MAPPING_ERROR: return "CUBLAS_STATUS_MAPPING_ERROR"; - case CUBLAS_STATUS_EXECUTION_FAILED: return "CUBLAS_STATUS_EXECUTION_FAILED"; - case CUBLAS_STATUS_INTERNAL_ERROR: return "CUBLAS_STATUS_INTERNAL_ERROR"; -#if CUDA_VERSION > 5050 - case CUBLAS_STATUS_NOT_SUPPORTED: return "CUBLAS_STATUS_NOT_SUPPORTED"; -#endif - default: return "UNKNOWN"; - } - } - - //RAII class around the cublas Handle - class cublasHandle - { - cublasHandle_t handle; - public: - - cublasHandle() : handle(0) - { - CUBLAS_CHECK(cublasCreate(&handle)); - CUBLAS_CHECK(cublasSetStream(handle, cuda::getStream(cuda::getActiveDeviceId()))); - } - - ~cublasHandle() - { - cublasDestroy(handle); - } - - cublasHandle_t get() const - { - return handle; - } - }; - - cublasHandle_t getHandle() - { - static std::unique_ptr handle[cuda::DeviceManager::MAX_DEVICES]; - - int id = cuda::getActiveDeviceId(); - - if(!handle[id]) { - handle[id].reset(new cublasHandle()); - } - - return handle[id]->get(); - } -} diff --git a/src/backend/cuda/cufft.cpp b/src/backend/cuda/cufft.cpp new file mode 100644 index 0000000000..727c693f2a --- /dev/null +++ b/src/backend/cuda/cufft.cpp @@ -0,0 +1,144 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#include +#include +#include + +namespace cuda +{ +const char * _cufftGetResultString(cufftResult res) +{ + switch (res) + { + case CUFFT_SUCCESS: + return "cuFFT: success"; + + case CUFFT_INVALID_PLAN: + return "cuFFT: invalid plan handle passed"; + + case CUFFT_ALLOC_FAILED: + return "cuFFT: resources allocation failed"; + + case CUFFT_INVALID_TYPE: + return "cuFFT: invalid type (deprecated)"; + + case CUFFT_INVALID_VALUE: + return "cuFFT: invalid parameters passed to cuFFT API"; + + case CUFFT_INTERNAL_ERROR: + return "cuFFT: internal error detected using cuFFT"; + + case CUFFT_EXEC_FAILED: + return "cuFFT: FFT execution failed"; + + case CUFFT_SETUP_FAILED: + return "cuFFT: library initialization failed"; + + case CUFFT_INVALID_SIZE: + return "cuFFT: invalid size parameters passed"; + + case CUFFT_UNALIGNED_DATA: + return "cuFFT: unaligned data (deprecated)"; + + case CUFFT_INCOMPLETE_PARAMETER_LIST: + return "cuFFT: call is missing parameters"; + + case CUFFT_INVALID_DEVICE: + return "cuFFT: plan execution different than plan creation"; + + case CUFFT_PARSE_ERROR: + return "cuFFT: plan parse error"; + + case CUFFT_NO_WORKSPACE: + return "cuFFT: no workspace provided"; + + case CUFFT_NOT_IMPLEMENTED: + return "cuFFT: not implemented"; + + case CUFFT_LICENSE_ERROR: + return "cuFFT: license error"; + +#if CUDA_VERSION >= 8000 + case CUFFT_NOT_SUPPORTED: + return "cuFFT: not supported"; +#endif + } + + return "cuFFT: unknown error"; +} + +SharedPlan findPlan(int rank, int *n, + int *inembed, int istride, int idist, + int *onembed, int ostride, int odist, + cufftType type, int batch) +{ + // create the key string + char key_str_temp[64]; + sprintf(key_str_temp, "%d:", rank); + + std::string key_string(key_str_temp); + + for(int r=0; r +#include +#include +#include + +namespace cuda +{ +typedef cufftHandle PlanType; +typedef std::shared_ptr SharedPlan; + +const char * _cufftGetResultString(cufftResult res); + +SharedPlan findPlan(int rank, int *n, + int *inembed, int istride, int idist, + int *onembed, int ostride, int odist, + cufftType type, int batch); + +class PlanCache : public common::FFTPlanCache +{ + friend SharedPlan findPlan(int rank, int *n, + int *inembed, int istride, int idist, + int *onembed, int ostride, int odist, + cufftType type, int batch); +}; +} + +#define CUFFT_CHECK(fn) do { \ + cufftResult _cufft_res = fn; \ + if (_cufft_res != CUFFT_SUCCESS) { \ + char cufft_res_msg[1024]; \ + snprintf(cufft_res_msg, \ + sizeof(cufft_res_msg), \ + "cuFFT Error (%d): %s\n", \ + (int)(_cufft_res), \ + cuda::_cufftGetResultString( \ + _cufft_res)); \ + \ + AF_ERROR(cufft_res_msg, \ + AF_ERR_INTERNAL); \ + } \ + } while(0) diff --git a/src/backend/cuda/cusolverDn.cpp b/src/backend/cuda/cusolverDn.cpp new file mode 100644 index 0000000000..e02f50cae2 --- /dev/null +++ b/src/backend/cuda/cusolverDn.cpp @@ -0,0 +1,36 @@ +/******************************************************* + * Copyright (c) 2014, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#include +#include +#include +#include +#include + +namespace cuda +{ +const char *errorString(cusolverStatus_t err) +{ + switch(err) { + case CUSOLVER_STATUS_SUCCESS : return "CUSOLVER_STATUS_SUCCESS" ; + case CUSOLVER_STATUS_NOT_INITIALIZED : return "CUSOLVER_STATUS_NOT_INITIALIZED" ; + case CUSOLVER_STATUS_ALLOC_FAILED : return "CUSOLVER_STATUS_ALLOC_FAILED" ; + case CUSOLVER_STATUS_INVALID_VALUE : return "CUSOLVER_STATUS_INVALID_VALUE" ; + case CUSOLVER_STATUS_ARCH_MISMATCH : return "CUSOLVER_STATUS_ARCH_MISMATCH" ; + case CUSOLVER_STATUS_MAPPING_ERROR : return "CUSOLVER_STATUS_MAPPING_ERROR" ; + case CUSOLVER_STATUS_EXECUTION_FAILED : return "CUSOLVER_STATUS_EXECUTION_FAILED" ; + case CUSOLVER_STATUS_INTERNAL_ERROR : return "CUSOLVER_STATUS_INTERNAL_ERROR" ; + case CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED: return "CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED"; + case CUSOLVER_STATUS_NOT_SUPPORTED : return "CUSOLVER_STATUS_NOT_SUPPORTED" ; + case CUSOLVER_STATUS_ZERO_PIVOT : return "CUSOLVER_STATUS_ZERO_PIVOT" ; + case CUSOLVER_STATUS_INVALID_LICENSE : return "CUSOLVER_STATUS_INVALID_LICENSE" ; + default: return "UNKNOWN"; + } +} +} diff --git a/src/backend/cuda/cusolverDnManager.hpp b/src/backend/cuda/cusolverDn.hpp similarity index 67% rename from src/backend/cuda/cusolverDnManager.hpp rename to src/backend/cuda/cusolverDn.hpp index 49687996aa..dd42094a0c 100644 --- a/src/backend/cuda/cusolverDnManager.hpp +++ b/src/backend/cuda/cusolverDn.hpp @@ -11,16 +11,14 @@ #include #include -#include #include -#include +#include -namespace cusolver +namespace cuda { +typedef cusolverDnHandle_t SolveHandle; - const char * errorString(cusolverStatus_t err); - cusolverDnHandle_t getDnHandle(); -} +const char * errorString(cusolverStatus_t err); #define CUSOLVER_CHECK(fn) do { \ cusolverStatus_t _error = fn; \ @@ -30,10 +28,22 @@ namespace cusolver sizeof(_err_msg), \ "CUBLAS Error (%d): %s\n", \ (int)(_error), \ - cusolver::errorString( \ - _error)); \ + errorString(_error)); \ \ AF_ERROR(_err_msg, \ AF_ERR_INTERNAL); \ } \ } while(0) + +class cusolverDnHandle : public common::MatrixAlgebraHandle +{ + public: + void createHandle(SolveHandle* handle) { + CUSOLVER_CHECK(cusolverDnCreate(handle)); + } + + void destroyHandle(SolveHandle handle) { + cusolverDnDestroy(handle); + } +}; +} diff --git a/src/backend/cuda/cusolverDnManager.cpp b/src/backend/cuda/cusolverDnManager.cpp deleted file mode 100644 index 4b8b19defd..0000000000 --- a/src/backend/cuda/cusolverDnManager.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#include -#include -#include - -#include -#include -#include -#include - -namespace cusolver { - - const char *errorString(cusolverStatus_t err) - { - switch(err) { - case CUSOLVER_STATUS_SUCCESS : return "CUSOLVER_STATUS_SUCCESS" ; - case CUSOLVER_STATUS_NOT_INITIALIZED : return "CUSOLVER_STATUS_NOT_INITIALIZED" ; - case CUSOLVER_STATUS_ALLOC_FAILED : return "CUSOLVER_STATUS_ALLOC_FAILED" ; - case CUSOLVER_STATUS_INVALID_VALUE : return "CUSOLVER_STATUS_INVALID_VALUE" ; - case CUSOLVER_STATUS_ARCH_MISMATCH : return "CUSOLVER_STATUS_ARCH_MISMATCH" ; - case CUSOLVER_STATUS_MAPPING_ERROR : return "CUSOLVER_STATUS_MAPPING_ERROR" ; - case CUSOLVER_STATUS_EXECUTION_FAILED : return "CUSOLVER_STATUS_EXECUTION_FAILED" ; - case CUSOLVER_STATUS_INTERNAL_ERROR : return "CUSOLVER_STATUS_INTERNAL_ERROR" ; - case CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED : return "CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED" ; - case CUSOLVER_STATUS_NOT_SUPPORTED : return "CUSOLVER_STATUS_NOT_SUPPORTED" ; - case CUSOLVER_STATUS_ZERO_PIVOT : return "CUSOLVER_STATUS_ZERO_PIVOT" ; - case CUSOLVER_STATUS_INVALID_LICENSE : return "CUSOLVER_STATUS_INVALID_LICENSE" ; - default : return "UNKNOWN"; - } - } - - -//RAII class around the cusolver Handle - class cusolverDnHandle - { - cusolverDnHandle_t handle; - public: - - cusolverDnHandle() - : handle(0) - { - CUSOLVER_CHECK(cusolverDnCreate(&handle)); - } - - ~cusolverDnHandle() - { - cusolverDnDestroy(handle); - } - - cusolverDnHandle_t get() const - { - return handle; - } - }; - - cusolverDnHandle_t getDnHandle() - { - static std::unique_ptr handle[cuda::DeviceManager::MAX_DEVICES]; - - int id = cuda::getActiveDeviceId(); - - if(!handle[id]) { - handle[id].reset(new cusolverDnHandle()); - } - - // FIXME - // This is not an ideal case. It's just a hack. - // The correct way to do is to use - // CUSOLVER_CHECK(cusolverDnSetStream(cuda::getStream(cuda::getActiveDeviceId()))) - // in the class constructor. - // However, this is causing a lot of the cusolver functions to fail. - // The only way to fix them is to use cudaDeviceSynchronize() and cudaStreamSynchronize() - // all over the place, but even then some calls like getrs in solve_lu - // continue to fail on any stream other than 0. - // - // cuSolver Streams patch: - // https://gist.github.com/shehzan10/414c3d04a40e7c4a03ed3c2e1b9072e7 - // - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(id))); - - return handle[id]->get(); - } - -} diff --git a/src/backend/cuda/cusparse.cpp b/src/backend/cuda/cusparse.cpp new file mode 100644 index 0000000000..1e6776fff1 --- /dev/null +++ b/src/backend/cuda/cusparse.cpp @@ -0,0 +1,38 @@ +/******************************************************* + * Copyright (c) 2014, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#include +#include +#include +#include + +namespace cuda +{ +const char *errorString(cusparseStatus_t err) +{ + switch(err) { + case CUSPARSE_STATUS_SUCCESS : return "CUSPARSE_STATUS_SUCCESS" ; + case CUSPARSE_STATUS_NOT_INITIALIZED : return "CUSPARSE_STATUS_NOT_INITIALIZED" ; + case CUSPARSE_STATUS_ALLOC_FAILED : return "CUSPARSE_STATUS_ALLOC_FAILED" ; + case CUSPARSE_STATUS_INVALID_VALUE : return "CUSPARSE_STATUS_INVALID_VALUE" ; + case CUSPARSE_STATUS_ARCH_MISMATCH : return "CUSPARSE_STATUS_ARCH_MISMATCH" ; + case CUSPARSE_STATUS_MAPPING_ERROR : return "CUSPARSE_STATUS_MAPPING_ERROR" ; + case CUSPARSE_STATUS_EXECUTION_FAILED : return "CUSPARSE_STATUS_EXECUTION_FAILED" ; + case CUSPARSE_STATUS_INTERNAL_ERROR : return "CUSPARSE_STATUS_INTERNAL_ERROR" ; + case CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED: return "CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED"; + case CUSPARSE_STATUS_ZERO_PIVOT : return "CUSPARSE_STATUS_ZERO_PIVOT" ; + default: return "UNKNOWN"; + } +} + +void cusparseHandle::createHandle(SparseHandle* handle) +{ + CUSPARSE_CHECK(cusparseCreate(handle)); +} +} diff --git a/src/backend/cuda/cusparseManager.hpp b/src/backend/cuda/cusparse.hpp similarity index 68% rename from src/backend/cuda/cusparseManager.hpp rename to src/backend/cuda/cusparse.hpp index 23fcbf12c9..bb4237fb6a 100644 --- a/src/backend/cuda/cusparseManager.hpp +++ b/src/backend/cuda/cusparse.hpp @@ -8,17 +8,15 @@ ********************************************************/ #pragma once -#include -#include -#include #include +#include +#include -namespace cusparse { - - const char * errorString(cusparseStatus_t err); - cusparseHandle_t getHandle(); -} +namespace cuda +{ +typedef cusparseHandle_t SparseHandle; +const char * errorString(cusparseStatus_t err); #define CUSPARSE_CHECK(fn) do { \ cusparseStatus_t _error = fn; \ @@ -27,8 +25,18 @@ namespace cusparse { snprintf(_err_msg, sizeof(_err_msg), \ "CUSPARSE Error (%d): %s\n", \ (int)(_error), \ - cusparse::errorString( _error)); \ + errorString( _error)); \ \ AF_ERROR(_err_msg, AF_ERR_INTERNAL); \ } \ } while(0) + +class cusparseHandle : public common::MatrixAlgebraHandle +{ + public: + void createHandle(SparseHandle* handle); + void destroyHandle(SparseHandle handle) { + cusparseDestroy(handle); + } +}; +} diff --git a/src/backend/cuda/cusparseManager.cpp b/src/backend/cuda/cusparseManager.cpp deleted file mode 100644 index 3bfe5934e6..0000000000 --- a/src/backend/cuda/cusparseManager.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#include -#include - -#include -#include -#include -#include - -namespace cusparse { - - const char *errorString(cusparseStatus_t err) - { - switch(err) { - case CUSPARSE_STATUS_SUCCESS : return "CUSPARSE_STATUS_SUCCESS" ; - case CUSPARSE_STATUS_NOT_INITIALIZED : return "CUSPARSE_STATUS_NOT_INITIALIZED" ; - case CUSPARSE_STATUS_ALLOC_FAILED : return "CUSPARSE_STATUS_ALLOC_FAILED" ; - case CUSPARSE_STATUS_INVALID_VALUE : return "CUSPARSE_STATUS_INVALID_VALUE" ; - case CUSPARSE_STATUS_ARCH_MISMATCH : return "CUSPARSE_STATUS_ARCH_MISMATCH" ; - case CUSPARSE_STATUS_MAPPING_ERROR : return "CUSPARSE_STATUS_MAPPING_ERROR" ; - case CUSPARSE_STATUS_EXECUTION_FAILED : return "CUSPARSE_STATUS_EXECUTION_FAILED" ; - case CUSPARSE_STATUS_INTERNAL_ERROR : return "CUSPARSE_STATUS_INTERNAL_ERROR" ; - case CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED : return "CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED" ; - case CUSPARSE_STATUS_ZERO_PIVOT : return "CUSPARSE_STATUS_ZERO_PIVOT" ; - default : return "UNKNOWN"; - } - } - - -//RAII class around the cusparse Handle - class cusparseHandle - { - cusparseHandle_t handle; - public: - - cusparseHandle() - : handle(0) - { - CUSPARSE_CHECK(cusparseCreate(&handle)); - CUSPARSE_CHECK(cusparseSetStream(handle, cuda::getStream(cuda::getActiveDeviceId()))); - } - - ~cusparseHandle() - { - cusparseDestroy(handle); - } - - cusparseHandle_t get() const - { - return handle; - } - }; - - cusparseHandle_t getHandle() - { - static std::unique_ptr handle[cuda::DeviceManager::MAX_DEVICES]; - - int id = cuda::getActiveDeviceId(); - - if(!handle[id]) { - handle[id].reset(new cusparseHandle()); - } - - return handle[id]->get(); - } - -} diff --git a/src/backend/cuda/debug_cuda.hpp b/src/backend/cuda/debug_cuda.hpp index f5424950dc..5cf036b503 100644 --- a/src/backend/cuda/debug_cuda.hpp +++ b/src/backend/cuda/debug_cuda.hpp @@ -12,8 +12,15 @@ #include #include #include +#include -#define THRUST_STREAM thrust::cuda::par.on(cuda::getStream(cuda::getActiveDeviceId())) +namespace cuda +{ +template +using ThrustVector = thrust::device_vector >; +} + +#define THRUST_STREAM thrust::cuda::par.on(cuda::getActiveStream()) #if THRUST_MAJOR_VERSION>=1 && THRUST_MINOR_VERSION>=8 @@ -24,20 +31,20 @@ #define THRUST_SELECT(fn, ...) \ do { \ - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); \ + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); \ fn(__VA_ARGS__); \ } while(0) #define THRUST_SELECT_OUT(res, fn, ...) \ do { \ - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); \ + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); \ res = fn(__VA_ARGS__); \ } while(0) #endif #define CUDA_LAUNCH_SMEM(fn, blks, thrds, smem_size, ...) \ - fn<<>>(__VA_ARGS__) + fn<<>>(__VA_ARGS__) #define CUDA_LAUNCH(fn, blks, thrds, ...) \ CUDA_LAUNCH_SMEM(fn, blks, thrds, 0, __VA_ARGS__) @@ -46,14 +53,14 @@ #ifndef NDEBUG #define POST_LAUNCH_CHECK() do { \ - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); \ + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); \ } while(0) \ #else #define POST_LAUNCH_CHECK() do { \ if(cuda::synchronize_calls()) { \ - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); \ + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); \ } else { \ CUDA_CHECK(cudaPeekAtLastError()); \ } \ diff --git a/src/backend/cuda/err_cufft.hpp b/src/backend/cuda/err_cufft.hpp deleted file mode 100644 index 99fcd97cee..0000000000 --- a/src/backend/cuda/err_cufft.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#pragma once -#include -#include -#include // Need this for CUDA_VERSION -#include - -static const char * _cufftGetResultString(cufftResult res) -{ - switch (res) - { - case CUFFT_SUCCESS: - return "cuFFT: success"; - - case CUFFT_INVALID_PLAN: - return "cuFFT: invalid plan handle passed"; - - case CUFFT_ALLOC_FAILED: - return "cuFFT: resources allocation failed"; - - case CUFFT_INVALID_TYPE: - return "cuFFT: invalid type (deprecated)"; - - case CUFFT_INVALID_VALUE: - return "cuFFT: invalid parameters passed to cuFFT API"; - - case CUFFT_INTERNAL_ERROR: - return "cuFFT: internal error detected using cuFFT"; - - case CUFFT_EXEC_FAILED: - return "cuFFT: FFT execution failed"; - - case CUFFT_SETUP_FAILED: - return "cuFFT: library initialization failed"; - - case CUFFT_INVALID_SIZE: - return "cuFFT: invalid size parameters passed"; - - case CUFFT_UNALIGNED_DATA: - return "cuFFT: unaligned data (deprecated)"; - - case CUFFT_INCOMPLETE_PARAMETER_LIST: - return "cuFFT: call is missing parameters"; - - case CUFFT_INVALID_DEVICE: - return "cuFFT: plan execution different than plan creation"; - - case CUFFT_PARSE_ERROR: - return "cuFFT: plan parse error"; - - case CUFFT_NO_WORKSPACE: - return "cuFFT: no workspace provided"; - -#if CUDA_VERSION >= 6050 - case CUFFT_NOT_IMPLEMENTED: - return "cuFFT: not implemented"; - - case CUFFT_LICENSE_ERROR: - return "cuFFT: license error"; -#endif -#if CUDA_VERSION >= 8000 - case CUFFT_NOT_SUPPORTED: - return "cuFFT: not supported"; -#endif - } - - return "cuFFT: unknown error"; -} - -#define CUFFT_CHECK(fn) do { \ - cufftResult _cufft_res = fn; \ - if (_cufft_res != CUFFT_SUCCESS) { \ - char cufft_res_msg[1024]; \ - snprintf(cufft_res_msg, \ - sizeof(cufft_res_msg), \ - "cuFFT Error (%d): %s\n", \ - (int)(_cufft_res), \ - _cufftGetResultString( \ - _cufft_res)); \ - \ - AF_ERROR(cufft_res_msg, \ - AF_ERR_INTERNAL); \ - } \ - } while(0) diff --git a/src/backend/cuda/fft.cpp b/src/backend/cuda/fft.cpp index 30e61084ce..280704012a 100644 --- a/src/backend/cuda/fft.cpp +++ b/src/backend/cuda/fft.cpp @@ -12,13 +12,8 @@ #include #include #include -#include -#include +#include #include -#include -#include -#include -#include #include using af::dim4; @@ -26,154 +21,9 @@ using std::string; namespace cuda { - -typedef std::pair FFTPlanPair; -typedef std::deque FFTPlanCache; - -// cuFFTPlanner caches fft plans -// -// new plan |--> IF number of plans cached is at limit, pop the least used entry and push new plan. -// | -// |--> ELSE just push the plan -// existing plan -> reuse a plan -class cuFFTPlanner -{ - friend void find_cufft_plan(cufftHandle &plan, int rank, int *n, - int *inembed, int istride, int idist, - int *onembed, int ostride, int odist, - cufftType type, int batch); - - public: - static cuFFTPlanner& getInstance() { - static cuFFTPlanner instances[cuda::DeviceManager::MAX_DEVICES]; - return instances[cuda::getActiveDeviceId()]; - } - - inline void setMaxCacheSize(size_t size) { - mCache.resize(size, FFTPlanPair(std::string(""), 0)); - } - - inline size_t getMaxCacheSize() const { - return mMaxCacheSize; - } - - inline cufftHandle getPlan(int index) const { - return mCache[index].second; - } - - // iterates through plan cache from front to back - // of the cache(queue) - int findIfPlanExists(std::string keyString) const { - int retVal = -1; - for(uint i=0; imMaxCacheSize) { - popPlan(); - } - mCache.push_front(FFTPlanPair(keyString, plan)); - } - - private: - cuFFTPlanner() : mMaxCacheSize(5) {} - cuFFTPlanner(cuFFTPlanner const&); - void operator=(cuFFTPlanner const&); - - size_t mMaxCacheSize; - FFTPlanCache mCache; -}; - -void find_cufft_plan(cufftHandle &plan, int rank, int *n, - int *inembed, int istride, int idist, - int *onembed, int ostride, int odist, - cufftType type, int batch) -{ - // create the key string - char key_str_temp[64]; - sprintf(key_str_temp, "%d:", rank); - - string key_string(key_str_temp); - - for(int r=0; r @@ -238,14 +88,13 @@ void fft_inplace(Array &in) batch *= idims[i]; } - cufftHandle plan; - find_cufft_plan(plan, rank, t_dims, - in_embed , istrides[0], istrides[rank], - in_embed , istrides[0], istrides[rank], - (cufftType)cufft_transform::type, batch); + SharedPlan plan = findPlan(rank, t_dims, + in_embed , istrides[0], istrides[rank], + in_embed , istrides[0], istrides[rank], + (cufftType)cufft_transform::type, batch); cufft_transform transform; - CUFFT_CHECK(transform(plan, (T *)in.get(), in.get(), direction ? CUFFT_FORWARD : CUFFT_INVERSE)); + CUFFT_CHECK(transform(*plan.get(), (T *)in.get(), in.get(), direction ? CUFFT_FORWARD : CUFFT_INVERSE)); } template @@ -273,14 +122,13 @@ Array fft_r2c(const Array &in) dim4 istrides = in.strides(); dim4 ostrides = out.strides(); - cufftHandle plan; - find_cufft_plan(plan, rank, t_dims, - in_embed , istrides[0], istrides[rank], - out_embed , ostrides[0], ostrides[rank], - (cufftType)cufft_real_transform::type, batch); + SharedPlan plan = findPlan(rank, t_dims, + in_embed , istrides[0], istrides[rank], + out_embed , ostrides[0], ostrides[rank], + (cufftType)cufft_real_transform::type, batch); cufft_real_transform transform; - CUFFT_CHECK(transform(plan, (Tr *)in.get(), out.get())); + CUFFT_CHECK(transform(*plan.get(), (Tr *)in.get(), out.get())); return out; } @@ -306,13 +154,12 @@ Array fft_c2r(const Array &in, const dim4 &odims) cufft_real_transform transform; - cufftHandle plan; - find_cufft_plan(plan, rank, t_dims, - in_embed , istrides[0], istrides[rank], - out_embed , ostrides[0], ostrides[rank], - (cufftType)cufft_real_transform::type, batch); + SharedPlan plan = findPlan(rank, t_dims, + in_embed , istrides[0], istrides[rank], + out_embed , ostrides[0], ostrides[rank], + (cufftType)cufft_real_transform::type, batch); - CUFFT_CHECK(transform(plan, (Tc *)in.get(), out.get())); + CUFFT_CHECK(transform(*plan.get(), (Tc *)in.get(), out.get())); return out; } diff --git a/src/backend/cuda/hist_graphics.cpp b/src/backend/cuda/hist_graphics.cpp index 4e9017da88..d43355a123 100644 --- a/src/backend/cuda/hist_graphics.cpp +++ b/src/backend/cuda/hist_graphics.cpp @@ -9,11 +9,11 @@ #if defined (WITH_GRAPHICS) -#include #include #include #include #include +#include namespace cuda { @@ -22,20 +22,18 @@ using namespace gl; template void copy_histogram(const Array &data, const forge::Histogram* hist) { - if(InteropManager::checkGraphicsInteropCapability()) { + if(DeviceManager::checkGraphicsInteropCapability()) { const T *d_P = data.get(); - InteropManager& intrpMngr = InteropManager::getInstance(); + ShrdResVector res = interopManager().getBufferResource(hist); - cudaGraphicsResource_t *resources = intrpMngr.getBufferResource(hist); // Map resource. Copy data to VBO. Unmap resource. size_t num_bytes = hist->verticesSize(); T* d_vbo = NULL; - cudaGraphicsMapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, resources[0]); - cudaMemcpyAsync(d_vbo, d_P, num_bytes, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsUnmapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); + cudaGraphicsMapResources(1, res[0].get(), cuda::getActiveStream()); + cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, *(res[0].get())); + cudaMemcpyAsync(d_vbo, d_P, num_bytes, cudaMemcpyDeviceToDevice, cuda::getActiveStream()); + cudaGraphicsUnmapResources(1, res[0].get(), cuda::getActiveStream()); CheckGL("After cuda resource copy"); diff --git a/src/backend/cuda/image.cpp b/src/backend/cuda/image.cpp index 2303184daf..ef177dc28f 100644 --- a/src/backend/cuda/image.cpp +++ b/src/backend/cuda/image.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include using af::dim4; @@ -27,20 +27,17 @@ using namespace gl; template void copy_image(const Array &in, const forge::Image* image) { - if(InteropManager::checkGraphicsInteropCapability()) { - InteropManager& intrpMngr = InteropManager::getInstance(); - - cudaGraphicsResource_t *resources = intrpMngr.getBufferResource(image); + if(DeviceManager::checkGraphicsInteropCapability()) { + ShrdResVector res = interopManager().getBufferResource(image); const T *d_X = in.get(); // Map resource. Copy data to pixels. Unmap resource. size_t num_bytes; T* d_pixels = NULL; - cudaGraphicsMapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsResourceGetMappedPointer((void **)&d_pixels, &num_bytes, resources[0]); - cudaMemcpyAsync(d_pixels, d_X, num_bytes, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsUnmapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); + cudaGraphicsMapResources(1, res[0].get(), cuda::getActiveStream()); + cudaGraphicsResourceGetMappedPointer((void **)&d_pixels, &num_bytes, *(res[0].get())); + cudaMemcpyAsync(d_pixels, d_X, num_bytes, cudaMemcpyDeviceToDevice, cuda::getActiveStream()); + cudaGraphicsUnmapResources(1, res[0].get(), cuda::getActiveStream()); POST_LAUNCH_CHECK(); CheckGL("After cuda resource copy"); diff --git a/src/backend/cuda/interopManager.cpp b/src/backend/cuda/interopManager.cpp deleted file mode 100644 index 077e4ac703..0000000000 --- a/src/backend/cuda/interopManager.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -// Parts of this code sourced from SnopyDogy -// https://gist.github.com/SnopyDogy/a9a22497a893ec86aa3e - -#if defined(WITH_GRAPHICS) - -#include -#include -#include -#include - -namespace cuda -{ - -void InteropManager::destroyResources() -{ - typedef std::vector::iterator CGRIter_t; - - int n = getActiveDeviceId(); - for(iter_t iter = interop_maps[n].begin(); iter != interop_maps[n].end(); iter++) { - for(CGRIter_t ct = (iter->second).begin(); ct != (iter->second).end(); ct++) { - CUDA_CHECK(cudaGraphicsUnregisterResource(*ct)); - } - (iter->second).clear(); - } -} - -InteropManager::~InteropManager() -{ - try { - for(int i = 0; i < getDeviceCount(); i++) { - setDevice(i); - destroyResources(); - } - } catch (AfError &ex) { - - std::string perr = getEnvVar("AF_PRINT_ERRORS"); - if(!perr.empty()) { - if(perr != "0") - fprintf(stderr, "%s\n", ex.what()); - } - } -} - -InteropManager& InteropManager::getInstance() -{ - static InteropManager my_instance; - return my_instance; -} - -interop_t& InteropManager::getDeviceMap(int device) -{ - return (device == -1) ? interop_maps[getActiveDeviceId()] : interop_maps[device]; -} - -CGR_t* InteropManager::getBufferResource(const forge::Image* key) -{ - void* key_value = (void*)key; - interop_t& i_map = getDeviceMap(); - - if(i_map.find(key_value) == i_map.end()) { - CGR_t pixelsResource; - // Register pixels with CUDA - CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&pixelsResource, key->pixels(), cudaGraphicsMapFlagsWriteDiscard)); - // TODO: - // A way to store multiple buffers and take PBO/CBO etc as - // argument and return the appropriate buffer - std::vector vec(1); - vec[0] = pixelsResource; - i_map[key_value] = vec; - } - - return &i_map[key_value].front(); -} - -CGR_t* InteropManager::getBufferResource(const forge::Plot* key) -{ - void* key_value = (void*)key; - interop_t& i_map = getDeviceMap(); - - iter_t iter = i_map.find(key_value); - - if(iter == i_map.end()) { - CGR_t vboResource; - // Register VBO with CUDA - CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&vboResource, key->vertices(), cudaGraphicsMapFlagsWriteDiscard)); - // TODO: - // A way to store multiple buffers and take PBO/CBO etc as - // argument and return the appropriate buffer - std::vector vec(1); - vec[0] = vboResource; - i_map[key_value] = vec; - } - - return &i_map[key_value].front(); -} - -CGR_t* InteropManager::getBufferResource(const forge::Histogram* key) -{ - void* key_value = (void*)key; - interop_t& i_map = getDeviceMap(); - - iter_t iter = i_map.find(key_value); - - if(iter == i_map.end()) { - CGR_t vboResource; - // Register VBO with CUDA - CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&vboResource, key->vertices(), cudaGraphicsMapFlagsWriteDiscard)); - // TODO: - // A way to store multiple buffers and take PBO/CBO etc as - // argument and return the appropriate buffer - std::vector vec(1); - vec[0] = vboResource; - i_map[key_value] = vec; - } - - return &i_map[key_value].front(); -} - -CGR_t* InteropManager::getBufferResource(const forge::Surface* key) -{ - void* key_value = (void*)key; - interop_t& i_map = getDeviceMap(); - - iter_t iter = i_map.find(key_value); - - if(iter == i_map.end()) { - CGR_t vboResource; - // Register VBO with CUDA - CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&vboResource, key->vertices(), cudaGraphicsMapFlagsWriteDiscard)); - // TODO: - // A way to store multiple buffers and take PBO/CBO etc as - // argument and return the appropriate buffer - std::vector vec(1); - vec[0] = vboResource; - i_map[key_value] = vec; - } - - return &i_map[key_value].front(); -} - -CGR_t* InteropManager::getBufferResource(const forge::VectorField* key) -{ - void* key_value = (void*)key; - interop_t& i_map = getDeviceMap(); - - iter_t iter = i_map.find(key_value); - - if(iter == i_map.end()) { - CGR_t pResource; - CGR_t dResource; - // Register VBO with CUDA - CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&pResource, key->vertices(), cudaGraphicsMapFlagsWriteDiscard)); - CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&dResource, key->directions(), cudaGraphicsMapFlagsWriteDiscard)); - // TODO: - // A way to store multiple buffers and take PBO/CBO etc as - // argument and return the appropriate buffer - std::vector vec(2); - vec[0] = pResource; - vec[1] = dResource; - i_map[key_value] = vec; - } - - return &i_map[key_value].front(); -} - -bool InteropManager::checkGraphicsInteropCapability() -{ - static bool run_once = true; - static bool capable = true; - - if(run_once) { - unsigned int pCudaEnabledDeviceCount = 0; - int pCudaGraphicsEnabledDeviceIds = 0; - cudaGetLastError(); // Reset Errors - cudaError_t err = cudaGLGetDevices(&pCudaEnabledDeviceCount, &pCudaGraphicsEnabledDeviceIds, getDeviceCount(), cudaGLDeviceListAll); - if(err == 63) { // OS Support Failure - Happens when devices are only Tesla - capable = false; - printf("Warning: No CUDA Device capable of CUDA-OpenGL. CUDA-OpenGL Interop will use CPU fallback.\n"); - printf("Corresponding CUDA Error (%d): %s.\n", err, cudaGetErrorString(err)); - printf("This may happen if all CUDA Devices are in TCC Mode and/or not connected to a display.\n"); - } - cudaGetLastError(); // Reset Errors - run_once = false; - } - return capable; -} - -} - -#endif diff --git a/src/backend/cuda/interopManager.hpp b/src/backend/cuda/interopManager.hpp deleted file mode 100644 index deb4f126e9..0000000000 --- a/src/backend/cuda/interopManager.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -// Parts of this code sourced from SnopyDogy -// https://gist.github.com/SnopyDogy/a9a22497a893ec86aa3e - -#if defined(WITH_GRAPHICS) - -#if defined(OS_WIN) -#include -#endif - -// cuda_gl_interop.h does not include OpenGL headers for ARM -#include -#include - -#if defined(__arm__) || defined(__aarch64__) -using namespace gl; -#define GL_VERSION gl::GL_VERSION -#endif - -#include -#include - -#include -#include -#include - -#include -#include - -using af::dim4; - -namespace cuda -{ - -typedef std::map > interop_t; -typedef interop_t::iterator iter_t; -typedef cudaGraphicsResource_t CGR_t; - - -// Manager Class for cudaPBOResource: calls garbage collection at the end of the program -class InteropManager -{ - private: - interop_t interop_maps[DeviceManager::MAX_DEVICES]; - - public: - static InteropManager& getInstance(); - static bool checkGraphicsInteropCapability(); - - ~InteropManager(); - CGR_t* getBufferResource(const forge::Image *handle); - CGR_t* getBufferResource(const forge::Plot *handle); - CGR_t* getBufferResource(const forge::Histogram *handle); - CGR_t* getBufferResource(const forge::Surface *handle); - CGR_t* getBufferResource(const forge::VectorField *handle); - - protected: - InteropManager() {} - InteropManager(InteropManager const&); - void operator=(InteropManager const&); - interop_t& getDeviceMap(int device = -1); // default will return current device - void destroyResources(); -}; - -} - -#endif diff --git a/src/backend/cuda/jit.cpp b/src/backend/cuda/jit.cpp index 44fa9e1616..097bc120cf 100644 --- a/src/backend/cuda/jit.cpp +++ b/src/backend/cuda/jit.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include namespace cuda @@ -54,6 +55,8 @@ namespace cuda using JIT::Node; using JIT::str_map_iter; using JIT::str_map_t; +using JIT::Node_ids; +using JIT::Node_map_t; using std::hash; using std::map; using std::string; @@ -67,7 +70,10 @@ const char *layout32 = "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16: const char *triple64 = "target triple = \"nvptx64-unknown-cuda\"\n\n"; const char *triple32 = "target triple = \"nvptx-unknown-cuda\"\n\n"; -static string getFuncName(vector nodes, bool is_linear) +static string getFuncName(const vector &output_nodes, + const vector &full_nodes, + const vector &full_ids, + bool is_linear) { stringstream funcName; stringstream hashName; @@ -75,14 +81,12 @@ static string getFuncName(vector nodes, bool is_linear) if (is_linear) funcName << "L_"; //Kernel Linear else funcName << "G_"; //Kernel General - int id = 0; + for (const auto &node : output_nodes) { + funcName << node->getNameStr() << "_"; + } - for (int i = 0; i < (int)nodes.size(); i++) { - funcName << "["; - id = nodes[i]->setId(id); - funcName << nodes[i]->getNameStr(); - nodes[i]->genKerName(funcName); - funcName << "]"; + for (int i = 0; i < (int)full_nodes.size(); i++) { + full_nodes[i]->genKerName(funcName, full_ids[i]); } hash hash_fn; @@ -92,7 +96,11 @@ static string getFuncName(vector nodes, bool is_linear) return hashName.str(); } -static string getKernelString(string funcName, vector nodes, bool is_linear) +static string getKernelString(const string funcName, + const vector &full_nodes, + const vector &full_ids, + const vector &output_ids, + bool is_linear) { static const char *defineVoid = "define void "; static const char *generalDimParams = "\n" @@ -218,15 +226,30 @@ static string getKernelString(string funcName, vector nodes, bool is_lin stringstream outWriteStream; str_map_t declStrs; - for (int i = 0; i < (int)nodes.size(); i++) { - string outTypeStr = nodes[i]->getTypeStr(); - int id = nodes[i]->getId(); + vector types_output(output_ids.size()); + for (int i = 0; i < (int)output_ids.size(); i++) { + types_output[i] = full_nodes[output_ids[i]]->getTypeStr(); + } + + for (int i = 0; i < (int)full_nodes.size(); i++) { + const auto &node = full_nodes[i]; + const auto &ids_curr = full_ids[i]; + // Generate input parameters, needs only current id + node->genParams(inParamStream, inAnnStream, ids_curr.id, is_linear); + // Generate input offsets, needs only current id + node->genOffsets(offsetsStream, ids_curr.id, is_linear); + // Generate the core function body, needs children id as well + node->genFuncs(funcBodyStream, declStrs, ids_curr, is_linear); + } - nodes[i]->genParams(inParamStream, inAnnStream, is_linear); + for (int i = 0; i < (int)output_ids.size(); i++) { + int id = output_ids[i]; + string outTypeStr = types_output[i]; + + // Generate output parameters outParamStream << outTypeStr << "* %out" << id << ",\n"; - nodes[i]->genOffsets(offsetsStream, is_linear); - nodes[i]->genFuncs(funcBodyStream, declStrs, is_linear); + // Generate instruction to write output outWriteStream << "%outIdx" << id << "= getelementptr inbounds " << outTypeStr @@ -237,6 +260,8 @@ static string getKernelString(string funcName, vector nodes, bool is_lin << " %val" << id << ", " << outTypeStr << "* %outIdx" << id << "\n"; + + // Generate output annotation string outAnnStream << outTypeStr << "*,\n"; } @@ -267,7 +292,8 @@ static string getKernelString(string funcName, vector nodes, bool is_lin << outWriteStream.str() << blockEnd; - for(str_map_iter iterator = declStrs.begin(); iterator != declStrs.end(); iterator++) { + for(str_map_iter iterator = declStrs.begin(); + iterator != declStrs.end(); iterator++) { kerStream << iterator->first << "\n"; } kerStream << functionLoad; @@ -528,20 +554,24 @@ static kc_entry_t compileKernel(const char *ker_name, string jit_ker) return entry; } -static CUfunction getKernel(vector nodes, bool is_linear) +static CUfunction getKernel(const vector &output_nodes, + const vector &output_ids, + const vector &full_nodes, + const vector &full_ids, + const bool is_linear) { + typedef std::map kc_t; - string funcName = getFuncName(nodes, is_linear); + thread_local kc_t kernelCaches[DeviceManager::MAX_DEVICES]; - typedef map kc_t; - static kc_t kernelCaches[DeviceManager::MAX_DEVICES]; - int device = getActiveDeviceId(); + string funcName = getFuncName(output_nodes, full_nodes, full_ids, is_linear); + int device = getActiveDeviceId(); kc_t::iterator idx = kernelCaches[device].find(funcName); kc_entry_t entry = {NULL, NULL}; if (idx == kernelCaches[device].end()) { - string jit_ker = getKernelString(funcName, nodes, is_linear); + string jit_ker = getKernelString(funcName, full_nodes, full_ids, output_ids, is_linear); entry = compileKernel(funcName.c_str(), jit_ker); kernelCaches[device][funcName] = entry; } else { @@ -552,19 +582,31 @@ static CUfunction getKernel(vector nodes, bool is_linear) } template -void evalNodes(vector >&outputs, vector nodes) +void evalNodes(vector >&outputs, vector output_nodes) { int num_outputs = (int)outputs.size(); if (num_outputs == 0) return; - bool is_linear = true; + Node_map_t nodes; + vector output_ids; + for (auto &node : output_nodes) { + node->getNodesMap(nodes); + output_ids.push_back(nodes[node].id); + } - for (int i = 0; i < num_outputs; i++) { - is_linear &= nodes[i]->isLinear(outputs[0].dims); + vector full_nodes(nodes.size()); + vector full_ids(nodes.size()); + bool is_linear = true; + for (auto &map_entry : nodes) { + full_nodes[map_entry.second.id] = map_entry.first; + full_ids[map_entry.second.id] = map_entry.second; + is_linear &= map_entry.first->isLinear(outputs[0].dims); } - CUfunction ker = getKernel(nodes, is_linear); + CUfunction ker = getKernel(output_nodes, output_ids, + full_nodes, full_ids, + is_linear); int threads_x = 1, threads_y = 1; int blocks_x_ = 1, blocks_y_ = 1; @@ -607,8 +649,8 @@ void evalNodes(vector >&outputs, vector nodes) vector args; - for (int i = 0; i < num_outputs; i++) { - nodes[i]->setArgs(args, is_linear); + for (const auto &node : full_nodes) { + node->setArgs(args, is_linear); } for (int i = 0; i < num_outputs; i++) { @@ -655,24 +697,20 @@ void evalNodes(vector >&outputs, vector nodes) threads_y, 1, 0, - getStream(getActiveDeviceId()), + getActiveStream(), &args.front(), NULL)); - - for (int i = 0; i < num_outputs; i++) { - nodes[i]->resetFlags(); - } } template void evalNodes(Param &out, Node *node) { vector> outputs; - vector nodes; + vector output_nodes; outputs.push_back(out); - nodes.push_back(node); - evalNodes(outputs, nodes); + output_nodes.push_back(node); + evalNodes(outputs, output_nodes); return; } diff --git a/src/backend/cuda/kernel/convolve.cu b/src/backend/cuda/kernel/convolve.cu index 0a7f5425cf..d237fd8ca1 100644 --- a/src/backend/cuda/kernel/convolve.cu +++ b/src/backend/cuda/kernel/convolve.cu @@ -372,7 +372,7 @@ void convolve_1d(conv_kparam_t &p, Param out, CParam sig, CParam filt) filt.ptr+(f1Off+f2Off+f3Off), filterLen*sizeof(aT), 0, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); p.o[0] = (p.outHasNoOffset ? 0 : b1); p.o[1] = (p.outHasNoOffset ? 0 : b2); @@ -410,7 +410,7 @@ void convolve_2d(conv_kparam_t &p, Param out, CParam sig, CParam filt) filt.ptr+(f2Off+f3Off), filterLen*sizeof(aT), 0, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); p.o[1] = (p.outHasNoOffset ? 0 : b2); p.o[2] = (p.outHasNoOffset ? 0 : b3); @@ -438,7 +438,7 @@ void convolve_3d(conv_kparam_t &p, Param out, CParam sig, CParam filt) filt.ptr+f3Off, filterLen*sizeof(aT), 0, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); p.o[2] = (p.outHasNoOffset ? 0 : b3); p.s[2] = (p.inHasNoOffset ? 0 : b3); diff --git a/src/backend/cuda/kernel/convolve_separable.cu b/src/backend/cuda/kernel/convolve_separable.cu index 1b34e64043..65d7901a2c 100644 --- a/src/backend/cuda/kernel/convolve_separable.cu +++ b/src/backend/cuda/kernel/convolve_separable.cu @@ -133,7 +133,7 @@ void convolve2(Param out, CParam signal, CParam filter) // FIX ME: if the filter array is strided, direct copy of symbols // might cause issues CUDA_CHECK(cudaMemcpyToSymbolAsync(kernel::sFilter, filter.ptr, fLen*sizeof(accType), 0, - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); switch(fLen) { case 2: conv2Helper(blocks, threads, out, signal, blk_x, blk_y); break; diff --git a/src/backend/cuda/kernel/fast.hpp b/src/backend/cuda/kernel/fast.hpp index c50a3a1a3c..491ffe5f01 100644 --- a/src/backend/cuda/kernel/fast.hpp +++ b/src/backend/cuda/kernel/fast.hpp @@ -447,7 +447,7 @@ void fast(unsigned* out_feat, blocks.y = divup(in.dims[1], 64); unsigned *d_total = (unsigned *)(d_score + in.dims[0] * in.dims[1]); - CUDA_CHECK(cudaMemsetAsync(d_total, 0, sizeof(unsigned), cuda::getStream(cuda::getActiveDeviceId()))); + CUDA_CHECK(cudaMemsetAsync(d_total, 0, sizeof(unsigned), cuda::getActiveStream())); unsigned *d_counts = memAlloc(blocks.x * blocks.y); unsigned *d_offsets = memAlloc(blocks.x * blocks.y); @@ -465,8 +465,8 @@ void fast(unsigned* out_feat, // Dimensions of output array unsigned total; CUDA_CHECK(cudaMemcpyAsync(&total, d_total, sizeof(unsigned), cudaMemcpyDeviceToHost, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); total = total < max_feat ? total : max_feat; if (total > 0) { diff --git a/src/backend/cuda/kernel/fftconvolve.hpp b/src/backend/cuda/kernel/fftconvolve.hpp index d7412452a0..399e3f389e 100644 --- a/src/backend/cuda/kernel/fftconvolve.hpp +++ b/src/backend/cuda/kernel/fftconvolve.hpp @@ -10,11 +10,9 @@ #include #include #include -#include #include #include #include -#include namespace cuda { diff --git a/src/backend/cuda/kernel/harris.hpp b/src/backend/cuda/kernel/harris.hpp index c2391c666f..e00b740f05 100644 --- a/src/backend/cuda/kernel/harris.hpp +++ b/src/backend/cuda/kernel/harris.hpp @@ -216,7 +216,7 @@ void harris(unsigned* corners_out, int filter_elem = filter.strides[3] * filter.dims[3]; filter.ptr = memAlloc(filter_elem); CUDA_CHECK(cudaMemcpyAsync(filter.ptr, h_filter, filter_elem * sizeof(convAccT), - cudaMemcpyHostToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyHostToDevice, cuda::getActiveStream())); delete[] h_filter; @@ -277,7 +277,7 @@ void harris(unsigned* corners_out, unsigned* d_corners_found = memAlloc(1); CUDA_CHECK(cudaMemsetAsync(d_corners_found, 0, sizeof(unsigned), - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); float* d_x_corners = memAlloc(corner_lim); float* d_y_corners = memAlloc(corner_lim); @@ -306,8 +306,8 @@ void harris(unsigned* corners_out, unsigned corners_found = 0; CUDA_CHECK(cudaMemcpyAsync(&corners_found, d_corners_found, sizeof(unsigned), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); memFree(d_responses); memFree(d_corners_found); @@ -362,11 +362,11 @@ void harris(unsigned* corners_out, *y_out = memAlloc(*corners_out); *resp_out = memAlloc(*corners_out); CUDA_CHECK(cudaMemcpyAsync(*x_out, d_x_corners, *corners_out * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*y_out, d_y_corners, *corners_out * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*resp_out, d_resp_corners, *corners_out * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); memFree(d_x_corners); memFree(d_y_corners); diff --git a/src/backend/cuda/kernel/homography.hpp b/src/backend/cuda/kernel/homography.hpp index 23eb043c57..051fdb875e 100644 --- a/src/backend/cuda/kernel/homography.hpp +++ b/src/backend/cuda/kernel/homography.hpp @@ -612,27 +612,27 @@ int computeH( POST_LAUNCH_CHECK(); CUDA_CHECK(cudaMemcpyAsync(&minMedian, finalMedian, sizeof(float), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(&minIdx, finalIdx, sizeof(unsigned), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); memFree(finalMedian); memFree(finalIdx); } else { CUDA_CHECK(cudaMemcpyAsync(&minMedian, median.ptr, sizeof(float), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(&minIdx, idx.ptr, sizeof(unsigned), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); } // Copy best homography to output CUDA_CHECK(cudaMemcpyAsync(bestH.ptr, H.ptr + minIdx * 9, 9*sizeof(T), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); blocks = dim3(divup(nsamples, threads.x)); // sync stream for the device to host copies to be visible for // the subsequent kernel launch - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); CUDA_LAUNCH((computeLMedSInliers), blocks, threads, inliers, bestH, x_src, y_src, x_dst, y_dst, @@ -648,7 +648,7 @@ int computeH( kernel::reduce(totalInliers, inliers, 0, false, 0.0); CUDA_CHECK(cudaMemcpyAsync(&inliersH, totalInliers.ptr, sizeof(unsigned), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); memFree(totalInliers.ptr); memFree(median.ptr); @@ -657,9 +657,9 @@ int computeH( inliersH = kernel::ireduce_all(&blockIdx, inliers); // Copies back index and number of inliers of best homography estimation CUDA_CHECK(cudaMemcpyAsync(&idxH, idx.ptr+blockIdx, sizeof(unsigned), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(bestH.ptr, H.ptr + idxH * 9, 9*sizeof(T), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); } @@ -667,7 +667,7 @@ int computeH( memFree(idx.ptr); // sync stream for the device to host copies to be visible for // the subsequent kernel launch - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); return (int)inliersH; } diff --git a/src/backend/cuda/kernel/ireduce.hpp b/src/backend/cuda/kernel/ireduce.hpp index 12d4a8956f..e87360eb76 100644 --- a/src/backend/cuda/kernel/ireduce.hpp +++ b/src/backend/cuda/kernel/ireduce.hpp @@ -491,10 +491,10 @@ namespace kernel uint* h_lptr_raw = h_lptr.get(); CUDA_CHECK(cudaMemcpyAsync(h_ptr_raw, tmp.ptr, tmp_elements * sizeof(T), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(h_lptr_raw, tlptr, tmp_elements * sizeof(uint), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); memFree(tmp.ptr); memFree(tlptr); @@ -522,8 +522,8 @@ namespace kernel unique_ptr h_ptr(new T[in_elements]); T* h_ptr_raw = h_ptr.get(); CUDA_CHECK(cudaMemcpyAsync(h_ptr_raw, in.ptr, in_elements * sizeof(T), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); MinMaxOp Op(h_ptr_raw[0], 0); for (int i = 1; i < in_elements; i++) { diff --git a/src/backend/cuda/kernel/orb.hpp b/src/backend/cuda/kernel/orb.hpp index d0cd2e7d3b..9b6c67bfd8 100644 --- a/src/backend/cuda/kernel/orb.hpp +++ b/src/backend/cuda/kernel/orb.hpp @@ -328,7 +328,7 @@ void orb(unsigned* out_feat, // In future implementations, the user will be capable of passing his // distribution instead of using the reference one //CUDA_CHECK(cudaMemcpyToSymbolAsync(d_ref_pat, h_ref_pat, 256 * 4 * sizeof(int), 0, - // cudaMemcpyHostToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + // cudaMemcpyHostToDevice, cuda::getActiveStream())); vector d_score_pyr(max_levels); vector d_ori_pyr(max_levels); @@ -355,8 +355,8 @@ void orb(unsigned* out_feat, int gauss_elem = gauss_filter.strides[3] * gauss_filter.dims[3]; gauss_filter.ptr = memAlloc(gauss_elem); CUDA_CHECK(cudaMemcpyAsync(gauss_filter.ptr, h_gauss.get(), gauss_elem * sizeof(convAccT), - cudaMemcpyHostToDevice, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyHostToDevice, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); } for (int i = 0; i < (int)max_levels; i++) { @@ -460,7 +460,7 @@ void orb(unsigned* out_feat, unsigned* d_desc_lvl = memAlloc(feat_pyr[i] * 8); CUDA_CHECK(cudaMemsetAsync(d_desc_lvl, 0, feat_pyr[i] * 8 * sizeof(unsigned), - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); // Compute ORB descriptors threads = dim3(THREADS_X, THREADS_Y); @@ -508,17 +508,17 @@ void orb(unsigned* out_feat, offset += feat_pyr[i-1]; CUDA_CHECK(cudaMemcpyAsync(*d_x+offset, d_x_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_y+offset, d_y_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_score+offset, d_score_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_ori+offset, d_ori_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_size+offset, d_size_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_desc+(offset*8), d_desc_pyr[i], feat_pyr[i] * 8 * sizeof(unsigned), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); memFree(d_x_pyr[i]); memFree(d_y_pyr[i]); diff --git a/src/backend/cuda/kernel/reduce.hpp b/src/backend/cuda/kernel/reduce.hpp index 3491fa69f1..30cbd05ff3 100644 --- a/src/backend/cuda/kernel/reduce.hpp +++ b/src/backend/cuda/kernel/reduce.hpp @@ -413,8 +413,8 @@ namespace kernel To* h_ptr_raw = h_ptr.get(); CUDA_CHECK(cudaMemcpyAsync(h_ptr_raw, tmp.ptr, tmp_elements * sizeof(To), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); memFree(tmp.ptr); Binary reduce; @@ -430,8 +430,8 @@ namespace kernel unique_ptr h_ptr(new Ti[in_elements]); Ti* h_ptr_raw = h_ptr.get(); CUDA_CHECK(cudaMemcpyAsync(h_ptr_raw, in.ptr, in_elements * sizeof(Ti), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); Transform transform; Binary reduce; diff --git a/src/backend/cuda/kernel/regions.hpp b/src/backend/cuda/kernel/regions.hpp index 6c9abf3a02..6014e971eb 100644 --- a/src/backend/cuda/kernel/regions.hpp +++ b/src/backend/cuda/kernel/regions.hpp @@ -13,7 +13,6 @@ #include #include #include - #include #include #include @@ -415,7 +414,7 @@ void regions(cuda::Param out, cuda::CParam in, cudaTextureObject_t tex) h_continue = 0; CUDA_CHECK(cudaMemcpyToSymbolAsync(continue_flag, &h_continue, sizeof(int), 0, cudaMemcpyHostToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); CUDA_LAUNCH((update_equiv), blocks, threads, out, tex); @@ -423,8 +422,8 @@ void regions(cuda::Param out, cuda::CParam in, cudaTextureObject_t tex) CUDA_CHECK(cudaMemcpyFromSymbolAsync(&h_continue, continue_flag, sizeof(int), 0, cudaMemcpyDeviceToHost, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); } // Now, perform the final relabeling. This converts the equivalency @@ -435,7 +434,7 @@ void regions(cuda::Param out, cuda::CParam in, cudaTextureObject_t tex) T* tmp = cuda::memAlloc(size); CUDA_CHECK(cudaMemcpyAsync(tmp, out.ptr, size * sizeof(T), cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); // Wrap raw device ptr thrust::device_ptr wrapped_tmp = thrust::device_pointer_cast(tmp); @@ -447,7 +446,7 @@ void regions(cuda::Param out, cuda::CParam in, cudaTextureObject_t tex) // compute. int num_bins = wrapped_tmp[size - 1] + 1; - thrust::device_vector labels(num_bins); + cuda::ThrustVector labels(num_bins); // Find the end of each section of values thrust::counting_iterator search_begin(0); diff --git a/src/backend/cuda/kernel/sift_nonfree.hpp b/src/backend/cuda/kernel/sift_nonfree.hpp index 0aa5749b1b..79d5c8d70b 100644 --- a/src/backend/cuda/kernel/sift_nonfree.hpp +++ b/src/backend/cuda/kernel/sift_nonfree.hpp @@ -190,8 +190,8 @@ Param gauss_filter(float sigma) dim_t gauss_elem = gauss_filter.strides[3] * gauss_filter.dims[3]; gauss_filter.ptr = memAlloc(gauss_elem); CUDA_CHECK(cudaMemcpyAsync(gauss_filter.ptr, h_gauss, gauss_elem * sizeof(T), - cudaMemcpyHostToDevice, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyHostToDevice, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); delete[] h_gauss; @@ -1239,7 +1239,7 @@ std::vector< Param > buildGaussPyr( CUDA_CHECK(cudaMemcpyAsync(gauss_pyr[o].ptr + offset, tmp_pyr[idx].ptr, imel * sizeof(T), cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); } } @@ -1283,10 +1283,10 @@ std::vector< Param > buildDoGPyr( } template -void update_permutation(thrust::device_ptr& keys, thrust::device_vector& permutation) +void update_permutation(thrust::device_ptr& keys, cuda::ThrustVector& permutation) { // temporary storage for keys - thrust::device_vector temp(permutation.size()); + cuda::ThrustVector temp(permutation.size()); // permute the keys with the current reordering THRUST_SELECT((thrust::gather), permutation.begin(), permutation.end(), keys, temp.begin()); @@ -1296,10 +1296,10 @@ void update_permutation(thrust::device_ptr& keys, thrust::device_vector& } template -void apply_permutation(thrust::device_ptr& keys, thrust::device_vector& permutation) +void apply_permutation(thrust::device_ptr& keys, cuda::ThrustVector& permutation) { // copy keys to temporary vector - thrust::device_vector temp(keys, keys+permutation.size()); + cuda::ThrustVector temp(keys, keys+permutation.size()); // permute the keys THRUST_SELECT((thrust::gather), permutation.begin(), permutation.end(), temp.begin(), keys); @@ -1361,7 +1361,7 @@ void sift(unsigned* out_feat, const unsigned max_feat = ceil(imel * feature_ratio); CUDA_CHECK(cudaMemsetAsync(d_count, 0, sizeof(unsigned), - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); float* d_extrema_x = memAlloc(max_feat); float* d_extrema_y = memAlloc(max_feat); @@ -1382,8 +1382,8 @@ void sift(unsigned* out_feat, unsigned extrema_feat = 0; CUDA_CHECK(cudaMemcpyAsync(&extrema_feat, d_count, sizeof(unsigned), cudaMemcpyDeviceToHost, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); extrema_feat = min(extrema_feat, max_feat); if (extrema_feat == 0) { @@ -1395,7 +1395,7 @@ void sift(unsigned* out_feat, } CUDA_CHECK(cudaMemsetAsync(d_count, 0, sizeof(unsigned), - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); unsigned interp_feat = 0; @@ -1421,12 +1421,12 @@ void sift(unsigned* out_feat, memFree(d_extrema_layer); CUDA_CHECK(cudaMemcpyAsync(&interp_feat, d_count, sizeof(unsigned), cudaMemcpyDeviceToHost, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); interp_feat = min(interp_feat, max_feat); CUDA_CHECK(cudaMemsetAsync(d_count, 0, sizeof(unsigned), - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); if (interp_feat == 0) { memFree(d_interp_x); @@ -1444,7 +1444,7 @@ void sift(unsigned* out_feat, thrust::device_ptr interp_response_ptr = thrust::device_pointer_cast(d_interp_response); thrust::device_ptr interp_size_ptr = thrust::device_pointer_cast(d_interp_size); - thrust::device_vector permutation(interp_feat); + cuda::ThrustVector permutation(interp_feat); thrust::sequence(permutation.begin(), permutation.end()); update_permutation(interp_size_ptr, permutation); @@ -1483,10 +1483,10 @@ void sift(unsigned* out_feat, unsigned nodup_feat = 0; CUDA_CHECK(cudaMemcpyAsync(&nodup_feat, d_count, sizeof(unsigned), cudaMemcpyDeviceToHost, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); CUDA_CHECK(cudaMemsetAsync(d_count, 0, sizeof(unsigned), - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); const unsigned max_oriented_feat = nodup_feat * 3; @@ -1517,8 +1517,8 @@ void sift(unsigned* out_feat, unsigned oriented_feat = 0; CUDA_CHECK(cudaMemcpyAsync(&oriented_feat, d_count, sizeof(unsigned), cudaMemcpyDeviceToHost, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); oriented_feat = min(oriented_feat, max_oriented_feat); if (oriented_feat == 0) { @@ -1592,19 +1592,19 @@ void sift(unsigned* out_feat, continue; CUDA_CHECK(cudaMemcpyAsync(*d_x+offset, d_x_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_y+offset, d_y_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_score+offset, d_response_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_ori+offset, d_ori_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_size+offset, d_size_pyr[i], feat_pyr[i] * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(*d_desc+(offset*desc_len), d_desc_pyr[i], feat_pyr[i] * desc_len * sizeof(float), - cudaMemcpyDeviceToDevice, cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToDevice, cuda::getActiveStream())); memFree(d_x_pyr[i]); memFree(d_y_pyr[i]); diff --git a/src/backend/cuda/kernel/sort.hpp b/src/backend/cuda/kernel/sort.hpp index 45dc023206..ac92ec8387 100644 --- a/src/backend/cuda/kernel/sort.hpp +++ b/src/backend/cuda/kernel/sort.hpp @@ -104,7 +104,7 @@ namespace cuda //val.modDims(inDims); // Not really necessary - // CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + // CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); memFree(key); } diff --git a/src/backend/cuda/kernel/sort_by_key.hpp b/src/backend/cuda/kernel/sort_by_key.hpp index a84e41c53d..8095da2d16 100644 --- a/src/backend/cuda/kernel/sort_by_key.hpp +++ b/src/backend/cuda/kernel/sort_by_key.hpp @@ -82,7 +82,7 @@ namespace cuda Tk *cKey = memAlloc(elements); CUDA_CHECK(cudaMemcpyAsync(cKey, Key, elements * sizeof(Tk), cudaMemcpyDeviceToDevice, - getStream(cuda::getActiveDeviceId()))); + getActiveStream())); Tv *Val = pVal.ptr; thrustSortByKey(Key, Val, elements, isAscending); @@ -91,7 +91,7 @@ namespace cuda uint *cSeq = memAlloc(elements); CUDA_CHECK(cudaMemcpyAsync(cSeq, Seq, elements * sizeof(uint), cudaMemcpyDeviceToDevice, - getStream(cuda::getActiveDeviceId()))); + getActiveStream())); // This always needs to be ascending thrustSortByKey(Seq, Val, elements, true); diff --git a/src/backend/cuda/kernel/susan.hpp b/src/backend/cuda/kernel/susan.hpp index 5c11e35367..c0ecc7f000 100644 --- a/src/backend/cuda/kernel/susan.hpp +++ b/src/backend/cuda/kernel/susan.hpp @@ -163,7 +163,7 @@ void nonMaximal(float* x_out, float* y_out, float* resp_out, unsigned* d_corners_found = memAlloc(1); CUDA_CHECK(cudaMemsetAsync(d_corners_found, 0, sizeof(unsigned), - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); CUDA_LAUNCH((nonMaxKernel), blocks, threads, x_out, y_out, resp_out, d_corners_found, idim0, idim1, resp_in, edge, max_corners); @@ -171,8 +171,8 @@ void nonMaximal(float* x_out, float* y_out, float* resp_out, POST_LAUNCH_CHECK(); CUDA_CHECK(cudaMemcpyAsync(count, d_corners_found, sizeof(unsigned), - cudaMemcpyDeviceToHost, cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cudaMemcpyDeviceToHost, cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); memFree(d_corners_found); } diff --git a/src/backend/cuda/kernel/transform.hpp b/src/backend/cuda/kernel/transform.hpp index 735eaa5408..f3f551a3c6 100644 --- a/src/backend/cuda/kernel/transform.hpp +++ b/src/backend/cuda/kernel/transform.hpp @@ -199,7 +199,7 @@ namespace cuda CUDA_CHECK(cudaMemcpyToSymbolAsync(c_tmat, tf.ptr, nTfs2 * nTfs3 * tf_len * sizeof(float), 0, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); dim3 threads(TX, TY, 1); dim3 blocks(divup(out.dims[0], threads.x), divup(out.dims[1], threads.y)); diff --git a/src/backend/cuda/kernel/where.hpp b/src/backend/cuda/kernel/where.hpp index 8df9b506de..c6c5894367 100644 --- a/src/backend/cuda/kernel/where.hpp +++ b/src/backend/cuda/kernel/where.hpp @@ -118,8 +118,8 @@ namespace kernel uint total; CUDA_CHECK(cudaMemcpyAsync(&total, rtmp.ptr + rtmp_elements - 1, sizeof(uint), cudaMemcpyDeviceToHost, - cuda::getStream(cuda::getActiveDeviceId()))); - CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); + CUDA_CHECK(cudaStreamSynchronize(cuda::getActiveStream())); out.ptr = memAlloc(total); diff --git a/src/backend/cuda/lu.cu b/src/backend/cuda/lu.cu index 0b92b9593a..104bc9025f 100644 --- a/src/backend/cuda/lu.cu +++ b/src/backend/cuda/lu.cu @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -21,8 +21,6 @@ namespace cuda { -using cusolver::getDnHandle; - //cusolverStatus_t CUDENSEAPI cusolverDn<>getrf_bufferSize( // cusolverDnHandle_t handle, // int m, int n, @@ -131,7 +129,7 @@ Array lu_inplace(Array &in, const bool convert_pivot) int lwork = 0; - CUSOLVER_CHECK(getrf_buf_func()(getDnHandle(), + CUSOLVER_CHECK(getrf_buf_func()(solverDnHandle(), M, N, in.get(), in.strides()[1], &lwork)); @@ -139,7 +137,7 @@ Array lu_inplace(Array &in, const bool convert_pivot) T *workspace = memAlloc(lwork); int *info = memAlloc(1); - CUSOLVER_CHECK(getrf_func()(getDnHandle(), + CUSOLVER_CHECK(getrf_func()(solverDnHandle(), M, N, in.get(), in.strides()[1], workspace, diff --git a/src/backend/cuda/memory.cpp b/src/backend/cuda/memory.cpp index 03ebc20d44..f6261dc80a 100644 --- a/src/backend/cuda/memory.cpp +++ b/src/backend/cuda/memory.cpp @@ -7,21 +7,15 @@ * http://arrayfire.com/licenses/BSD-3-Clause ********************************************************/ -#include #include #include #include +#include #include +#include #include #include -#include -#include -#include -#include #include -#include -#include - #ifndef AF_MEM_DEBUG #define AF_MEM_DEBUG 0 @@ -33,210 +27,94 @@ namespace cuda { - -class MemoryManager : public common::MemoryManager -{ - int getActiveDeviceId(); - size_t getMaxMemorySize(int id); -public: - MemoryManager(); - void *nativeAlloc(const size_t bytes); - void nativeFree(void *ptr); - ~MemoryManager() - { - common::lock_guard_t lock(this->memory_mutex); - for (int n = 0; n < getDeviceCount(); n++) { - try { - cuda::setDevice(n); - this->garbageCollect(); - } catch(AfError err) { - continue; // Do not throw any errors while shutting down - } - } - } -}; - -// CUDA Pinned Memory does not depend on device -// So we pass 1 as numDevices to the constructor so that it creates 1 vector -// of memory_info -// When allocating and freeing, it doesn't really matter which device is active -class MemoryManagerPinned : public common::MemoryManager -{ - int getActiveDeviceId(); - size_t getMaxMemorySize(int id); -public: - MemoryManagerPinned(); - void *nativeAlloc(const size_t bytes); - void nativeFree(void *ptr); - ~MemoryManagerPinned() - { - common::lock_guard_t lock(this->memory_mutex); - this->garbageCollect(); - } -}; - -int MemoryManager::getActiveDeviceId() -{ - return cuda::getActiveDeviceId(); -} - -size_t MemoryManager::getMaxMemorySize(int id) -{ - return cuda::getDeviceMemorySize(id); -} - -MemoryManager::MemoryManager() : - common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, AF_MEM_DEBUG || AF_CUDA_MEM_DEBUG) -{ - this->setMaxMemorySize(); -} - -void *MemoryManager::nativeAlloc(const size_t bytes) -{ - void *ptr = NULL; - CUDA_CHECK(cudaMalloc(&ptr, bytes)); - return ptr; -} - -void MemoryManager::nativeFree(void *ptr) -{ - cudaError_t err = cudaFree(ptr); - if (err != cudaErrorCudartUnloading) { - CUDA_CHECK(err); - } -} - -static MemoryManager &getMemoryManager() -{ - static MemoryManager instance; - return instance; -} - -int MemoryManagerPinned::getActiveDeviceId() -{ - return 0; // pinned uses a single vector -} - -size_t MemoryManagerPinned::getMaxMemorySize(int id) -{ - return cuda::getHostMemorySize(); -} - -MemoryManagerPinned::MemoryManagerPinned() : - common::MemoryManager(1, common::MAX_BUFFERS, AF_MEM_DEBUG || AF_CUDA_MEM_DEBUG) -{ - this->setMaxMemorySize(); -} - -void *MemoryManagerPinned::nativeAlloc(const size_t bytes) -{ - void *ptr; - CUDA_CHECK(cudaMallocHost(&ptr, bytes)); - return ptr; -} - -void MemoryManagerPinned::nativeFree(void *ptr) -{ - cudaError_t err = cudaFreeHost(ptr); - if (err != cudaErrorCudartUnloading) { - CUDA_CHECK(err); - } -} - -static MemoryManagerPinned &getMemoryManagerPinned() -{ - static MemoryManagerPinned instance; - return instance; -} - void setMemStepSize(size_t step_bytes) { - getMemoryManager().setMemStepSize(step_bytes); + memoryManager().setMemStepSize(step_bytes); } size_t getMemStepSize(void) { - return getMemoryManager().getMemStepSize(); + return memoryManager().getMemStepSize(); } size_t getMaxBytes() { - return getMemoryManager().getMaxBytes(); + return memoryManager().getMaxBytes(); } unsigned getMaxBuffers() { - return getMemoryManager().getMaxBuffers(); + return memoryManager().getMaxBuffers(); } void garbageCollect() { - getMemoryManager().garbageCollect(); + memoryManager().garbageCollect(); } void printMemInfo(const char *msg, const int device) { - getMemoryManager().printInfo(msg, device); + memoryManager().printInfo(msg, device); } template T* memAlloc(const size_t &elements) { - return (T *)getMemoryManager().alloc(elements * sizeof(T), false); + return (T *)memoryManager().alloc(elements * sizeof(T), false); } void* memAllocUser(const size_t &bytes) { - return getMemoryManager().alloc(bytes, true); + return memoryManager().alloc(bytes, true); } template void memFree(T *ptr) { - return getMemoryManager().unlock((void *)ptr, false); + return memoryManager().unlock((void *)ptr, false); } void memFreeUser(void *ptr) { - getMemoryManager().unlock((void *)ptr, true); + memoryManager().unlock((void *)ptr, true); } void memLock(const void *ptr) { - getMemoryManager().userLock((void *)ptr); + memoryManager().userLock((void *)ptr); } void memUnlock(const void *ptr) { - getMemoryManager().userUnlock((void *)ptr); + memoryManager().userUnlock((void *)ptr); } bool isLocked(const void *ptr) { - return getMemoryManager().isUserLocked((void *)ptr); + return memoryManager().isUserLocked((void *)ptr); } void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, size_t *lock_bytes, size_t *lock_buffers) { - getMemoryManager().bufferInfo(alloc_bytes, alloc_buffers, + memoryManager().bufferInfo(alloc_bytes, alloc_buffers, lock_bytes, lock_buffers); } template T* pinnedAlloc(const size_t &elements) { - return (T *)getMemoryManagerPinned().alloc(elements * sizeof(T), false); + return (T *)pinnedMemoryManager().alloc(elements * sizeof(T), false); } template void pinnedFree(T* ptr) { - return getMemoryManagerPinned().unlock((void *)ptr, false); + return pinnedMemoryManager().unlock((void *)ptr, false); } bool checkMemoryLimit() { - return getMemoryManager().checkMemoryLimit(); + return memoryManager().checkMemoryLimit(); } #define INSTANTIATE(T) \ @@ -258,4 +136,85 @@ bool checkMemoryLimit() INSTANTIATE(short) INSTANTIATE(ushort) +MemoryManager::MemoryManager() + : common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, + AF_MEM_DEBUG || AF_CUDA_MEM_DEBUG) +{ + this->setMaxMemorySize(); +} + +MemoryManager::~MemoryManager() +{ + common::lock_guard_t lock(this->memory_mutex); + for (int n = 0; n < cuda::getDeviceCount(); n++) { + try { + cuda::setDevice(n); + garbageCollect(); + } catch(AfError err) { + continue; // Do not throw any errors while shutting down + } + } +} + +int MemoryManager::getActiveDeviceId() +{ + return cuda::getActiveDeviceId(); +} + +size_t MemoryManager::getMaxMemorySize(int id) +{ + return cuda::getDeviceMemorySize(id); +} + +void *MemoryManager::nativeAlloc(const size_t bytes) +{ + void *ptr = NULL; + CUDA_CHECK(cudaMalloc(&ptr, bytes)); + return ptr; +} + +void MemoryManager::nativeFree(void *ptr) +{ + cudaError_t err = cudaFree(ptr); + if (err != cudaErrorCudartUnloading) { + CUDA_CHECK(err); + } +} + +MemoryManagerPinned::MemoryManagerPinned() + : common::MemoryManager(1, common::MAX_BUFFERS, + AF_MEM_DEBUG || AF_CUDA_MEM_DEBUG) +{ + this->setMaxMemorySize(); +} + +MemoryManagerPinned::~MemoryManagerPinned() +{ + garbageCollect(); +} + +int MemoryManagerPinned::getActiveDeviceId() +{ + return 0; // pinned uses a single vector +} + +size_t MemoryManagerPinned::getMaxMemorySize(int id) +{ + return cuda::getHostMemorySize(); +} + +void *MemoryManagerPinned::nativeAlloc(const size_t bytes) +{ + void *ptr; + CUDA_CHECK(cudaMallocHost(&ptr, bytes)); + return ptr; +} + +void MemoryManagerPinned::nativeFree(void *ptr) +{ + cudaError_t err = cudaFreeHost(ptr); + if (err != cudaErrorCudartUnloading) { + CUDA_CHECK(err); + } +} } diff --git a/src/backend/cuda/memory.hpp b/src/backend/cuda/memory.hpp index ab895168d4..9c0ff38503 100644 --- a/src/backend/cuda/memory.hpp +++ b/src/backend/cuda/memory.hpp @@ -9,37 +9,64 @@ #pragma once #include +#include namespace cuda { - template T* memAlloc(const size_t &elements); - void *memAllocUser(const size_t &bytes); +template T* memAlloc(const size_t &elements); +void *memAllocUser(const size_t &bytes); - // Need these as 2 separate function and not a default argument - // This is because it is used as the deleter in shared pointer - // which cannot support default arguments - template void memFree(T* ptr); - void memFreeUser(void* ptr); +// Need these as 2 separate function and not a default argument +// This is because it is used as the deleter in shared pointer +// which cannot support default arguments +template void memFree(T* ptr); +void memFreeUser(void* ptr); - void memLock(const void *ptr); - void memUnlock(const void *ptr); - bool isLocked(const void *ptr); +void memLock(const void *ptr); +void memUnlock(const void *ptr); +bool isLocked(const void *ptr); - template T* pinnedAlloc(const size_t &elements); - template void pinnedFree(T* ptr); +template T* pinnedAlloc(const size_t &elements); +template void pinnedFree(T* ptr); - size_t getMaxBytes(); - unsigned getMaxBuffers(); +size_t getMaxBytes(); +unsigned getMaxBuffers(); - void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, - size_t *lock_bytes, size_t *lock_buffers); - void garbageCollect(); - void pinnedGarbageCollect(); +void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, + size_t *lock_bytes, size_t *lock_buffers); +void garbageCollect(); +void pinnedGarbageCollect(); - void printMemInfo(const char *msg, const int device); +void printMemInfo(const char *msg, const int device); - void setMemStepSize(size_t step_bytes); - size_t getMemStepSize(void); +void setMemStepSize(size_t step_bytes); +size_t getMemStepSize(void); - bool checkMemoryLimit(); +bool checkMemoryLimit(); + +class MemoryManager : public common::MemoryManager +{ + public: + MemoryManager(); + ~MemoryManager(); + int getActiveDeviceId(); + size_t getMaxMemorySize(int id); + void *nativeAlloc(const size_t bytes); + void nativeFree(void *ptr); +}; + +// CUDA Pinned Memory does not depend on device +// So we pass 1 as numDevices to the constructor so that it creates 1 vector +// of memory_info +// When allocating and freeing, it doesn't really matter which device is active +class MemoryManagerPinned : public common::MemoryManager +{ + public: + MemoryManagerPinned(); + ~MemoryManagerPinned(); + int getActiveDeviceId(); + size_t getMaxMemorySize(int id); + void *nativeAlloc(const size_t bytes); + void nativeFree(void *ptr); +}; } diff --git a/src/backend/cuda/morph3d_impl.hpp b/src/backend/cuda/morph3d_impl.hpp index d5a19f7128..e6233c989e 100644 --- a/src/backend/cuda/morph3d_impl.hpp +++ b/src/backend/cuda/morph3d_impl.hpp @@ -34,7 +34,7 @@ Array morph3d(const Array &in, const Array &mask) CUDA_CHECK(cudaMemcpyToSymbolAsync(kernel::cFilter, mask.get(), mdims[0] * mdims[1] *mdims[2] * sizeof(T), 0, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); if (isDilation) kernel::morph3d(out, in, mdims[0]); diff --git a/src/backend/cuda/morph_impl.hpp b/src/backend/cuda/morph_impl.hpp index 16437985bd..994e792a4b 100644 --- a/src/backend/cuda/morph_impl.hpp +++ b/src/backend/cuda/morph_impl.hpp @@ -33,7 +33,7 @@ Array morph(const Array &in, const Array &mask) CUDA_CHECK(cudaMemcpyToSymbolAsync(kernel::cFilter, mask.get(), mdims[0] * mdims[1] * sizeof(T), 0, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); if (isDilation) kernel::morph(out, in, mdims[0]); diff --git a/src/backend/cuda/platform.cpp b/src/backend/cuda/platform.cpp index 8a13993a12..b80d288f91 100644 --- a/src/backend/cuda/platform.cpp +++ b/src/backend/cuda/platform.cpp @@ -14,22 +14,24 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + using namespace std; namespace cuda { - /////////////////////////////////////////////////////////////////////////// // HELPERS /////////////////////////////////////////////////////////////////////////// @@ -281,6 +283,13 @@ unsigned getMaxJitSize() return length; } +int& tlocalActiveDeviceId() +{ + thread_local int activeDeviceId = 0; + + return activeDeviceId; +} + int getDeviceCount() { return DeviceManager::getInstance().nDevices; @@ -288,7 +297,7 @@ int getDeviceCount() int getActiveDeviceId() { - return DeviceManager::getInstance().activeDev; + return tlocalActiveDeviceId(); } int getDeviceNativeId(int device) @@ -312,17 +321,20 @@ int getDeviceIdFromNativeId(int nativeId) cudaStream_t getStream(int device) { - cudaStream_t str = DeviceManager::getInstance().streams[device]; - // if the stream has not yet been initialized, ie. the device has not been - // set to active at least once (cuz that's where the stream is created) - // then set the device, get the stream, reset the device to current - if(!str) { - int active_dev = DeviceManager::getInstance().activeDev; - setDevice(device); - str = DeviceManager::getInstance().streams[device]; - setDevice(active_dev); - } - return str; + static std::once_flag streamInitFlags[DeviceManager::MAX_DEVICES]; + + std::call_once(streamInitFlags[device], + [device]() { + DeviceManager& inst = DeviceManager::getInstance(); + CUDA_CHECK(cudaStreamCreate( & (inst.streams[device]) )); + }); + + return DeviceManager::getInstance().streams[device]; +} + +cudaStream_t getActiveStream() +{ + return getStream(getActiveDeviceId()); } size_t getDeviceMemorySize(int device) @@ -350,14 +362,138 @@ cudaDeviceProp getDeviceProp(int device) /////////////////////////////////////////////////////////////////////////// // DeviceManager Class Functions /////////////////////////////////////////////////////////////////////////// +#if defined(WITH_GRAPHICS) +bool DeviceManager::checkGraphicsInteropCapability() +{ + static bool run_once = true; + static bool capable = true; + + if(run_once) { + unsigned int pCudaEnabledDeviceCount = 0; + int pCudaGraphicsEnabledDeviceIds = 0; + cudaGetLastError(); // Reset Errors + cudaError_t err = cudaGLGetDevices(&pCudaEnabledDeviceCount, &pCudaGraphicsEnabledDeviceIds, getDeviceCount(), cudaGLDeviceListAll); + if(err == 63) { // OS Support Failure - Happens when devices are only Tesla + capable = false; + printf("Warning: No CUDA Device capable of CUDA-OpenGL. CUDA-OpenGL Interop will use CPU fallback.\n"); + printf("Corresponding CUDA Error (%d): %s.\n", err, cudaGetErrorString(err)); + printf("This may happen if all CUDA Devices are in TCC Mode and/or not connected to a display.\n"); + } + cudaGetLastError(); // Reset Errors + run_once = false; + } + + return capable; +} +#endif + DeviceManager& DeviceManager::getInstance() { static DeviceManager my_instance; return my_instance; } +MemoryManager& memoryManager() +{ + static std::once_flag flag; + + DeviceManager& inst = DeviceManager::getInstance(); + + std::call_once(flag, [&]() { inst.memManager.reset(new MemoryManager()); }); + + return *(inst.memManager.get()); +} + +MemoryManagerPinned& pinnedMemoryManager() +{ + static std::once_flag flag; + + DeviceManager& inst = DeviceManager::getInstance(); + + std::call_once(flag, [&]() { inst.pinnedMemManager.reset(new MemoryManagerPinned()); }); + + return *(inst.pinnedMemManager.get()); +} + +#if defined(WITH_GRAPHICS) +GraphicsResourceManager& interopManager() +{ + static std::once_flag initFlags[DeviceManager::MAX_DEVICES]; + + int id = getActiveDeviceId(); + + DeviceManager& inst = DeviceManager::getInstance(); + + std::call_once(initFlags[id], [&]{ inst.gfxManagers[id].reset(new GraphicsResourceManager()); }); + + return *(inst.gfxManagers[id].get()); +} +#endif + +PlanCache& fftManager() +{ + thread_local PlanCache cufftManagers[DeviceManager::MAX_DEVICES]; + + return cufftManagers[getActiveDeviceId()]; +} + +BlasHandle blasHandle() +{ + thread_local std::unique_ptr cublasHandles[DeviceManager::MAX_DEVICES]; + thread_local std::once_flag initFlags[DeviceManager::MAX_DEVICES]; + + int id = cuda::getActiveDeviceId(); + + std::call_once(initFlags[id], [&]{ cublasHandles[id].reset(new cublasHandle()); }); + + CUBLAS_CHECK(cublasSetStream(cublasHandles[id].get()->get(), cuda::getStream(id))); + + return cublasHandles[id].get()->get(); +} + +SolveHandle solverDnHandle() +{ + thread_local std::unique_ptr cusolverHandles[DeviceManager::MAX_DEVICES]; + thread_local std::once_flag initFlags[DeviceManager::MAX_DEVICES]; + + int id = cuda::getActiveDeviceId(); + + std::call_once(initFlags[id], [&]{ cusolverHandles[id].reset(new cusolverDnHandle()); }); + + //FIXME + // This is not an ideal case. It's just a hack. + // The correct way to do is to use + // CUSOLVER_CHECK(cusolverDnSetStream(cuda::getStream(cuda::getActiveDeviceId()))) + // in the class constructor. + // However, this is causing a lot of the cusolver functions to fail. + // The only way to fix them is to use cudaDeviceSynchronize() and + // cudaStreamSynchronize() + // all over the place, but even then some calls like getrs in solve_lu + // continue to fail on any stream other than 0. + // + // cuSolver Streams patch: + // https://gist.github.com/shehzan10/414c3d04a40e7c4a03ed3c2e1b9072e7 + CUDA_CHECK(cudaStreamSynchronize(cuda::getStream(id))); + + return cusolverHandles[id].get()->get(); +} + +SparseHandle sparseHandle() +{ + thread_local std::unique_ptr cusparseHandles[DeviceManager::MAX_DEVICES]; + thread_local std::once_flag initFlags[DeviceManager::MAX_DEVICES]; + + int id = cuda::getActiveDeviceId(); + + std::call_once(initFlags[id], [&]{ cusparseHandles[id].reset(new cusparseHandle()); }); + + CUSPARSE_CHECK(cusparseSetStream(cusparseHandles[id].get()->get(), cuda::getStream(id))); + + return cusparseHandles[id].get()->get(); +} + DeviceManager::DeviceManager() - : cuDevices(0), activeDev(0), nDevices(0) + : cuDevices(0), nDevices(0) { CUDA_CHECK(cudaGetDeviceCount(&nDevices)); if (nDevices == 0) @@ -417,48 +553,49 @@ void DeviceManager::sortDevices(sort_mode mode) int DeviceManager::setActiveDevice(int device, int nId) { - static bool first = true; + thread_local bool retryFlag = true; int numDevices = cuDevices.size(); - if(device > numDevices) return -1; + if (device > numDevices) + return -1; - int old = activeDev; - if(nId == -1) nId = getDeviceNativeId(device); - CUDA_CHECK(cudaSetDevice(nId)); + int old = getActiveDeviceId(); - cudaError_t err = cudaSuccess; - if(!streams[device]) - err = cudaStreamCreate(&streams[device]); + if(nId == -1) + nId = getDeviceNativeId(device); - activeDev = device; + cudaError_t err = cudaSetDevice(nId); - if (err == cudaSuccess) return old; + if (err == cudaSuccess) { + tlocalActiveDeviceId() = device; + return old; + } - // Comes when user sets device - // If success, return. Else throw error - if (!first) { + // For the first time a thread calls setDevice, + // if the requested device is unavailable, try checking + // for other available devices - while loop below + if (!retryFlag) { CUDA_CHECK(err); return old; } - // Comes only when first is true. Set it to false - first = false; + // Comes only when retryFlag is true. Set it to false + retryFlag = false; while(true) { // Check for errors other than DevicesUnavailable // If success, return. Else throw error // If DevicesUnavailable, try other devices (while loop below) - if (err != cudaErrorDevicesUnavailable) { + if (err != cudaErrorDeviceAlreadyInUse) { CUDA_CHECK(err); - activeDev = device; + tlocalActiveDeviceId() = device; return old; } cudaGetLastError(); // Reset error stack #ifndef NDEBUG printf("Warning: Device %d is unavailable. Incrementing to next device \n", device); #endif - // Comes here is the device is in exclusive mode or // otherwise fails streamCreate with this error. // All other errors will error out @@ -468,11 +605,10 @@ int DeviceManager::setActiveDevice(int device, int nId) // Can't call getNativeId here as it will cause an infinite loop with the constructor nId = cuDevices[device].nativeId; - CUDA_CHECK(cudaSetDevice(nId)); - err = cudaStreamCreate(&streams[device]); + err = cudaSetDevice(nId); } - // If all devices fail with DevicesUnavailable, then throw this error + // If all devices fail with DeviceAlreadyInUse, then throw this error CUDA_CHECK(err); return old; @@ -482,11 +618,12 @@ void sync(int device) { int currDevice = getActiveDeviceId(); setDevice(device); - CUDA_CHECK(cudaStreamSynchronize(getStream(getActiveDeviceId()))); + CUDA_CHECK(cudaStreamSynchronize(getActiveStream())); setDevice(currDevice); } -bool synchronize_calls() { +bool synchronize_calls() +{ static bool sync = getEnvVar("AF_SYNCHRONOUS_CALLS") == "1"; return sync; } @@ -496,7 +633,6 @@ bool& evalFlag() static bool flag = true; return flag; } - } af_err afcu_get_stream(cudaStream_t* stream, int id) diff --git a/src/backend/cuda/platform.hpp b/src/backend/cuda/platform.hpp index fa73ccf5e0..b9ac3df401 100644 --- a/src/backend/cuda/platform.hpp +++ b/src/backend/cuda/platform.hpp @@ -11,15 +11,19 @@ #include #include +#include #include #include -#if defined(WITH_GRAPHICS) -#include -#endif +#include +#include +#include +#include +#include +#include +#include namespace cuda { - int getBackend(); std::string getDeviceInfo(); @@ -45,6 +49,8 @@ int getDeviceNativeId(int device); cudaStream_t getStream(int device); +cudaStream_t getActiveStream(); + size_t getDeviceMemorySize(int device); size_t getHostMemorySize(); @@ -66,13 +72,45 @@ struct cudaDevice_t { bool& evalFlag(); +///////////////////////// BEGIN Sub-Managers /////////////////// +// +MemoryManager& memoryManager(); + +MemoryManagerPinned& pinnedMemoryManager(); + +#if defined(WITH_GRAPHICS) +GraphicsResourceManager& interopManager(); +#endif + +PlanCache& fftManager(); + +BlasHandle blasHandle(); + +SolveHandle solverDnHandle(); + +SparseHandle sparseHandle(); +// +///////////////////////// END Sub-Managers ///////////////////// + class DeviceManager { public: static const unsigned MAX_DEVICES = 16; +#if defined(WITH_GRAPHICS) + static bool checkGraphicsInteropCapability(); +#endif + static DeviceManager& getInstance(); + friend MemoryManager& memoryManager(); + + friend MemoryManagerPinned& pinnedMemoryManager(); + +#if defined(WITH_GRAPHICS) + friend GraphicsResourceManager& interopManager(); +#endif + friend std::string getDeviceInfo(int device); friend std::string getPlatformInfo(); @@ -85,8 +123,6 @@ class DeviceManager friend int getDeviceCount(); - friend int getActiveDeviceId(); - friend int getDeviceNativeId(int device); friend int getDeviceIdFromNativeId(int nativeId); @@ -116,9 +152,15 @@ class DeviceManager int setActiveDevice(int device, int native = -1); - int activeDev; int nDevices; cudaStream_t streams[MAX_DEVICES]; -}; + std::unique_ptr memManager; + + std::unique_ptr pinnedMemManager; + +#if defined(WITH_GRAPHICS) + std::unique_ptr gfxManagers[MAX_DEVICES]; +#endif +}; } diff --git a/src/backend/cuda/plot.cpp b/src/backend/cuda/plot.cpp index 6c1aad70af..e9d7bea738 100644 --- a/src/backend/cuda/plot.cpp +++ b/src/backend/cuda/plot.cpp @@ -9,7 +9,6 @@ #if defined (WITH_GRAPHICS) -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include using af::dim4; @@ -27,20 +27,18 @@ using namespace gl; template void copy_plot(const Array &P, forge::Plot* plot) { - if(InteropManager::checkGraphicsInteropCapability()) { + if(DeviceManager::checkGraphicsInteropCapability()) { const T *d_P = P.get(); - InteropManager& intrpMngr = InteropManager::getInstance(); + ShrdResVector res = interopManager().getBufferResource(plot); - cudaGraphicsResource_t *resources = intrpMngr.getBufferResource(plot); // Map resource. Copy data to VBO. Unmap resource. size_t num_bytes = plot->verticesSize(); T* d_vbo = NULL; - cudaGraphicsMapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, resources[0]); - cudaMemcpyAsync(d_vbo, d_P, num_bytes, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsUnmapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); + cudaGraphicsMapResources(1, res[0].get(), cuda::getActiveStream()); + cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, *(res[0].get())); + cudaMemcpyAsync(d_vbo, d_P, num_bytes, cudaMemcpyDeviceToDevice, cuda::getActiveStream()); + cudaGraphicsUnmapResources(1, res[0].get(), cuda::getActiveStream()); CheckGL("After cuda resource copy"); diff --git a/src/backend/cuda/qr.cu b/src/backend/cuda/qr.cu index 9da6405006..b058453310 100644 --- a/src/backend/cuda/qr.cu +++ b/src/backend/cuda/qr.cu @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -24,8 +24,6 @@ namespace cuda { -using cusolver::getDnHandle; - //cusolverStatus_t cusolverDn<>geqrf_bufferSize( // cusolverDnHandle_t handle, // int m, int n, @@ -132,7 +130,7 @@ void qr(Array &q, Array &r, Array &t, const Array &in) int lwork = 0; - CUSOLVER_CHECK(geqrf_buf_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_buf_func()(solverDnHandle(), M, N, in_copy.get(), in_copy.strides()[1], &lwork)); @@ -142,7 +140,7 @@ void qr(Array &q, Array &r, Array &t, const Array &in) t = createEmptyArray(af::dim4(min(M, N), 1, 1, 1)); int *info = memAlloc(1); - CUSOLVER_CHECK(geqrf_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_func()(solverDnHandle(), M, N, in_copy.get(), in_copy.strides()[1], t.get(), @@ -159,7 +157,7 @@ void qr(Array &q, Array &r, Array &t, const Array &in) dim4 qdims(M, mn); q = identity(qdims); - CUSOLVER_CHECK(mqr_func()(getDnHandle(), + CUSOLVER_CHECK(mqr_func()(solverDnHandle(), CUBLAS_SIDE_LEFT, CUBLAS_OP_N, q.dims()[0], q.dims()[1], @@ -187,7 +185,7 @@ Array qr_inplace(Array &in) int lwork = 0; - CUSOLVER_CHECK(geqrf_buf_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_buf_func()(solverDnHandle(), M, N, in.get(), in.strides()[1], &lwork)); @@ -195,7 +193,7 @@ Array qr_inplace(Array &in) T *workspace = memAlloc(lwork); int *info = memAlloc(1); - CUSOLVER_CHECK(geqrf_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_func()(solverDnHandle(), M, N, in.get(), in.strides()[1], t.get(), diff --git a/src/backend/cuda/solve.cu b/src/backend/cuda/solve.cu index 68e2dcbc9c..7f3e7901ac 100644 --- a/src/backend/cuda/solve.cu +++ b/src/backend/cuda/solve.cu @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -30,8 +30,6 @@ namespace cuda { -using cusolver::getDnHandle; - //cusolverStatus_t cusolverDn<>getrs( // cusolverDnHandle_t handle, // cublasOperation_t trans, @@ -176,7 +174,7 @@ Array solveLU(const Array &A, const Array &pivot, int *info = memAlloc(1); - CUSOLVER_CHECK(getrs_func()(getDnHandle(), + CUSOLVER_CHECK(getrs_func()(solverDnHandle(), CUBLAS_OP_N, N, NRHS, A.get(), A.strides()[1], @@ -201,7 +199,7 @@ Array generalSolve(const Array &a, const Array &b) int *info = memAlloc(1); - CUSOLVER_CHECK(getrs_func()(getDnHandle(), + CUSOLVER_CHECK(getrs_func()(solverDnHandle(), CUBLAS_OP_N, N, K, A.get(), A.strides()[1], @@ -244,7 +242,7 @@ Array leastSquares(const Array &a, const Array &b) int lwork = 0; // Get workspace needed for QR - CUSOLVER_CHECK(geqrf_solve_buf_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_solve_buf_func()(solverDnHandle(), A.dims()[0], A.dims()[1], A.get(), A.strides()[1], &lwork)); @@ -254,7 +252,7 @@ Array leastSquares(const Array &a, const Array &b) int *info = memAlloc(1); // In place Perform in place QR - CUSOLVER_CHECK(geqrf_solve_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_solve_func()(solverDnHandle(), A.dims()[0], A.dims()[1], A.get(), A.strides()[1], t.get(), @@ -272,7 +270,7 @@ Array leastSquares(const Array &a, const Array &b) B.resetDims(dim4(N, K)); // matmul(Q, Bpad) - CUSOLVER_CHECK(mqr_solve_func()(getDnHandle(), + CUSOLVER_CHECK(mqr_solve_func()(solverDnHandle(), CUBLAS_SIDE_LEFT, CUBLAS_OP_N, B.dims()[0], B.dims()[1], @@ -302,7 +300,7 @@ Array leastSquares(const Array &a, const Array &b) int lwork = 0; // Get workspace needed for QR - CUSOLVER_CHECK(geqrf_solve_buf_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_solve_buf_func()(solverDnHandle(), A.dims()[0], A.dims()[1], A.get(), A.strides()[1], &lwork)); @@ -312,7 +310,7 @@ Array leastSquares(const Array &a, const Array &b) int *info = memAlloc(1); // In place Perform in place QR - CUSOLVER_CHECK(geqrf_solve_func()(getDnHandle(), + CUSOLVER_CHECK(geqrf_solve_func()(solverDnHandle(), A.dims()[0], A.dims()[1], A.get(), A.strides()[1], t.get(), @@ -320,7 +318,7 @@ Array leastSquares(const Array &a, const Array &b) info)); // matmul(Q1, B) - CUSOLVER_CHECK(mqr_solve_func()(getDnHandle(), + CUSOLVER_CHECK(mqr_solve_func()(solverDnHandle(), CUBLAS_SIDE_LEFT, trans(), M, K, N, diff --git a/src/backend/cuda/sparse.cu b/src/backend/cuda/sparse.cu index dbdf008a98..0fe53c438d 100644 --- a/src/backend/cuda/sparse.cu +++ b/src/backend/cuda/sparse.cu @@ -8,7 +8,6 @@ ********************************************************/ #include -#include #include #include @@ -27,7 +26,6 @@ namespace cuda { -using cusparse::getHandle; using namespace common; using namespace std; @@ -263,7 +261,7 @@ SparseArray sparseConvertDenseToStorage(const Array &in) int nNZ = -1; CUSPARSE_CHECK(nnz_func()( - getHandle(), + sparseHandle(), dir, M, N, descr, @@ -284,7 +282,7 @@ SparseArray sparseConvertDenseToStorage(const Array &in) if(stype == AF_STORAGE_CSR) CUSPARSE_CHECK(dense2csr_func()( - getHandle(), + sparseHandle(), M, N, descr, in.get(), in.strides()[1], @@ -292,7 +290,7 @@ SparseArray sparseConvertDenseToStorage(const Array &in) values.get(), rowIdx.get(), colIdx.get())); else CUSPARSE_CHECK(dense2csc_func()( - getHandle(), + sparseHandle(), M, N, descr, in.get(), in.strides()[1], @@ -338,7 +336,7 @@ Array sparseConvertStorageToDense(const SparseArray &in) if(stype == AF_STORAGE_CSR) CUSPARSE_CHECK(csr2dense_func()( - getHandle(), + sparseHandle(), M, N, descr, in.getValues().get(), @@ -347,7 +345,7 @@ Array sparseConvertStorageToDense(const SparseArray &in) dense.get(), d_strides1)); else CUSPARSE_CHECK(csc2dense_func()( - getHandle(), + sparseHandle(), M, N, descr, in.getValues().get(), @@ -375,11 +373,11 @@ SparseArray sparseConvertStorageToStorage(const SparseArray &in) CUDA_CHECK(cudaMemcpyAsync(converted.getColIdx().get(), in.getColIdx().get(), in.getColIdx().elements() * sizeof(int), cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); // cusparse function to expand compressed row into coordinate CUSPARSE_CHECK(cusparseXcsr2coo( - getHandle(), + sparseHandle(), in.getRowIdx().get(), nNZ, in.dims()[0], converted.getRowIdx().get(), @@ -388,23 +386,23 @@ SparseArray sparseConvertStorageToStorage(const SparseArray &in) // Call sort size_t pBufferSizeInBytes = 0; CUSPARSE_CHECK(cusparseXcoosort_bufferSizeExt( - getHandle(), + sparseHandle(), in.dims()[0], in.dims()[1], nNZ, converted.getRowIdx().get(), converted.getColIdx().get(), &pBufferSizeInBytes)); shared_ptr pBuffer(memAlloc(pBufferSizeInBytes), memFree); shared_ptr P(memAlloc(nNZ), memFree); - CUSPARSE_CHECK(cusparseCreateIdentityPermutation(getHandle(), nNZ, P.get())); + CUSPARSE_CHECK(cusparseCreateIdentityPermutation(sparseHandle(), nNZ, P.get())); CUSPARSE_CHECK(cusparseXcoosortByColumn( - getHandle(), + sparseHandle(), in.dims()[0], in.dims()[1], nNZ, converted.getRowIdx().get(), converted.getColIdx().get(), P.get(), (void*)pBuffer.get())); CUSPARSE_CHECK(gthr_func()( - getHandle(), nNZ, + sparseHandle(), nNZ, in.getValues().get(), converted.getValues().get(), P.get(), CUSPARSE_INDEX_BASE_ZERO)); @@ -423,23 +421,23 @@ SparseArray sparseConvertStorageToStorage(const SparseArray &in) { size_t pBufferSizeInBytes = 0; CUSPARSE_CHECK(cusparseXcoosort_bufferSizeExt( - getHandle(), + sparseHandle(), cooT.dims()[0], cooT.dims()[1], nNZ, cooT.getRowIdx().get(), cooT.getColIdx().get(), &pBufferSizeInBytes)); shared_ptr pBuffer(memAlloc(pBufferSizeInBytes), memFree); shared_ptr P(memAlloc(nNZ), memFree); - CUSPARSE_CHECK(cusparseCreateIdentityPermutation(getHandle(), nNZ, P.get())); + CUSPARSE_CHECK(cusparseCreateIdentityPermutation(sparseHandle(), nNZ, P.get())); CUSPARSE_CHECK(cusparseXcoosortByRow( - getHandle(), + sparseHandle(), cooT.dims()[0], cooT.dims()[1], nNZ, cooT.getRowIdx().get(), cooT.getColIdx().get(), P.get(), (void*)pBuffer.get())); CUSPARSE_CHECK(gthr_func()( - getHandle(), nNZ, + sparseHandle(), nNZ, in.getValues().get(), cooT.getValues().get(), P.get(), CUSPARSE_INDEX_BASE_ZERO)); @@ -450,15 +448,15 @@ SparseArray sparseConvertStorageToStorage(const SparseArray &in) CUDA_CHECK(cudaMemcpyAsync(converted.getValues().get(), cooT.getValues().get(), cooT.getValues().elements() * sizeof(T), cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); CUDA_CHECK(cudaMemcpyAsync(converted.getColIdx().get(), cooT.getColIdx().get(), cooT.getColIdx().elements() * sizeof(int), cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId()))); + cuda::getActiveStream())); // cusparse function to compress row from coordinate CUSPARSE_CHECK(cusparseXcoo2csr( - getHandle(), + sparseHandle(), cooT.getRowIdx().get(), nNZ, cooT.dims()[0], converted.getRowIdx().get(), diff --git a/src/backend/cuda/sparse_blas.cpp b/src/backend/cuda/sparse_blas.cpp index a8394abdcb..df57c9c667 100644 --- a/src/backend/cuda/sparse_blas.cpp +++ b/src/backend/cuda/sparse_blas.cpp @@ -8,7 +8,6 @@ ********************************************************/ #include -#include #include #include @@ -21,7 +20,6 @@ namespace cuda { -using cusparse::getHandle; using namespace std; cusparseOperation_t @@ -157,7 +155,7 @@ Array matmul(const common::SparseArray lhs, const Array rhs, // and not OP(A) (gemm wants row/col of OP(A)). if(rDims[rColDim] == 1) { CUSPARSE_CHECK(csrmv_func()( - getHandle(), + sparseHandle(), lOpts, lDims[0], lDims[1], lhs.getNNZ(), &alpha, @@ -168,7 +166,7 @@ Array matmul(const common::SparseArray lhs, const Array rhs, out.get())); } else { CUSPARSE_CHECK(csrmm_func()( - getHandle(), + sparseHandle(), lOpts, lDims[0], rDims[rColDim], lDims[1], lhs.getNNZ(), &alpha, diff --git a/src/backend/cuda/surface.cpp b/src/backend/cuda/surface.cpp index 6d84d714b6..cc7ac29e73 100644 --- a/src/backend/cuda/surface.cpp +++ b/src/backend/cuda/surface.cpp @@ -9,7 +9,6 @@ #if defined (WITH_GRAPHICS) -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include using af::dim4; @@ -27,20 +27,18 @@ using namespace gl; template void copy_surface(const Array &P, forge::Surface* surface) { - if(InteropManager::checkGraphicsInteropCapability()) { + if(DeviceManager::checkGraphicsInteropCapability()) { const T *d_P = P.get(); - InteropManager& intrpMngr = InteropManager::getInstance(); + ShrdResVector res = interopManager().getBufferResource(surface); - cudaGraphicsResource_t *resources = intrpMngr.getBufferResource(surface); // Map resource. Copy data to VBO. Unmap resource. size_t num_bytes = surface->verticesSize(); T* d_vbo = NULL; - cudaGraphicsMapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, resources[0]); - cudaMemcpyAsync(d_vbo, d_P, num_bytes, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId())); - cudaGraphicsUnmapResources(1, resources, cuda::getStream(cuda::getActiveDeviceId())); + cudaGraphicsMapResources(1, res[0].get(), cuda::getActiveStream()); + cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, *(res[0].get())); + cudaMemcpyAsync(d_vbo, d_P, num_bytes, cudaMemcpyDeviceToDevice, cuda::getActiveStream()); + cudaGraphicsUnmapResources(1, res[0].get(), cuda::getActiveStream()); CheckGL("After cuda resource copy"); diff --git a/src/backend/cuda/svd.cu b/src/backend/cuda/svd.cu index 2cea6b9277..c323ab62ea 100644 --- a/src/backend/cuda/svd.cu +++ b/src/backend/cuda/svd.cu @@ -10,19 +10,17 @@ #include #include -#include +#include #include "transpose.hpp" #include #include #include #include -#include +#include namespace cuda { - using cusolver::getDnHandle; - template cusolverStatus_t gesvd_buf_func(cusolverDnHandle_t handle, int m, int n, int *Lwork) { @@ -88,14 +86,14 @@ SVD_SPECIALIZE(cdouble, double, Z); int lwork = 0; - CUSOLVER_CHECK(gesvd_buf_func(getDnHandle(), M, N, &lwork)); + CUSOLVER_CHECK(gesvd_buf_func(solverDnHandle(), M, N, &lwork)); T *lWorkspace = memAlloc(lwork); Tr *rWorkspace = memAlloc(5 * std::min(M, N)); int *info = memAlloc(1); - gesvd_func(getDnHandle(), 'A', 'A', M, N, in.get(), + gesvd_func(solverDnHandle(), 'A', 'A', M, N, in.get(), M, s.get(), u.get(), M, vt.get(), N, lWorkspace, lwork, rWorkspace, info); diff --git a/src/backend/cuda/types.hpp b/src/backend/cuda/types.hpp index 26d0bb658d..08aab5f374 100644 --- a/src/backend/cuda/types.hpp +++ b/src/backend/cuda/types.hpp @@ -13,17 +13,17 @@ namespace cuda { - typedef cuFloatComplex cfloat; - typedef cuDoubleComplex cdouble; - typedef unsigned int uint; - typedef unsigned char uchar; - typedef unsigned short ushort; +typedef cuFloatComplex cfloat; +typedef cuDoubleComplex cdouble; +typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned short ushort; - template struct is_complex { static const bool value = false; }; - template<> struct is_complex { static const bool value = true; }; - template<> struct is_complex { static const bool value = true; }; +template struct is_complex { static const bool value = false; }; +template<> struct is_complex { static const bool value = true; }; +template<> struct is_complex { static const bool value = true; }; - template const std::string cuMangledName(const char *fn); - template const char *afShortName(bool caps = true); - template const char *irname(); +template const std::string cuMangledName(const char *fn); +template const char *afShortName(bool caps = true); +template const char *irname(); } diff --git a/src/backend/cuda/vector_field.cpp b/src/backend/cuda/vector_field.cpp index 7ce587f199..043d0ba591 100644 --- a/src/backend/cuda/vector_field.cpp +++ b/src/backend/cuda/vector_field.cpp @@ -9,11 +9,11 @@ #if defined (WITH_GRAPHICS) -#include #include #include #include #include +#include using af::dim4; @@ -25,14 +25,13 @@ template void copy_vector_field(const Array &points, const Array &directions, forge::VectorField* vector_field) { - if(InteropManager::checkGraphicsInteropCapability()) { - InteropManager& intrpMngr = InteropManager::getInstance(); - - cudaGraphicsResource_t *resources = intrpMngr.getBufferResource(vector_field); + if(DeviceManager::checkGraphicsInteropCapability()) { + ShrdResVector res = interopManager().getBufferResource(vector_field); + CGR_t resources[2] = {*res[0].get(), *res[1].get()}; // Map resource. Copy data to VBO. Unmap resource. // Map all resources at once. - cudaGraphicsMapResources(2, resources, cuda::getStream(cuda::getActiveDeviceId())); + cudaGraphicsMapResources(2, resources, cuda::getActiveStream()); // Points { @@ -40,8 +39,7 @@ void copy_vector_field(const Array &points, const Array &directions, size_t num_bytes = vector_field->verticesSize(); T* d_vbo = NULL; cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, resources[0]); - cudaMemcpyAsync(d_vbo, ptr, num_bytes, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId())); + cudaMemcpyAsync(d_vbo, ptr, num_bytes, cudaMemcpyDeviceToDevice, cuda::getActiveStream()); } // Directions { @@ -49,10 +47,9 @@ void copy_vector_field(const Array &points, const Array &directions, size_t num_bytes = vector_field->directionsSize(); T* d_vbo = NULL; cudaGraphicsResourceGetMappedPointer((void **)&d_vbo, &num_bytes, resources[1]); - cudaMemcpyAsync(d_vbo, ptr, num_bytes, cudaMemcpyDeviceToDevice, - cuda::getStream(cuda::getActiveDeviceId())); + cudaMemcpyAsync(d_vbo, ptr, num_bytes, cudaMemcpyDeviceToDevice, cuda::getActiveStream()); } - cudaGraphicsUnmapResources(2, resources, cuda::getStream(cuda::getActiveDeviceId())); + cudaGraphicsUnmapResources(2, resources, cuda::getActiveStream()); CheckGL("After cuda resource copy"); diff --git a/src/backend/opencl/Array.cpp b/src/backend/opencl/Array.cpp index 431de642cd..1ab28a8e31 100644 --- a/src/backend/opencl/Array.cpp +++ b/src/backend/opencl/Array.cpp @@ -18,7 +18,6 @@ #include #include #include -#include using af::dim4; @@ -243,8 +242,13 @@ namespace opencl unsigned length =0, buf_count = 0, bytes = 0; Node *n = node.get(); - n->getInfo(length, buf_count, bytes); - n->resetFlags(); + JIT::Node_map_t nodes_map; + n->getNodesMap(nodes_map); + + for(auto &entry : nodes_map) { + Node *node = entry.first; + node->getInfo(length, buf_count, bytes); + } if (2 * bytes > lock_bytes) { out.eval(); @@ -380,6 +384,17 @@ namespace opencl return; } + template + void + Array::setDataDims(const dim4 &new_dims) + { + modDims(new_dims); + data_dims = new_dims; + if (node->isBuffer()) { + node = bufferNodePtr(); + } + } + #define INSTANTIATE(T) \ template Array createHostDataArray (const dim4 &size, const T * const data); \ template Array createDeviceDataArray (const dim4 &size, const void *data); \ @@ -401,9 +416,12 @@ namespace opencl template void Array::eval(); \ template void Array::eval() const; \ template cl::Buffer* Array::device(); \ - template void writeHostDataArray (Array &arr, const T * const data, const size_t bytes); \ - template void writeDeviceDataArray (Array &arr, const void * const data, const size_t bytes); \ + template void writeHostDataArray (Array &arr, const T * const data, \ + const size_t bytes); \ + template void writeDeviceDataArray (Array &arr, const void * const data, \ + const size_t bytes); \ template void evalMultiple (std::vector*> arrays); \ + template void Array::setDataDims(const dim4 &new_dims); \ INSTANTIATE(float) INSTANTIATE(double) diff --git a/src/backend/opencl/Array.hpp b/src/backend/opencl/Array.hpp index 64ac5c742f..f7c36b697b 100644 --- a/src/backend/opencl/Array.hpp +++ b/src/backend/opencl/Array.hpp @@ -204,11 +204,7 @@ namespace opencl return data_dims; } - void setDataDims(const dim4 &new_dims) - { - modDims(new_dims); - data_dims = new_dims; - } + void setDataDims(const dim4 &new_dims); size_t getAllocatedBytes() const { diff --git a/src/backend/opencl/CMakeLists.txt b/src/backend/opencl/CMakeLists.txt index f0e06d9a20..b0d1a1e537 100644 --- a/src/backend/opencl/CMakeLists.txt +++ b/src/backend/opencl/CMakeLists.txt @@ -107,10 +107,6 @@ ENDIF() INCLUDE_DIRECTORIES(${CLFFT_INCLUDE_DIRS}) LINK_DIRECTORIES(${CLFFT_LIBRARY_DIR}) -ADD_DEFINITIONS( -DBOOST_ALL_NO_LIB ) -SET(Boost_USE_STATIC_LIBS OFF) -FIND_PACKAGE(Boost 1.48 REQUIRED) - OPTION(USE_SYSTEM_BOOST_COMPUTE "Use system BoostCompute" OFF) IF(USE_SYSTEM_BOOST_COMPUTE) IF(Boost_VERSION VERSION_LESS "1.61") @@ -132,7 +128,6 @@ INCLUDE_DIRECTORIES( ${CLBLAST_INCLUDE_DIRS} ${CLBLAS_INCLUDE_DIRS} ${CLFFT_INCLUDE_DIRS} - ${Boost_INCLUDE_DIR} ${BoostCompute_INCLUDE_DIRS} ${CBLAS_INCLUDE_DIR} ) @@ -216,6 +211,7 @@ FILE(GLOB backend_headers ) FILE(GLOB backend_sources + "../common/*.cpp" "../*.cpp" ) diff --git a/src/backend/opencl/GraphicsResourceManager.cpp b/src/backend/opencl/GraphicsResourceManager.cpp new file mode 100644 index 0000000000..77d20ee9b9 --- /dev/null +++ b/src/backend/opencl/GraphicsResourceManager.cpp @@ -0,0 +1,26 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#if defined(WITH_GRAPHICS) +#include +#include + +namespace opencl +{ +ShrdResVector GraphicsResourceManager::registerResources(std::vector resources) +{ + ShrdResVector output; + + for (auto id: resources) + output.emplace_back(new cl::BufferGL(getContext(), CL_MEM_WRITE_ONLY, id, NULL)); + + return output; +} +} +#endif diff --git a/src/backend/opencl/GraphicsResourceManager.hpp b/src/backend/opencl/GraphicsResourceManager.hpp new file mode 100644 index 0000000000..d498dab894 --- /dev/null +++ b/src/backend/opencl/GraphicsResourceManager.hpp @@ -0,0 +1,39 @@ +/******************************************************* + * Copyright (c) 2016, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once + +#if defined(WITH_GRAPHICS) +#include +#include +#include + +namespace cl +{ +class Buffer; +} + +namespace opencl +{ +typedef cl::Buffer CGR_t; +typedef std::shared_ptr SharedResource; +typedef std::vector ShrdResVector; + +class GraphicsResourceManager : public common::InteropManager +{ + public: + GraphicsResourceManager() {} + ShrdResVector registerResources(std::vector resources); + + protected: + GraphicsResourceManager(GraphicsResourceManager const&); + void operator=(GraphicsResourceManager const&); +}; +} +#endif diff --git a/src/backend/opencl/JIT/BinaryNode.hpp b/src/backend/opencl/JIT/BinaryNode.hpp index 5aa98810bd..c2f605f6e2 100644 --- a/src/backend/opencl/JIT/BinaryNode.hpp +++ b/src/backend/opencl/JIT/BinaryNode.hpp @@ -21,117 +21,33 @@ namespace JIT { private: std::string m_op_str; - Node_ptr m_lhs, m_rhs; int m_op; public: BinaryNode(const char *out_type_str, const char *name_str, const char *op_str, Node_ptr lhs, Node_ptr rhs, int op) - : Node(out_type_str, name_str, std::max(lhs->getHeight(), rhs->getHeight()) + 1), + : Node(out_type_str, name_str, std::max(lhs->getHeight(), rhs->getHeight()) + 1, {lhs, rhs}), m_op_str(op_str), - m_lhs(lhs), - m_rhs(rhs), m_op(op) { } - bool isLinear(dim_t dims[4]) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - if (!m_set_is_linear) { - m_linear = m_lhs->isLinear(dims) && m_rhs->isLinear(dims); - m_set_is_linear = true; - } - return m_linear; - } - - void genParams(std::stringstream &kerStream, bool is_linear) - { - if (m_gen_param) return; - if (!(m_lhs->isGenParam())) m_lhs->genParams(kerStream, is_linear); - if (!(m_rhs->isGenParam())) m_rhs->genParams(kerStream, is_linear); - m_gen_param = true; - } - - int setArgs(cl::Kernel &ker, int id, bool is_linear) - { - if (m_set_arg) return id; - m_set_arg = true; - - id = m_lhs->setArgs(ker, id, is_linear); - id = m_rhs->setArgs(ker, id, is_linear); - return id; - } - - void genOffsets(std::stringstream &kerStream, bool is_linear) - { - if (m_gen_offset) return; - if (!(m_lhs->isGenOffset())) m_lhs->genOffsets(kerStream, is_linear); - if (!(m_rhs->isGenOffset())) m_rhs->genOffsets(kerStream, is_linear); - m_gen_offset = true; - } - - void genKerName(std::stringstream &kerStream) - { - if (m_gen_name) return; - m_lhs->genKerName(kerStream); - m_rhs->genKerName(kerStream); - // Make the dec representation of enum part of the Kernel name kerStream << "_" << std::setw(3) << std::setfill('0') << std::dec << m_op; - kerStream << std::setw(3) << std::setfill('0') << std::dec << m_lhs->getId(); - kerStream << std::setw(3) << std::setfill('0') << std::dec << m_rhs->getId(); - kerStream << std::setw(3) << std::setfill('0') << std::dec << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(3) << std::setfill('0') << std::dec << ids.child_ids[0]; + kerStream << std::setw(3) << std::setfill('0') << std::dec << ids.child_ids[1]; + kerStream << std::setw(3) << std::setfill('0') << std::dec << ids.id << std::dec; } - void genFuncs(std::stringstream &kerStream) + void genFuncs(std::stringstream &kerStream, Node_ids ids) { - if (m_gen_func) return; - - if (!(m_lhs->isGenFunc())) m_lhs->genFuncs(kerStream); - if (!(m_rhs->isGenFunc())) m_rhs->genFuncs(kerStream); - - kerStream << m_type_str << " val" << m_id << " = " - << m_op_str << "(val" << m_lhs->getId() - << ", val" << m_rhs->getId() << ");" + kerStream << m_type_str << " val" << ids.id << " = " + << m_op_str << "(val" << ids.child_ids[0] + << ", val" << ids.child_ids[1] << ");" << "\n"; - - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - - id = m_lhs->setId(id); - id = m_rhs->setId(id); - - m_id = id; - m_set_id = true; - - return m_id + 1; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) - { - if (m_set_id) return; - - m_lhs->getInfo(len, buf_count, bytes); - m_rhs->getInfo(len, buf_count, bytes); - len++; - - m_set_id = true; - return; - } - - void resetFlags() - { - if (m_set_id) { - resetCommonFlags(); - m_lhs->resetFlags(); - m_rhs->resetFlags(); - } } }; diff --git a/src/backend/opencl/JIT/BufferNode.hpp b/src/backend/opencl/JIT/BufferNode.hpp index 280c796a79..92a2c32697 100644 --- a/src/backend/opencl/JIT/BufferNode.hpp +++ b/src/backend/opencl/JIT/BufferNode.hpp @@ -10,6 +10,7 @@ #pragma once #include "Node.hpp" #include +#include namespace opencl { @@ -25,12 +26,13 @@ namespace JIT KParam m_info; unsigned m_bytes; bool m_linear_buffer; + std::once_flag m_set_data_flag; public: BufferNode(const char *type_str, const char *name_str) - : Node(type_str, name_str, 0) + : Node(type_str, name_str, 0, {}) { } @@ -42,70 +44,55 @@ namespace JIT void setData(KParam info, std::shared_ptr data, const unsigned bytes, bool is_linear) { - m_info = info; - m_data = data; - m_bytes = bytes; - m_linear_buffer = is_linear; + std::call_once(m_set_data_flag, [this, info, data, bytes, is_linear]() { + m_info = info; + m_data = data; + m_bytes = bytes; + m_linear_buffer = is_linear; + }); } bool isLinear(dim_t dims[4]) { - if (!m_set_is_linear) { - bool same_dims = true; - for (int i = 0; same_dims && i < 4; i++) { - same_dims &= (dims[i] == m_info.dims[i]); - } - m_set_is_linear = true; - m_linear = m_linear_buffer && same_dims; + bool same_dims = true; + for (int i = 0; same_dims && i < 4; i++) { + same_dims &= (dims[i] == m_info.dims[i]); } - return m_linear; + return m_linear_buffer && same_dims; } - void genKerName(std::stringstream &kerStream) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - if (m_gen_name) return; - kerStream << "_" << m_name_str; - kerStream << std::setw(3) << std::setfill('0') << std::dec << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(3) << std::setfill('0') << std::dec << ids.id << std::dec; } - void genParams(std::stringstream &kerStream, bool is_linear) + void genParams(std::stringstream &kerStream, int id, bool is_linear) { - if (m_gen_param) return; - if (!is_linear) { - kerStream << "__global " << m_type_str << " *in" << m_id - << ", KParam iInfo" << m_id << ", " << "\n"; + kerStream << "__global " << m_type_str << " *in" << id + << ", KParam iInfo" << id << ", " << "\n"; } else { - kerStream << "__global " << m_type_str << " *in" << m_id - << ", dim_t iInfo" << m_id << "_offset, " << "\n"; + kerStream << "__global " << m_type_str << " *in" << id + << ", dim_t iInfo" << id << "_offset, " << "\n"; } - m_gen_param = true; } int setArgs(cl::Kernel &ker, int id, bool is_linear) { - if (m_set_arg) return id; - ker.setArg(id + 0, *m_data); - if (!is_linear) { ker.setArg(id + 1, m_info); } else { ker.setArg(id + 1, m_info.offset); } - - m_set_arg = true; return id + 2; } - void genOffsets(std::stringstream &kerStream, bool is_linear) + void genOffsets(std::stringstream &kerStream, int id, bool is_linear) { - if (m_gen_offset) return; - - std::string idx_str = std::string("int idx") + std::to_string(m_id); - std::string info_str = std::string("iInfo") + std::to_string(m_id);; + std::string idx_str = std::string("int idx") + std::to_string(id); + std::string info_str = std::string("iInfo") + std::to_string(id);; if (!is_linear) { kerStream << idx_str << " = " @@ -121,47 +108,22 @@ namespace JIT } else { kerStream << idx_str << " = idx + " << info_str << "_offset;" << "\n"; } - - m_gen_offset = true; } - void genFuncs(std::stringstream &kerStream) + void genFuncs(std::stringstream &kerStream, Node_ids ids) { - if (m_gen_func) return; - - kerStream << m_type_str << " val" << m_id << " = " - << "in" << m_id << "[idx" << m_id << "];" + kerStream << m_type_str << " val" << ids.id << " = " + << "in" << ids.id << "[idx" << ids.id << "];" << "\n"; - - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - - m_id = id; - m_set_id = true; - - return m_id + 1; } void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) { - if (m_set_id) return; - len++; buf_count++; bytes += m_bytes; - m_set_id = true; return; } - - - void resetFlags() - { - if (m_set_id) resetCommonFlags(); - } }; } diff --git a/src/backend/opencl/JIT/Node.hpp b/src/backend/opencl/JIT/Node.hpp index abf7a2d908..c4e0176664 100644 --- a/src/backend/opencl/JIT/Node.hpp +++ b/src/backend/opencl/JIT/Node.hpp @@ -13,101 +13,78 @@ #include #include #include +#include namespace opencl { namespace JIT { + + class Node; using std::shared_ptr; + typedef shared_ptr Node_ptr; + + typedef struct + { + int id; + std::vector child_ids; + } Node_ids; + + typedef std::unordered_map Node_map_t; + typedef Node_map_t::iterator Node_map_iter; class Node { protected: const std::string m_type_str; const std::string m_name_str; - int m_id; const int m_height; - bool m_set_id; - bool m_gen_func; - bool m_gen_param; - bool m_gen_offset; - bool m_set_arg; - bool m_gen_name; - bool m_linear; - bool m_set_is_linear; - - protected: - void resetCommonFlags() - { - m_set_id = false; - m_gen_func = false; - m_gen_param = false; - m_gen_offset = false; - m_set_arg = false; - m_gen_name = false; - m_linear = false; - m_set_is_linear = false; - } + const std::vector m_children; public: - Node(const char *type_str, const char *name_str, const int height) + Node(const char *type_str, const char *name_str, const int height, + const std::vector children) : m_type_str(type_str), m_name_str(name_str), - m_id(-1), m_height(height), - m_set_id(false), - m_gen_func(false), - m_gen_param(false), - m_gen_offset(false), - m_set_arg(false), - m_gen_name(false), - m_linear(false), - m_set_is_linear(false) + m_children(children) {} - virtual void genKerName(std::stringstream &kerStream) {} - virtual void genParams (std::stringstream &kerStream, bool is_linear) {} - virtual void genOffsets (std::stringstream &kerStream, bool is_linear) {} - virtual void genFuncs (std::stringstream &kerStream) { m_gen_func = true;} - - virtual int setArgs (cl::Kernel &ker, int id, bool is_linear) { return id; } - - virtual int setId(int id) { m_set_id = true; return id; } - - virtual void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) + void getNodesMap(Node_map_t &node_map) { - len = 0; - buf_count = 0; - bytes = 0; + if (node_map.find(this) == node_map.end()) { + Node_ids ids; + for (const auto &child : m_children) { + child->getNodesMap(node_map); + ids.child_ids.push_back(node_map[child.get()].id); + } + ids.id = node_map.size(); + node_map[this] = ids; + } } - virtual bool isBuffer() { return false; } + virtual void genKerName(std::stringstream &kerStream, Node_ids ids) {} + virtual void genParams (std::stringstream &kerStream, int id, bool is_linear) {} + virtual void genOffsets (std::stringstream &kerStream, int id, bool is_linear) {} + virtual void genFuncs (std::stringstream &kerStream, Node_ids) {} + virtual int setArgs (cl::Kernel &ker, int id, bool is_linear) { return id; } - virtual void resetFlags() + virtual void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) { - resetCommonFlags(); + len++; } + virtual bool isBuffer() { return false; } virtual bool isLinear(dim_t dims[4]) { return true; } - std::string getTypeStr() { return m_type_str; } - - bool isGenFunc() { return m_gen_func; } - bool isGenParam() { return m_gen_param; } - bool isGenOffset() { return m_gen_offset; } - - int getId() { return m_id; } int getHeight() { return m_height; } std::string getNameStr() { return m_name_str; } virtual ~Node() {} }; - - typedef shared_ptr Node_ptr; - } } diff --git a/src/backend/opencl/JIT/ScalarNode.hpp b/src/backend/opencl/JIT/ScalarNode.hpp index b172b67680..e3e269e1fd 100644 --- a/src/backend/opencl/JIT/ScalarNode.hpp +++ b/src/backend/opencl/JIT/ScalarNode.hpp @@ -28,76 +28,33 @@ namespace JIT public: ScalarNode(T val) - : Node(dtype_traits::getName(), shortname(false), 0), + : Node(dtype_traits::getName(), shortname(false), 0, {}), m_val(val) { } - bool isLinear(dim_t dims[4]) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - return true; - } - - void genKerName(std::stringstream &kerStream) - { - if (m_gen_name) return; - kerStream << "_" << m_name_str; - kerStream << std::setw(3) << std::setfill('0') << std::dec << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(3) << std::setfill('0') << std::dec << ids.id << std::dec; } - void genParams(std::stringstream &kerStream, bool is_linear) + void genParams(std::stringstream &kerStream, int id, bool is_linear) { - if (m_gen_param) return; - kerStream << m_type_str << " scalar" << m_id << ", " << "\n"; - m_gen_param = true; + kerStream << m_type_str << " scalar" << id << ", " << "\n"; } int setArgs(cl::Kernel &ker, int id, bool is_linear) { - if (m_set_arg) return id; ker.setArg(id, m_val); - m_set_arg = true; return id + 1; } - void genOffsets(std::stringstream &kerStream, bool is_linear) + void genFuncs(std::stringstream &kerStream, Node_ids ids) { - if (m_gen_offset) return; - m_gen_offset = true; - } - - void genFuncs(std::stringstream &kerStream) - { - if (m_gen_func) return; - - kerStream << m_type_str << " val" << m_id << " = " - << "scalar" << m_id << ";" + kerStream << m_type_str << " val" << ids.id << " = " + << "scalar" << ids.id << ";" << "\n"; - - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - m_id = id; - m_set_id = true; - return m_id + 1; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) - { - if (m_set_id) return; - len++; - m_set_id = true; - return; - } - - void resetFlags() - { - if (m_set_id) resetCommonFlags(); } }; diff --git a/src/backend/opencl/JIT/UnaryNode.hpp b/src/backend/opencl/JIT/UnaryNode.hpp index 5b9bdbc481..6b178615ab 100644 --- a/src/backend/opencl/JIT/UnaryNode.hpp +++ b/src/backend/opencl/JIT/UnaryNode.hpp @@ -20,102 +20,32 @@ namespace JIT class UnaryNode : public Node { private: - std::string m_op_str; - Node_ptr m_child; - int m_op; + const std::string m_op_str; + const int m_op; public: UnaryNode(const char *out_type_str, const char *name_str, const char *op_str, Node_ptr child, int op) - : Node(out_type_str, name_str, child->getHeight() + 1), + : Node(out_type_str, name_str, child->getHeight() + 1, {child}), m_op_str(op_str), - m_child(child), m_op(op) { } - bool isLinear(dim_t dims[4]) + void genKerName(std::stringstream &kerStream, Node_ids ids) { - if (!m_set_is_linear) { - m_linear = m_child->isLinear(dims); - m_set_is_linear = true; - } - return m_linear; - } - - void genParams(std::stringstream &kerStream, bool is_linear) - { - if (m_gen_param) return; - if (!(m_child->isGenParam())) m_child->genParams(kerStream, is_linear); - m_gen_param = true; - } - - int setArgs(cl::Kernel &ker, int id, bool is_linear) - { - if (m_set_arg) return id; - m_set_arg = true; - return m_child->setArgs(ker, id, is_linear); - } - - void genOffsets(std::stringstream &kerStream, bool is_linear) - { - if (m_gen_offset) return; - if (!(m_child->isGenOffset())) m_child->genOffsets(kerStream, is_linear); - m_gen_offset = true; - } - - void genKerName(std::stringstream &kerStream) - { - if (m_gen_name) return; - m_child->genKerName(kerStream); - // Make the dec representation of enum part of the Kernel name kerStream << "_" << std::setw(3) << std::setfill('0') << std::dec << m_op; - kerStream << std::setw(3) << std::setfill('0') << std::dec << m_child->getId(); - kerStream << std::setw(3) << std::setfill('0') << std::dec << m_id << std::dec; - m_gen_name = true; + kerStream << std::setw(3) << std::setfill('0') << std::dec << ids.child_ids[0]; + kerStream << std::setw(3) << std::setfill('0') << std::dec << ids.id << std::dec; } - void genFuncs(std::stringstream &kerStream) + void genFuncs(std::stringstream &kerStream, Node_ids ids) { - if (m_gen_func) return; - - if (!(m_child->isGenFunc())) m_child->genFuncs(kerStream); - - kerStream << m_type_str << " val" << m_id << " = " - << m_op_str << "(val" << m_child->getId() << ");" + kerStream << m_type_str << " val" << ids.id << " = " + << m_op_str << "(val" << ids.child_ids[0] << ");" << "\n"; - - m_gen_func = true; - } - - int setId(int id) - { - if (m_set_id) return id; - id = m_child->setId(id); - m_id = id; - m_set_id = true; - return m_id + 1; - } - - void getInfo(unsigned &len, unsigned &buf_count, unsigned &bytes) - { - if (m_set_id) return; - - m_child->getInfo(len, buf_count, bytes); - len++; - - m_set_id = true; - return; - } - - void resetFlags() - { - if (m_set_id) { - resetCommonFlags(); - m_child->resetFlags(); - } } }; diff --git a/src/backend/opencl/blas.cpp b/src/backend/opencl/blas.cpp index b1ee8b63d3..df6b772a09 100644 --- a/src/backend/opencl/blas.cpp +++ b/src/backend/opencl/blas.cpp @@ -28,6 +28,16 @@ namespace opencl { +void initBlas() +{ + gpu_blas_init(); +} + +void deInitBlas() +{ + gpu_blas_deinit(); +} + // Converts an af_mat_prop options to a transpose type for one of the OpenCL BLAS back-ends OPENCL_BLAS_TRANS_T toBlasTranspose(af_mat_prop opt) @@ -40,13 +50,6 @@ toBlasTranspose(af_mat_prop opt) } } -// Initialization of the OpenCL BLAS library -void -initBlas() -{ - gpu_blas_init(); -} - template Array matmul(const Array &lhs, const Array &rhs, af_mat_prop optLhs, af_mat_prop optRhs) @@ -56,8 +59,6 @@ Array matmul(const Array &lhs, const Array &rhs, return cpu::matmul(lhs, rhs, optLhs, optRhs); } #endif - initBlas(); - const auto lOpts = toBlasTranspose(optLhs); const auto rOpts = toBlasTranspose(optRhs); diff --git a/src/backend/opencl/blas.hpp b/src/backend/opencl/blas.hpp index a40801cea8..6946f38166 100644 --- a/src/backend/opencl/blas.hpp +++ b/src/backend/opencl/blas.hpp @@ -16,6 +16,8 @@ namespace opencl { +void initBlas(); +void deInitBlas(); template Array matmul(const Array &lhs, const Array &rhs, @@ -24,7 +26,4 @@ Array matmul(const Array &lhs, const Array &rhs, template Array dot(const Array &lhs, const Array &rhs, af_mat_prop optLhs, af_mat_prop optRhs); - -void initBlas(); - } diff --git a/src/backend/opencl/cache.hpp b/src/backend/opencl/cache.hpp index 937d3d8eac..0dab9d9e24 100644 --- a/src/backend/opencl/cache.hpp +++ b/src/backend/opencl/cache.hpp @@ -14,14 +14,10 @@ namespace opencl { - using cl::Kernel; - using cl::Program; - typedef struct { - Program* prog; - Kernel* ker; + cl::Program* prog; + cl::Kernel* ker; } kc_entry_t; - typedef std::map kc_t; - static kc_t kernelCaches[DeviceManager::MAX_DEVICES]; + typedef std::map kc_t; } diff --git a/src/backend/opencl/cholesky.cpp b/src/backend/opencl/cholesky.cpp index f949b85a01..47df889539 100644 --- a/src/backend/opencl/cholesky.cpp +++ b/src/backend/opencl/cholesky.cpp @@ -28,8 +28,6 @@ int cholesky_inplace(Array &in, const bool is_upper) return cpu::cholesky_inplace(in, is_upper); } - initBlas(); - dim4 iDims = in.dims(); int N = iDims[0]; diff --git a/src/backend/opencl/err_clfft.hpp b/src/backend/opencl/clfft.cpp similarity index 58% rename from src/backend/opencl/err_clfft.hpp rename to src/backend/opencl/clfft.cpp index 94ca210c46..aa7d26e63d 100644 --- a/src/backend/opencl/err_clfft.hpp +++ b/src/backend/opencl/clfft.cpp @@ -1,5 +1,5 @@ /******************************************************* - * Copyright (c) 2014, ArrayFire + * Copyright (c) 2016, ArrayFire * All rights reserved. * * This file is distributed under 3-clause BSD license. @@ -7,12 +7,17 @@ * http://arrayfire.com/licenses/BSD-3-Clause ********************************************************/ -#pragma once -#include -#include -#include +#include +#include +#include +#include +#include -static const char * _clfftGetResultString(clfftStatus st) +using std::string; + +namespace opencl +{ +const char * _clfftGetResultString(clfftStatus st) { switch (st) { @@ -78,23 +83,83 @@ static const char * _clfftGetResultString(clfftStatus st) return "Unknown error"; } -#define CLFFT_CHECK(fn) do { \ - clfftStatus _clfft_st = fn; \ - if (_clfft_st != CLFFT_SUCCESS) { \ - garbageCollect(); \ - _clfft_st = (fn); \ - } \ - if (_clfft_st != CLFFT_SUCCESS) { \ - char clfft_st_msg[1024]; \ - snprintf(clfft_st_msg, \ - sizeof(clfft_st_msg), \ - "clFFT Error (%d): %s\n", \ - (int)(_clfft_st), \ - _clfftGetResultString( \ - _clfft_st)); \ - \ - AF_ERROR(clfft_st_msg, \ - AF_ERR_INTERNAL); \ - } \ - } while(0) +SharedPlan findPlan(clfftLayout iLayout, clfftLayout oLayout, + clfftDim rank, size_t *clLengths, + size_t *istrides, size_t idist, + size_t *ostrides, size_t odist, + clfftPrecision precision, size_t batch) +{ + // create the key string + char key_str_temp[64]; + sprintf(key_str_temp, "%d:%d:%d:", iLayout, oLayout, rank); + + string key_string(key_str_temp); + + /* WARNING: DO NOT CHANGE sprintf format specifier */ + for(int r=0; r +#include +#include +#include + +namespace opencl +{ +typedef clfftPlanHandle PlanType; +typedef std::shared_ptr SharedPlan; + +const char * _clfftGetResultString(clfftStatus st); + +SharedPlan findPlan(clfftLayout iLayout, clfftLayout oLayout, + clfftDim rank, size_t *clLengths, + size_t *istrides, size_t idist, + size_t *ostrides, size_t odist, + clfftPrecision precision, size_t batch); + +class PlanCache : public common::FFTPlanCache +{ + friend SharedPlan findPlan(clfftLayout iLayout, clfftLayout oLayout, + clfftDim rank, size_t *clLengths, + size_t *istrides, size_t idist, + size_t *ostrides, size_t odist, + clfftPrecision precision, size_t batch); +}; +} + +#define CLFFT_CHECK(fn) do { \ + clfftStatus _clfft_st = fn; \ + if (_clfft_st != CLFFT_SUCCESS) { \ + opencl::garbageCollect(); \ + _clfft_st = (fn); \ + } \ + if (_clfft_st != CLFFT_SUCCESS) { \ + char clfft_st_msg[1024]; \ + snprintf(clfft_st_msg, \ + sizeof(clfft_st_msg), \ + "clFFT Error (%d): %s\n", \ + (int)(_clfft_st), \ + opencl::_clfftGetResultString( \ + _clfft_st)); \ + \ + AF_ERROR(clfft_st_msg, \ + AF_ERR_INTERNAL); \ + } \ + } while(0) diff --git a/src/backend/opencl/err_clblas.hpp b/src/backend/opencl/err_clblas.hpp index 4d28f94960..1440582e2c 100644 --- a/src/backend/opencl/err_clblas.hpp +++ b/src/backend/opencl/err_clblas.hpp @@ -11,6 +11,7 @@ #include #include #include +#include static const char * _clblasGetResultString(clblasStatus st) { @@ -51,9 +52,13 @@ static const char * _clblasGetResultString(clblasStatus st) return "Unknown error"; } +static std::recursive_mutex gCLBlasMutex; + #define CLBLAS_CHECK(fn) do { \ + gCLBlasMutex.lock(); \ clblasStatus _clblas_st = fn; \ - if (_clblas_st != clblasSuccess) { \ + gCLBlasMutex.unlock(); \ + if (_clblas_st != clblasSuccess) { \ char clblas_st_msg[1024]; \ snprintf(clblas_st_msg, \ sizeof(clblas_st_msg), \ diff --git a/src/backend/opencl/err_clblast.hpp b/src/backend/opencl/err_clblast.hpp index fae1722251..c02d600d49 100644 --- a/src/backend/opencl/err_clblast.hpp +++ b/src/backend/opencl/err_clblast.hpp @@ -80,8 +80,12 @@ static const char * _clblastGetResultString(clblast::StatusCode st) return "Unknown error"; } +static std::recursive_mutex gCLBlastMutex; + #define CLBLAST_CHECK(fn) do { \ + gCLBlastMutex.lock(); \ clblast::StatusCode _clblast_st = fn; \ + gCLBlastMutex.unlock(); \ if (_clblast_st != clblast::StatusCode::kSuccess) { \ char clblast_st_msg[1024]; \ snprintf(clblast_st_msg, \ diff --git a/src/backend/opencl/err_opencl.hpp b/src/backend/opencl/err_opencl.hpp index 5d12ab2db0..841670e97e 100644 --- a/src/backend/opencl/err_opencl.hpp +++ b/src/backend/opencl/err_opencl.hpp @@ -8,7 +8,7 @@ ********************************************************/ #pragma once -#include +#include #include #include #include diff --git a/src/backend/opencl/fft.cpp b/src/backend/opencl/fft.cpp index 7cd39f46af..b882b21cdf 100644 --- a/src/backend/opencl/fft.cpp +++ b/src/backend/opencl/fft.cpp @@ -12,14 +12,9 @@ #include #include #include -#include -#include #include -#include -#include -#include -#include #include +#include using af::dim4; using std::string; @@ -27,187 +22,9 @@ using std::string; namespace opencl { -typedef std::pair FFTPlanPair; -typedef std::deque FFTPlanCache; - -// clFFTPlanner caches fft plans -// -// new plan |--> IF number of plans cached is at limit, pop the least used entry and push new plan. -// | -// |--> ELSE just push the plan -// existing plan -> reuse a plan -class clFFTPlanner -{ - friend void find_clfft_plan(clfftPlanHandle &plan, - clfftLayout iLayout, clfftLayout oLayout, - clfftDim rank, size_t *clLengths, - size_t *istrides, size_t idist, - size_t *ostrides, size_t odist, - clfftPrecision precision, size_t batch); - - public: - static clFFTPlanner& getInstance() { - static clFFTPlanner instances[opencl::DeviceManager::MAX_DEVICES]; - return instances[opencl::getActiveDeviceId()]; - } - - ~clFFTPlanner() { - //TODO: FIXME: - // clfftTeardown() cause a "Pure Virtual Function Called" crash on - // Window only when Intel devices are called. This causes tests to - // fail. - #ifndef OS_WIN - static bool flag = true; - if(flag) { - // THOU SHALL NOT THROW IN DESTRUCTORS - clfftTeardown(); - flag = false; - } - #endif - } - - inline void setMaxCacheSize(size_t size) { - mCache.resize(size, FFTPlanPair(std::string(""), 0)); - } - - inline size_t getMaxCacheSize() const { - return mMaxCacheSize; - } - - inline clfftPlanHandle getPlan(int index) const { - return mCache[index].second; - } - - // iterates through plan cache from front to back - // of the cache(queue) - int findIfPlanExists(std::string keyString) const { - int retVal = -1; - for(uint i=0; imMaxCacheSize) { - popPlan(); - } - mCache.push_front(FFTPlanPair(keyString, plan)); - } - - private: - clFFTPlanner() : mMaxCacheSize(5) { - CLFFT_CHECK(clfftInitSetupData(&mFFTSetup)); - CLFFT_CHECK(clfftSetup(&mFFTSetup)); - } - clFFTPlanner(clFFTPlanner const&); - void operator=(clFFTPlanner const&); - - clfftSetupData mFFTSetup; - - size_t mMaxCacheSize; - FFTPlanCache mCache; -}; - -void find_clfft_plan(clfftPlanHandle &plan, - clfftLayout iLayout, clfftLayout oLayout, - clfftDim rank, size_t *clLengths, - size_t *istrides, size_t idist, - size_t *ostrides, size_t odist, - clfftPrecision precision, size_t batch) -{ - // create the key string - char key_str_temp[64]; - sprintf(key_str_temp, "%d:%d:%d:", iLayout, oLayout, rank); - - string key_string(key_str_temp); - - /* WARNING: DO NOT CHANGE sprintf format specifier */ - for(int r=0; r struct Precision; @@ -261,26 +78,20 @@ void fft_inplace(Array &in) computeDims(tdims , in.dims()); computeDims(istrides, in.strides()); - clfftPlanHandle plan; - int batch = 1; for (int i = rank; i < 4; i++) { batch *= tdims[i]; } - find_clfft_plan(plan, - CLFFT_COMPLEX_INTERLEAVED, - CLFFT_COMPLEX_INTERLEAVED, - (clfftDim)rank, tdims, - istrides, istrides[rank], - istrides, istrides[rank], - (clfftPrecision)Precision::type, - batch); + SharedPlan plan = findPlan(CLFFT_COMPLEX_INTERLEAVED, CLFFT_COMPLEX_INTERLEAVED, + (clfftDim)rank, tdims, + istrides, istrides[rank], istrides, istrides[rank], + (clfftPrecision)Precision::type, batch); cl_mem imem = (*in.get())(); cl_command_queue queue = getQueue()(); - CLFFT_CHECK(clfftEnqueueTransform(plan, + CLFFT_CHECK(clfftEnqueueTransform(*plan.get(), direction ? CLFFT_FORWARD : CLFFT_BACKWARD, 1, &queue, 0, NULL, NULL, &imem, &imem, NULL)); @@ -302,27 +113,21 @@ Array fft_r2c(const Array &in) computeDims(istrides, in.strides()); computeDims(ostrides, out.strides()); - clfftPlanHandle plan; - int batch = 1; for (int i = rank; i < 4; i++) { batch *= tdims[i]; } - find_clfft_plan(plan, - CLFFT_REAL, - CLFFT_HERMITIAN_INTERLEAVED, - (clfftDim)rank, tdims, - istrides, istrides[rank], - ostrides, ostrides[rank], - (clfftPrecision)Precision::type, - batch); + SharedPlan plan = findPlan(CLFFT_REAL, CLFFT_HERMITIAN_INTERLEAVED, + (clfftDim)rank, tdims, + istrides, istrides[rank], ostrides, ostrides[rank], + (clfftPrecision)Precision::type, batch); cl_mem imem = (*in.get())(); cl_mem omem = (*out.get())(); cl_command_queue queue = getQueue()(); - CLFFT_CHECK(clfftEnqueueTransform(plan, + CLFFT_CHECK(clfftEnqueueTransform(*plan.get(), CLFFT_FORWARD, 1, &queue, 0, NULL, NULL, &imem, &omem, NULL)); @@ -342,27 +147,21 @@ Array fft_c2r(const Array &in, const dim4 &odims) computeDims(istrides, in.strides()); computeDims(ostrides, out.strides()); - clfftPlanHandle plan; - int batch = 1; for (int i = rank; i < 4; i++) { batch *= tdims[i]; } - find_clfft_plan(plan, - CLFFT_HERMITIAN_INTERLEAVED, - CLFFT_REAL, - (clfftDim)rank, tdims, - istrides, istrides[rank], - ostrides, ostrides[rank], - (clfftPrecision)Precision::type, - batch); + SharedPlan plan = findPlan(CLFFT_HERMITIAN_INTERLEAVED, CLFFT_REAL, + (clfftDim)rank, tdims, + istrides, istrides[rank], ostrides, ostrides[rank], + (clfftPrecision)Precision::type, batch); cl_mem imem = (*in.get())(); cl_mem omem = (*out.get())(); cl_command_queue queue = getQueue()(); - CLFFT_CHECK(clfftEnqueueTransform(plan, + CLFFT_CHECK(clfftEnqueueTransform(*plan.get(), CLFFT_BACKWARD, 1, &queue, 0, NULL, NULL, &imem, &omem, NULL)); diff --git a/src/backend/opencl/hist_graphics.cpp b/src/backend/opencl/hist_graphics.cpp index 50a78be927..798363769e 100644 --- a/src/backend/opencl/hist_graphics.cpp +++ b/src/backend/opencl/hist_graphics.cpp @@ -9,11 +9,11 @@ #if defined (WITH_GRAPHICS) -#include #include -#include -#include #include +#include +#include +#include namespace opencl { @@ -27,12 +27,10 @@ void copy_histogram(const Array &data, const forge::Histogram* hist) const cl::Buffer *d_P = data.get(); size_t bytes = hist->verticesSize(); - InteropManager& intrpMngr = InteropManager::getInstance(); - - cl::Buffer **resources = intrpMngr.getBufferResource(hist); + ShrdResVector res = interopManager().getBufferResource(hist); std::vector shared_objects; - shared_objects.push_back(*resources[0]); + shared_objects.push_back(*(res[0].get())); glFinish(); @@ -42,7 +40,7 @@ void copy_histogram(const Array &data, const forge::Histogram* hist) getQueue().enqueueAcquireGLObjects(&shared_objects, NULL, &event); event.wait(); - getQueue().enqueueCopyBuffer(*d_P, *resources[0], 0, 0, bytes, NULL, &event); + getQueue().enqueueCopyBuffer(*d_P, *(res[0].get()), 0, 0, bytes, NULL, &event); getQueue().enqueueReleaseGLObjects(&shared_objects, NULL, &event); event.wait(); diff --git a/src/backend/opencl/image.cpp b/src/backend/opencl/image.cpp index 8f403ea1b4..7acf5a66c9 100644 --- a/src/backend/opencl/image.cpp +++ b/src/backend/opencl/image.cpp @@ -9,11 +9,12 @@ #if defined(WITH_GRAPHICS) -#include #include -#include -#include #include +#include +#include +#include + #include #include @@ -26,14 +27,14 @@ void copy_image(const Array &in, const forge::Image* image) { if (isGLSharingSupported()) { CheckGL("Begin opencl resource copy"); - InteropManager& intrpMngr = InteropManager::getInstance(); - cl::Buffer **resources = intrpMngr.getBufferResource(image); + ShrdResVector res = interopManager().getBufferResource(image); + const cl::Buffer *d_X = in.get(); size_t num_bytes = image->size(); std::vector shared_objects; - shared_objects.push_back(*resources[0]); + shared_objects.push_back(*(res[0].get())); glFinish(); @@ -43,7 +44,7 @@ void copy_image(const Array &in, const forge::Image* image) getQueue().enqueueAcquireGLObjects(&shared_objects, NULL, &event); event.wait(); - getQueue().enqueueCopyBuffer(*d_X, *resources[0], 0, 0, num_bytes, NULL, &event); + getQueue().enqueueCopyBuffer(*d_X, *(res[0].get()), 0, 0, num_bytes, NULL, &event); getQueue().enqueueReleaseGLObjects(&shared_objects, NULL, &event); event.wait(); diff --git a/src/backend/opencl/interopManager.cpp b/src/backend/opencl/interopManager.cpp deleted file mode 100644 index ff2da42efc..0000000000 --- a/src/backend/opencl/interopManager.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#if defined(WITH_GRAPHICS) - -#include - -namespace opencl -{ - -void InteropManager::destroyResources() -{ - typedef std::vector::iterator buffer_t; - int n = getActiveDeviceId(); - for(iter_t iter = interop_maps[n].begin(); iter != interop_maps[n].end(); iter++) { - for(buffer_t bt = (iter->second).begin(); bt != (iter->second).end(); bt++) { - delete *bt; - } - (iter->second).clear(); - } -} - -InteropManager::~InteropManager() -{ - for(int i = 0; i < getDeviceCount(); i++) { - setDevice(i); - destroyResources(); - } -} - -InteropManager& InteropManager::getInstance() -{ - static InteropManager my_instance; - return my_instance; -} - -interop_t& InteropManager::getDeviceMap(int device) -{ - return (device == -1) ? interop_maps[getActiveDeviceId()] : interop_maps[device]; -} - -cl::Buffer** InteropManager::getBufferResource(const forge::Image* image) -{ - void * key = (void*)image; - interop_t& i_map = getDeviceMap(); - iter_t iter = i_map.find(key); - - if (iter == i_map.end()) { - std::vector vec(1); - vec[0] = new cl::BufferGL(getContext(), CL_MEM_WRITE_ONLY, image->pixels(), NULL); - i_map[key] = vec; - } - - return &i_map[key].front(); -} - -cl::Buffer** InteropManager::getBufferResource(const forge::Plot* plot) -{ - void * key = (void*)plot; - interop_t& i_map = getDeviceMap(); - iter_t iter = i_map.find(key); - - if (iter == i_map.end()) { - std::vector vec(1); - vec[0] = new cl::BufferGL(getContext(), CL_MEM_WRITE_ONLY, plot->vertices(), NULL); - i_map[key] = vec; - } - - return &i_map[key].front(); -} - -cl::Buffer** InteropManager::getBufferResource(const forge::Histogram* hist) -{ - void * key = (void*)hist; - interop_t& i_map = getDeviceMap(); - iter_t iter = i_map.find(key); - - if (iter == i_map.end()) { - std::vector vec(1); - vec[0] = new cl::BufferGL(getContext(), CL_MEM_WRITE_ONLY, hist->vertices(), NULL); - i_map[key] = vec; - } - - return &i_map[key].front(); -} - -cl::Buffer** InteropManager::getBufferResource(const forge::Surface* surface) -{ - void * key = (void*)surface; - interop_t& i_map = getDeviceMap(); - iter_t iter = i_map.find(key); - - if (iter == i_map.end()) { - std::vector vec(1); - vec[0] = new cl::BufferGL(getContext(), CL_MEM_WRITE_ONLY, surface->vertices(), NULL); - i_map[key] = vec; - } - - return &i_map[key].front(); -} - -cl::Buffer** InteropManager::getBufferResource(const forge::VectorField* vector_field) -{ - void * key = (void*)vector_field; - interop_t& i_map = getDeviceMap(); - iter_t iter = i_map.find(key); - - if (iter == i_map.end()) { - std::vector vec(2); - vec[0] = new cl::BufferGL(getContext(), CL_MEM_WRITE_ONLY, vector_field->vertices(), NULL); - vec[1] = new cl::BufferGL(getContext(), CL_MEM_WRITE_ONLY, vector_field->directions(), NULL); - i_map[key] = vec; - } - - return &i_map[key].front(); -} - -} - -#endif - diff --git a/src/backend/opencl/interopManager.hpp b/src/backend/opencl/interopManager.hpp deleted file mode 100644 index 6858f51c4b..0000000000 --- a/src/backend/opencl/interopManager.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************* - * Copyright (c) 2014, ArrayFire - * All rights reserved. - * - * This file is distributed under 3-clause BSD license. - * The complete license agreement can be obtained at: - * http://arrayfire.com/licenses/BSD-3-Clause - ********************************************************/ - -#if defined(WITH_GRAPHICS) - -#include -#include - -#include -#include -#include -#include - -namespace opencl -{ - -typedef std::map > interop_t; -typedef interop_t::iterator iter_t; - -class InteropManager -{ - private: - interop_t interop_maps[DeviceManager::MAX_DEVICES]; - - public: - static InteropManager& getInstance(); - ~InteropManager(); - cl::Buffer** getBufferResource(const forge::Image *handle); - cl::Buffer** getBufferResource(const forge::Plot *handle); - cl::Buffer** getBufferResource(const forge::Histogram *handle); - cl::Buffer** getBufferResource(const forge::Surface *handle); - cl::Buffer** getBufferResource(const forge::VectorField *handle); - - protected: - InteropManager() {} - InteropManager(InteropManager const&); - void operator=(InteropManager const&); - interop_t& getDeviceMap(int device = -1); // default will return current device - void destroyResources(); -}; - -} - -#endif diff --git a/src/backend/opencl/jit.cpp b/src/backend/opencl/jit.cpp index 83aab34186..5442eba7f4 100644 --- a/src/backend/opencl/jit.cpp +++ b/src/backend/opencl/jit.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +25,8 @@ namespace opencl { using JIT::Node; +using JIT::Node_ids; +using JIT::Node_map_t; using cl::Buffer; using cl::Program; @@ -35,8 +36,12 @@ using cl::EnqueueArgs; using cl::NDRange; using std::string; using std::stringstream; +using std::vector; -static string getFuncName(std::vector nodes, bool is_linear, bool *is_double) +static string getFuncName(const vector &output_nodes, + const vector &full_nodes, + const vector &full_ids, + bool is_linear, bool *is_double) { stringstream hashName; stringstream funcName; @@ -47,13 +52,12 @@ static string getFuncName(std::vector nodes, bool is_linear, bool *is_do funcName << "G_"; } - int id = 0; - for (auto node : nodes) { - funcName << "["; - id = node->setId(id); - funcName << node->getNameStr(); - node->genKerName(funcName); - funcName << "]"; + for (auto node : output_nodes) { + funcName << node->getNameStr() << "_"; + } + + for (int i = 0; i < (int)full_nodes.size(); i++) { + full_nodes[i]->genKerName(funcName, full_ids[i]); } string nameStr = funcName.str(); @@ -66,7 +70,11 @@ static string getFuncName(std::vector nodes, bool is_linear, bool *is_do return hashName.str(); } -static string getKernelString(string funcName, std::vector nodes, bool is_linear) +static string getKernelString(const string funcName, + const vector &full_nodes, + const vector &full_ids, + const vector &output_ids, + bool is_linear) { // Common OpenCL code @@ -119,16 +127,23 @@ static string getKernelString(string funcName, std::vector nodes, bool i stringstream offsetsStream; stringstream opsStream; - int count = 0; + for (int i = 0; i < (int)full_nodes.size(); i++) { + const auto &node = full_nodes[i]; + const auto &ids_curr = full_ids[i]; + // Generate input parameters, only needs current id + node->genParams(inParamStream, ids_curr.id, is_linear); + // Generate input offsets, only needs current id + node->genOffsets(offsetsStream, ids_curr.id, is_linear); + // Generate the core function body, needs children ids as well + node->genFuncs(opsStream, ids_curr); + } - for (auto node : nodes) { - int id = node->getId(); - node->genParams(inParamStream, is_linear); - outParamStream << "__global " << node->getTypeStr() << " *out" << id << ", \n"; + for (int i = 0; i < (int)output_ids.size(); i++) { + int id = output_ids[i]; + // Generate output parameters + outParamStream << "__global " << full_nodes[id]->getTypeStr() << " *out" << id << ", \n"; + // Generate code to write the output outWriteStream << "out" << id << "[idx] = " << "val" << id << ";\n"; - node->genOffsets(offsetsStream, is_linear); - node->genFuncs(opsStream); - opsStream << "//" << ++count << std::endl << std::endl; } // Put various blocks into a single stream @@ -154,35 +169,37 @@ static string getKernelString(string funcName, std::vector nodes, bool i return kerStream.str(); } -static Kernel getKernel(std::vector nodes, bool is_linear) +static Kernel getKernel(const vector &output_nodes, + const vector &output_ids, + const vector &full_nodes, + const vector &full_ids, + const bool is_linear) { - bool is_dbl = false; - string funcName = getFuncName(nodes, is_linear, &is_dbl); + string funcName = getFuncName(output_nodes, full_nodes, full_ids, is_linear, &is_dbl); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(funcName); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, funcName); - if (idx == kernelCaches[device].end()) { - string jit_ker = getKernelString(funcName, nodes, is_linear); + if (entry.prog==0 && entry.ker==0) { + string jit_ker = getKernelString(funcName, full_nodes, full_ids, output_ids, is_linear); const char *ker_strs[] = {jit_cl, jit_ker.c_str()}; const int ker_lens[] = {jit_cl_len, (int)jit_ker.size()}; + cl::Program prog; buildProgram(prog, 2, ker_strs, ker_lens, is_dbl ? string(" -D USE_DOUBLE") : string("")); + entry.prog = new cl::Program(prog); entry.ker = new Kernel(*entry.prog, funcName.c_str()); - kernelCaches[device][funcName] = entry; - } else { - entry = idx->second; + addKernelToCache(device, funcName, entry); } return *entry.ker; } -void evalNodes(std::vector &outputs, std::vector nodes) +void evalNodes(vector &outputs, vector output_nodes) { if (outputs.size() == 0) return; @@ -190,13 +207,25 @@ void evalNodes(std::vector &outputs, std::vector nodes) //FIXME: Add assert to check if all outputs are same size? KParam out_info = outputs[0].info; - // Verify if all ASTs hold Linear Arrays + Node_map_t nodes; + vector output_ids; + for (auto &node : output_nodes) { + node->getNodesMap(nodes); + output_ids.push_back(nodes[node].id); + } + + vector full_nodes(nodes.size()); + vector full_ids(nodes.size()); bool is_linear = true; - for (auto node : nodes) { - is_linear &= node->isLinear(out_info.dims); + for (auto &map_entry : nodes) { + full_nodes[map_entry.second.id] = map_entry.first; + full_ids[map_entry.second.id] = map_entry.second; + is_linear &= map_entry.first->isLinear(out_info.dims); } - Kernel ker = getKernel(nodes, is_linear); + Kernel ker = getKernel(output_nodes, output_ids, + full_nodes, full_ids, + is_linear); uint local_0 = 1; uint local_1 = 1; @@ -236,36 +265,32 @@ void evalNodes(std::vector &outputs, std::vector nodes) NDRange local(local_0, local_1); NDRange global(global_0, global_1); - int args = 0; - for (auto node : nodes) { - args = node->setArgs(ker, args, is_linear); + int nargs = 0; + for (const auto &node : full_nodes) { + nargs = node->setArgs(ker, nargs, is_linear); } // Set output parameters for (auto output : outputs) { - ker.setArg(args, *(output.data)); - ++args; + ker.setArg(nargs, *(output.data)); + ++nargs; } // Set dimensions // All outputs are asserted to be of same size // Just use the size from the first output - ker.setArg(args + 0, out_info); - ker.setArg(args + 1, groups_0); - ker.setArg(args + 2, groups_1); - ker.setArg(args + 3, num_odims); + ker.setArg(nargs + 0, out_info); + ker.setArg(nargs + 1, groups_0); + ker.setArg(nargs + 2, groups_1); + ker.setArg(nargs + 3, num_odims); getQueue().enqueueNDRangeKernel(ker, cl::NullRange, global, local); - - for (auto node : nodes) { - node->resetFlags(); - } } void evalNodes(Param &out, Node *node) { - std::vector outputs{out}; - std::vector nodes{node}; + vector outputs{out}; + vector nodes{node}; return evalNodes(outputs, nodes); } diff --git a/src/backend/opencl/kernel/approx.hpp b/src/backend/opencl/kernel/approx.hpp index 85d3ba579c..21a0274384 100644 --- a/src/backend/opencl/kernel/approx.hpp +++ b/src/backend/opencl/kernel/approx.hpp @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include #include @@ -34,148 +32,135 @@ using std::string; namespace opencl { - namespace kernel - { - static const int TX = 16; - static const int TY = 16; - - static const int THREADS = 256; - - /////////////////////////////////////////////////////////////////////////// - // Wrapper functions - /////////////////////////////////////////////////////////////////////////// - template - void approx1(Param out, const Param in, const Param xpos, const float offGrid, - af_interp_type method) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map approxProgs; - static std::map approxKernels; - - int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - ToNumStr toNumStr; - std::ostringstream options; - options << " -D Ty=" << dtype_traits::getName() - << " -D Tp=" << dtype_traits::getName() - << " -D InterpInTy=" << dtype_traits::getName() - << " -D InterpValTy=" << dtype_traits::getName() - << " -D InterpPosTy=" << dtype_traits::getName() - << " -D ZERO=" << toNumStr(scalar(0)); - - if((af_dtype) dtype_traits::af_type == c32 || - (af_dtype) dtype_traits::af_type == c64) { - options << " -D IS_CPLX=1"; - } else { - options << " -D IS_CPLX=0"; - } - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - options << " -D INTERP_ORDER=" << order; - addInterpEnumOptions(options); - - Program prog; - const char *ker_strs[] = {interp_cl, approx1_cl}; - const int ker_lens[] = {interp_cl_len, approx1_cl_len}; - buildProgram(prog, 2, ker_strs, ker_lens, options.str()); - approxProgs[device] = new Program(prog); - - approxKernels[device] = new Kernel(*approxProgs[device], "approx1_kernel"); - }); - - - auto approx1Op = KernelFunctor - (*approxKernels[device]); - - NDRange local(THREADS, 1, 1); - dim_t blocksPerMat = divup(out.info.dims[0], local[0]); - NDRange global(blocksPerMat * local[0] * out.info.dims[1], - out.info.dims[2] * out.info.dims[3] * local[0], - 1); - - // Passing bools to opencl kernels is not allowed - bool batch = !(xpos.info.dims[1] == 1 && xpos.info.dims[2] == 1 && - xpos.info.dims[3] == 1); - - approx1Op(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, - *xpos.data, xpos.info, scalar(offGrid), - blocksPerMat, (int)batch, (int)method); - - CL_DEBUG_FINISH(getQueue()); - } - - template - void approx2(Param out, const Param in, const Param xpos, const Param ypos, - const float offGrid, af_interp_type method) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map approxProgs; - static std::map approxKernels; - - int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - ToNumStr toNumStr; - std::ostringstream options; - options << " -D Ty=" << dtype_traits::getName() - << " -D Tp=" << dtype_traits::getName() - << " -D InterpInTy=" << dtype_traits::getName() - << " -D InterpValTy=" << dtype_traits::getName() - << " -D InterpPosTy=" << dtype_traits::getName() - << " -D ZERO=" << toNumStr(scalar(0)); - - if((af_dtype) dtype_traits::af_type == c32 || - (af_dtype) dtype_traits::af_type == c64) { - options << " -D IS_CPLX=1"; - } else { - options << " -D IS_CPLX=0"; - } - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - options << " -D INTERP_ORDER=" << order; - addInterpEnumOptions(options); - - Program prog; - const char *ker_strs[] = {interp_cl, approx2_cl}; - const int ker_lens[] = {interp_cl_len, approx2_cl_len}; - buildProgram(prog, 2, ker_strs, ker_lens, options.str()); - approxProgs[device] = new Program(prog); - - approxKernels[device] = new Kernel(*approxProgs[device], "approx2_kernel"); - }); - - auto approx2Op = KernelFunctor - (*approxKernels[device]); - - NDRange local(TX, TY, 1); - dim_t blocksPerMatX = divup(out.info.dims[0], local[0]); - dim_t blocksPerMatY = divup(out.info.dims[1], local[1]); - NDRange global(blocksPerMatX * local[0] * out.info.dims[2], - blocksPerMatY * local[1] * out.info.dims[3], - 1); - - // Passing bools to opencl kernels is not allowed - bool batch = !(xpos.info.dims[2] == 1 && xpos.info.dims[3] == 1); - - approx2Op(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, - *in.data, in.info, - *xpos.data, xpos.info, - *ypos.data, ypos.info, - scalar(offGrid), blocksPerMatX, blocksPerMatY, (int)batch, (int)method); - CL_DEBUG_FINISH(getQueue()); - } +namespace kernel +{ +static const int TX = 16; +static const int TY = 16; + +static const int THREADS = 256; + +template +std::string generateOptionsString() +{ + ToNumStr toNumStr; + + std::ostringstream options; + options << " -D Ty=" << dtype_traits::getName() + << " -D Tp=" << dtype_traits::getName() + << " -D InterpInTy=" << dtype_traits::getName() + << " -D InterpValTy=" << dtype_traits::getName() + << " -D InterpPosTy=" << dtype_traits::getName() + << " -D ZERO=" << toNumStr(scalar(0)); + + if((af_dtype) dtype_traits::af_type == c32 || + (af_dtype) dtype_traits::af_type == c64) { + options << " -D IS_CPLX=1"; + } else { + options << " -D IS_CPLX=0"; } + if (std::is_same::value || + std::is_same::value) { + options << " -D USE_DOUBLE"; + } + + options << " -D INTERP_ORDER=" << order; + addInterpEnumOptions(options); + + return options.str(); +} + +/////////////////////////////////////////////////////////////////////////// +// Wrapper functions +/////////////////////////////////////////////////////////////////////////// +template +void approx1(Param out, const Param in, const Param xpos, const float offGrid, + af_interp_type method) +{ + std::string refName = std::string("approx1_kernel_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(order); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::string options = generateOptionsString(); + + const char *ker_strs[] = {interp_cl, approx1_cl}; + const int ker_lens[] = {interp_cl_len, approx1_cl_len}; + Program prog; + buildProgram(prog, 2, ker_strs, ker_lens, options); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "approx1_kernel"); + + addKernelToCache(device, refName, entry); + } + + auto approx1Op = KernelFunctor< Buffer, const KParam, const Buffer, const KParam, + const Buffer, const KParam, const Ty, + const int, const int, const int >(*entry.ker); + + NDRange local(THREADS, 1, 1); + dim_t blocksPerMat = divup(out.info.dims[0], local[0]); + NDRange global(blocksPerMat * local[0] * out.info.dims[1], + out.info.dims[2] * out.info.dims[3] * local[0], 1); + + // Passing bools to opencl kernels is not allowed + bool batch = !(xpos.info.dims[1] == 1 && xpos.info.dims[2] == 1 && xpos.info.dims[3] == 1); + + approx1Op(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, *in.data, in.info, + *xpos.data, xpos.info, scalar(offGrid), + blocksPerMat, (int)batch, (int)method); + + CL_DEBUG_FINISH(getQueue()); +} + +template +void approx2(Param out, const Param in, const Param xpos, const Param ypos, + const float offGrid, af_interp_type method) +{ + std::string refName = std::string("approx2_kernel_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(order); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::string options = generateOptionsString(); + + const char *ker_strs[] = {interp_cl, approx2_cl}; + const int ker_lens[] = {interp_cl_len, approx2_cl_len}; + Program prog; + buildProgram(prog, 2, ker_strs, ker_lens, options); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "approx2_kernel"); + + addKernelToCache(device, refName, entry); + } + + auto approx2Op = KernelFunctor< Buffer, const KParam, const Buffer, const KParam, + const Buffer, const KParam, const Buffer, const KParam, + const Ty, const int, const int, const int, const int >(*entry.ker); + + NDRange local(TX, TY, 1); + dim_t blocksPerMatX = divup(out.info.dims[0], local[0]); + dim_t blocksPerMatY = divup(out.info.dims[1], local[1]); + NDRange global(blocksPerMatX * local[0] * out.info.dims[2], + blocksPerMatY * local[1] * out.info.dims[3], 1); + + // Passing bools to opencl kernels is not allowed + bool batch = !(xpos.info.dims[2] == 1 && xpos.info.dims[3] == 1); + + approx2Op(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, *in.data, in.info, + *xpos.data, xpos.info, *ypos.data, ypos.info, + scalar(offGrid), blocksPerMatX, blocksPerMatY, (int)batch, (int)method); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/assign.hpp b/src/backend/opencl/kernel/assign.hpp index 079768c4a9..580e7ab582 100644 --- a/src/backend/opencl/kernel/assign.hpp +++ b/src/backend/opencl/kernel/assign.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,10 +27,8 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 32; static const int THREADS_Y = 8; @@ -44,46 +41,42 @@ typedef struct { template void assign(Param out, const Param in, const AssignKernelParam_t& p, Buffer *bPtr[4]) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map agnProgs; - static std::map agnKernels; + std::string refName = std::string("assignKernel_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - - if (std::is_same::value || - std::is_same::value) { + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, assign_cl, assign_cl_len, options.str()); - agnProgs[device] = new Program(prog); - agnKernels[device] = new Kernel(*agnProgs[device], "assignKernel"); - }); + const char* ker_strs[] = {assign_cl}; + const int ker_lens[] = {assign_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "assignKernel"); + + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); int blk_x = divup(in.info.dims[0], THREADS_X); int blk_y = divup(in.info.dims[1], THREADS_Y); - NDRange global(blk_x * in.info.dims[2] * THREADS_X, - blk_y * in.info.dims[3] * THREADS_Y); + NDRange global(blk_x * in.info.dims[2] * THREADS_X, blk_y * in.info.dims[3] * THREADS_Y); - auto assignOp = KernelFunctor(*agnKernels[device]); + auto assignOp = KernelFunctor< Buffer, KParam, Buffer, KParam, AssignKernelParam_t, + Buffer, Buffer, Buffer, Buffer, int, int>(*entry.ker); assignOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, p, - *bPtr[0], *bPtr[1], *bPtr[2], *bPtr[3], blk_x, blk_y); + *out.data, out.info, *in.data, in.info, p, + *bPtr[0], *bPtr[1], *bPtr[2], *bPtr[3], blk_x, blk_y); CL_DEBUG_FINISH(getQueue()); } - } - } - diff --git a/src/backend/opencl/kernel/bilateral.hpp b/src/backend/opencl/kernel/bilateral.hpp index 9aafae09c3..1d93de83ba 100644 --- a/src/backend/opencl/kernel/bilateral.hpp +++ b/src/backend/opencl/kernel/bilateral.hpp @@ -12,9 +12,8 @@ #include #include #include -#include -#include #include +#include #include #include #include @@ -31,57 +30,53 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; template void bilateral(Param out, const Param in, float s_sigma, float c_sigma) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map bilProgs; - static std::map bilKernels; + std::string refName = std::string("bilateral_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(isColor); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - bool use_native_exp = (getActivePlatform() != AFCL_PLATFORM_POCL - && getActivePlatform() != AFCL_PLATFORM_APPLE - && getActivePlatform() != AFCL_PLATFORM_AMD); - std::ostringstream options; - options << " -D inType=" << dtype_traits::getName() - << " -D outType=" << dtype_traits::getName(); - if (std::is_same::value || + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + bool use_native_exp = (getActivePlatform() != AFCL_PLATFORM_POCL + && getActivePlatform() != AFCL_PLATFORM_APPLE); + std::ostringstream options; + options << " -D inType=" << dtype_traits::getName() + << " -D outType=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) { - options << " -D USE_DOUBLE"; - } - options << " -D USE_NATIVE_EXP=" << (int)use_native_exp; - - Program prog; - buildProgram(prog, bilateral_cl, bilateral_cl_len, options.str()); - bilProgs[device] = new Program(prog); - - bilKernels[device] = new Kernel(*bilProgs[device], "bilateral"); - }); + options << " -D USE_DOUBLE"; + } + options << " -D USE_NATIVE_EXP=" << (int)use_native_exp; + + const char* ker_strs[] = {bilateral_cl}; + const int ker_lens[] = {bilateral_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "bilateral"); + + addKernelToCache(device, refName, entry); + } - auto bilateralOp = KernelFunctor(*bilKernels[device]); + auto bilateralOp = KernelFunctor< Buffer, KParam, Buffer, KParam, LocalSpaceArg, LocalSpaceArg, + float, float, int, int, int >(*entry.ker); NDRange local(THREADS_X, THREADS_Y); int blk_x = divup(in.info.dims[0], THREADS_X); int blk_y = divup(in.info.dims[1], THREADS_Y); - NDRange global(blk_x*in.info.dims[2]*THREADS_X, - blk_y*in.info.dims[3]*THREADS_Y); + NDRange global(blk_x*in.info.dims[2]*THREADS_X, blk_y*in.info.dims[3]*THREADS_Y); // calculate local memory size int radius = (int)std::max(s_sigma * 1.5f, 1.f); @@ -101,7 +96,5 @@ void bilateral(Param out, const Param in, float s_sigma, float c_sigma) CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/canny.hpp b/src/backend/opencl/kernel/canny.hpp index 1fe52425b1..dc01057949 100644 --- a/src/backend/opencl/kernel/canny.hpp +++ b/src/backend/opencl/kernel/canny.hpp @@ -37,15 +37,12 @@ static const int THREADS_Y = 16; template void nonMaxSuppression(Param output, const Param magnitude, const Param dx, const Param dy) { - std::string ref_name = - std::string("non_max_suppression_") + - std::string(dtype_traits::getName()); + std::string refName = std::string("non_max_suppression_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, refName); - if (idx == kernelCaches[device].find(ref_name)) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName() << " -D SHRD_MEM_HEIGHT=" << (THREADS_X+2) @@ -56,13 +53,11 @@ void nonMaxSuppression(Param output, const Param magnitude, const Param dx, cons const char *ker_strs[] = {nonmax_suppression_cl}; const int ker_lens[] = {nonmax_suppression_cl_len}; - Program prog; buildProgram(prog, 1, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "nonMaxSuppressionKernel"); - } else { - entry = idx->second; + addKernelToCache(device, refName, entry); } auto nonMaxOp = KernelFunctor void initEdgeOut(Param output, const Param strong, const Param weak) { - std::string ref_name = - std::string("init_edge_out_") + - std::string(dtype_traits::getName()); + std::string refName = std::string("init_edge_out_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, refName); - if (idx == kernelCaches[device].find(ref_name)) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName() << " -D INIT_EDGE_OUT"; @@ -108,13 +100,11 @@ void initEdgeOut(Param output, const Param strong, const Param weak) const char *ker_strs[] = {trace_edge_cl}; const int ker_lens[] = {trace_edge_cl_len}; - Program prog; buildProgram(prog, 1, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "initEdgeOutKernel"); - } else { - entry = idx->second; + addKernelToCache(device, refName, entry); } auto initOp = KernelFunctor void suppressLeftOver(Param output) { - std::string ref_name = - std::string("suppress_left_over_") + - std::string(dtype_traits::getName()); + std::string refName = std::string("suppress_left_over_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, refName); - if (idx == kernelCaches[device].find(ref_name)) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName() << " -D SUPPRESS_LEFT_OVER"; @@ -163,8 +150,7 @@ void suppressLeftOver(Param output) buildProgram(prog, 1, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "suppressLeftOverKernel"); - } else { - entry = idx->second; + addKernelToCache(device, refName, entry); } auto finalOp = KernelFunctor(*entry.ker); @@ -187,15 +173,12 @@ void suppressLeftOver(Param output) template void edgeTrackingHysteresis(Param output, const Param strong, const Param weak) { - std::string ref_name = - std::string("edge_track_") + - std::string(dtype_traits::getName()); + std::string refName = std::string("edge_track_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, refName); - if (idx == kernelCaches[device].find(ref_name)) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName() << " -D SHRD_MEM_HEIGHT=" << (THREADS_X+2) @@ -212,8 +195,7 @@ void edgeTrackingHysteresis(Param output, const Param strong, const Param weak) buildProgram(prog, 1, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "edgeTrackKernel"); - } else { - entry = idx->second; + addKernelToCache(device, refName, entry); } NDRange threads(kernel::THREADS_X, kernel::THREADS_Y); diff --git a/src/backend/opencl/kernel/convolve/conv2_impl.hpp b/src/backend/opencl/kernel/convolve/conv2_impl.hpp index 04fae02d72..ef8df00d3b 100644 --- a/src/backend/opencl/kernel/convolve/conv2_impl.hpp +++ b/src/backend/opencl/kernel/convolve/conv2_impl.hpp @@ -35,10 +35,10 @@ void conv2Helper(const conv_kparam_t& param, Param out, const Param signal, cons std::to_string(f1); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { size_t LOC_SIZE = (THREADS_X+2*(f0-1))*(THREADS_Y+2*(f1-1)); std::ostringstream options; @@ -58,9 +58,7 @@ void conv2Helper(const conv_kparam_t& param, Param out, const Param signal, cons entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "convolve"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } auto convOp = cl::KernelFunctor -#include -#include #include #include #include @@ -23,6 +21,7 @@ #include #include #include +#include using cl::Buffer; using cl::Program; @@ -33,10 +32,8 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS = 256; static const int THREADS_X = 16; @@ -97,39 +94,37 @@ void prepareKernelArgs(conv_kparam_t& param, dim_t *oDims, template void convNHelper(const conv_kparam_t& param, Param& out, const Param& signal, const Param& filter) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map convProgs; - static std::map convKernels; + std::string ref_name = std::string("convolveND_") + + std::string(dtype_traits::getName()) + std::string(dtype_traits::getName()) + + std::to_string(bDim) + std::to_string(expand); int device = getActiveDeviceId(); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D accType="<< dtype_traits::getName() - << " -D BASE_DIM="<< bDim - << " -D EXPAND=" << expand; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, convolve_cl, convolve_cl_len, options.str()); - convProgs[device] = new Program(prog); - convKernels[device] = new Kernel(*convProgs[device], "convolve"); - }); - - auto convOp = cl::KernelFunctor(*convKernels[device]); + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D accType=" << dtype_traits::getName() + << " -D BASE_DIM=" << bDim + << " -D EXPAND=" << expand; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + Program prog; + buildProgram(prog, convolve_cl, convolve_cl_len, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "convolve"); + + addKernelToCache(device, ref_name, entry); + } + + auto convOp = cl::KernelFunctor(*entry.ker); convOp(EnqueueArgs(getQueue(), param.global, param.local), - *out.data, out.info, *signal.data, signal.info, cl::Local(param.loc_size), - *param.impulse, filter.info, param.nBBS0, param.nBBS1, - param.o[0], param.o[1], param.o[2], param.s[0], param.s[1], param.s[2]); + *out.data, out.info, *signal.data, signal.info, cl::Local(param.loc_size), + *param.impulse, filter.info, param.nBBS0, param.nBBS1, + param.o[0], param.o[1], param.o[2], param.s[0], param.s[1], param.s[2]); } template @@ -140,7 +135,5 @@ void conv2(conv_kparam_t& p, Param& out, const Param& sig, const Param& filt); template void conv3(conv_kparam_t& p, Param& out, const Param& sig, const Param& filt); - } - } diff --git a/src/backend/opencl/kernel/convolve_separable.cpp b/src/backend/opencl/kernel/convolve_separable.cpp index b212cbe452..aafc4253ef 100644 --- a/src/backend/opencl/kernel/convolve_separable.cpp +++ b/src/backend/opencl/kernel/convolve_separable.cpp @@ -54,10 +54,10 @@ void convSep(Param out, const Param signal, const Param filter) std::to_string(fLen); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { const size_t C0_SIZE = (THREADS_X+2*(fLen-1))* THREADS_Y; const size_t C1_SIZE = (THREADS_Y+2*(fLen-1))* THREADS_X; @@ -79,9 +79,8 @@ void convSep(Param out, const Param signal, const Param filter) entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "convolve"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } auto convOp = KernelFunctorsecond; + + addKernelToCache(device, ref_name, entry); } auto cscmm_kernel = *entry.ker; diff --git a/src/backend/opencl/kernel/cscmv.hpp b/src/backend/opencl/kernel/cscmv.hpp index 6aabe2cfd3..2a2b676c44 100644 --- a/src/backend/opencl/kernel/cscmv.hpp +++ b/src/backend/opencl/kernel/cscmv.hpp @@ -65,10 +65,10 @@ namespace opencl std::to_string(threads); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); @@ -96,8 +96,8 @@ namespace opencl buildProgram(prog, 1, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "cscmv_block"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } auto cscmv_kernel = *entry.ker; diff --git a/src/backend/opencl/kernel/csrmm.hpp b/src/backend/opencl/kernel/csrmm.hpp index 0afab2973c..40e2bd30e6 100644 --- a/src/backend/opencl/kernel/csrmm.hpp +++ b/src/backend/opencl/kernel/csrmm.hpp @@ -61,10 +61,10 @@ namespace opencl std::to_string(use_greedy); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); @@ -94,8 +94,8 @@ namespace opencl entry.ker[0] = Kernel(*entry.prog, "csrmm_nt"); // FIXME: Change this after adding another kernel entry.ker[1] = Kernel(*entry.prog, "csrmm_nt"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } auto csrmm_nt_kernel = entry.ker[0]; diff --git a/src/backend/opencl/kernel/csrmv.hpp b/src/backend/opencl/kernel/csrmv.hpp index faffb828e8..2ebb0b964e 100644 --- a/src/backend/opencl/kernel/csrmv.hpp +++ b/src/backend/opencl/kernel/csrmv.hpp @@ -67,10 +67,10 @@ namespace opencl std::to_string(threads); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); @@ -99,8 +99,8 @@ namespace opencl entry.ker = new Kernel[2]; entry.ker[0] = Kernel(*entry.prog, "csrmv_thread"); entry.ker[1] = Kernel(*entry.prog, "csrmv_block"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } int count = 0; diff --git a/src/backend/opencl/kernel/diagonal.hpp b/src/backend/opencl/kernel/diagonal.hpp index bf6acce9af..70659e580a 100644 --- a/src/backend/opencl/kernel/diagonal.hpp +++ b/src/backend/opencl/kernel/diagonal.hpp @@ -14,8 +14,7 @@ #include #include #include -#include -#include +#include #include #include "config.hpp" @@ -30,89 +29,87 @@ using af::scalar_to_option; namespace opencl { - namespace kernel { - - template - static void diagCreate(Param out, Param in, int num) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map diagCreateProgs; - static std::map diagCreateKernels; - - int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, diag_create_cl, diag_create_cl_len, options.str()); - diagCreateProgs[device] = new Program(prog); - diagCreateKernels[device] = new Kernel(*diagCreateProgs[device], - "diagCreateKernel"); - }); - - NDRange local(32, 8); - int groups_x = divup(out.info.dims[0], local[0]); - int groups_y = divup(out.info.dims[1], local[1]); - NDRange global(groups_x * local[0] * out.info.dims[2], - groups_y * local[1]); - - auto diagCreateOp = KernelFunctor (*diagCreateKernels[device]); - - diagCreateOp(EnqueueArgs(getQueue(), global, local), - *(out.data), out.info, *(in.data), in.info, num, groups_x); - CL_DEBUG_FINISH(getQueue()); +template +std::string generateOptionsString() +{ + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")"; + if (std::is_same::value || std::is_same::value) { + options << " -D USE_DOUBLE"; } + return options.str(); +} + +template +static void diagCreate(Param out, Param in, int num) +{ + std::string refName = std::string("diagCreateKernel_") + std::string(dtype_traits::getName()); - template - static void diagExtract(Param out, Param in, int num) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map diagExtractProgs; - static std::map diagExtractKernels; - - int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, diag_extract_cl, diag_extract_cl_len, options.str()); - diagExtractProgs[device] = new Program(prog); - diagExtractKernels[device] = new Kernel(*diagExtractProgs[device], - "diagExtractKernel"); - }); - - NDRange local(256, 1); - int groups_x = divup(out.info.dims[0], local[0]); - int groups_z = out.info.dims[2]; - NDRange global(groups_x * local[0], - groups_z * local[1] * out.info.dims[3]); - - auto diagExtractOp = KernelFunctor (*diagExtractKernels[device]); - - diagExtractOp(EnqueueArgs(getQueue(), global, local), - *(out.data), out.info, *(in.data), in.info, num, groups_z); - CL_DEBUG_FINISH(getQueue()); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + if (entry.prog==0 && entry.ker==0) { + std::string options = generateOptionsString(); + const char* ker_strs[] = {diag_create_cl}; + const int ker_lens[] = {diag_create_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "diagCreateKernel"); + + addKernelToCache(device, refName, entry); } + NDRange local(32, 8); + int groups_x = divup(out.info.dims[0], local[0]); + int groups_y = divup(out.info.dims[1], local[1]); + NDRange global(groups_x * local[0] * out.info.dims[2], groups_y * local[1]); + + auto diagCreateOp = KernelFunctor< Buffer, const KParam, Buffer, const KParam, + int, int > (*entry.ker); + + diagCreateOp(EnqueueArgs(getQueue(), global, local), + *(out.data), out.info, *(in.data), in.info, num, groups_x); + + CL_DEBUG_FINISH(getQueue()); } +template +static void diagExtract(Param out, Param in, int num) +{ + std::string refName = std::string("diagExtractKernel_") + std::string(dtype_traits::getName()); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::string options = generateOptionsString(); + const char* ker_strs[] = {diag_extract_cl}; + const int ker_lens[] = {diag_extract_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "diagExtractKernel"); + + addKernelToCache(device, refName, entry); + } + + NDRange local(256, 1); + int groups_x = divup(out.info.dims[0], local[0]); + int groups_z = out.info.dims[2]; + NDRange global(groups_x * local[0], groups_z * local[1] * out.info.dims[3]); + + auto diagExtractOp = KernelFunctor< Buffer, const KParam, Buffer, const KParam, + int, int > (*entry.ker); + + diagExtractOp(EnqueueArgs(getQueue(), global, local), + *(out.data), out.info, *(in.data), in.info, num, groups_z); + + CL_DEBUG_FINISH(getQueue()); + +} +} } diff --git a/src/backend/opencl/kernel/diff.hpp b/src/backend/opencl/kernel/diff.hpp index 1445829c19..438ad13354 100644 --- a/src/backend/opencl/kernel/diff.hpp +++ b/src/backend/opencl/kernel/diff.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,58 +27,61 @@ using std::string; namespace opencl { - namespace kernel - { - static const int TX = 16; - static const int TY = 16; +namespace kernel +{ +static const int TX = 16; +static const int TY = 16; - template - void diff(Param out, const Param in, const unsigned indims) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map diffProgs; - static std::map diffKernels; +template +void diff(Param out, const Param in, const unsigned indims) +{ + std::string refName = std::string("diff_kernel_") + + std::string(dtype_traits::getName()) + + std::to_string(dim) + + std::to_string(isDiff2); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D DIM=" << dim - << " -D isDiff2=" << isDiff2; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, diff_cl, diff_cl_len, options.str()); - diffProgs[device] = new Program(prog); - diffKernels[device] = new Kernel(*diffProgs[device], "diff_kernel"); - }); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D DIM=" << dim + << " -D isDiff2=" << isDiff2; + if (std::is_same::value || + std::is_same::value) { + options << " -D USE_DOUBLE"; + } - auto diffOp = KernelFunctor - (*diffKernels[device]); + const char* ker_strs[] = {diff_cl}; + const int ker_lens[] = {diff_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "diff_kernel"); - NDRange local(TX, TY, 1); - if(dim == 0 && indims == 1) { - local = NDRange(TX * TY, 1, 1); - } + addKernelToCache(device, refName, entry); + } - int blocksPerMatX = divup(out.info.dims[0], local[0]); - int blocksPerMatY = divup(out.info.dims[1], local[1]); - NDRange global(local[0] * blocksPerMatX * out.info.dims[2], - local[1] * blocksPerMatY * out.info.dims[3], - 1); + auto diffOp = KernelFunctor< Buffer, const Buffer, const KParam, const KParam, + const int, const int, const int> (*entry.ker); - const int oElem = out.info.dims[0] * out.info.dims[1] - * out.info.dims[2] * out.info.dims[3]; + NDRange local(TX, TY, 1); + if(dim == 0 && indims == 1) { + local = NDRange(TX * TY, 1, 1); + } - diffOp(EnqueueArgs(getQueue(), global, local), - *out.data, *in.data, out.info, in.info, - oElem, blocksPerMatX, blocksPerMatY); + int blocksPerMatX = divup(out.info.dims[0], local[0]); + int blocksPerMatY = divup(out.info.dims[1], local[1]); + NDRange global(local[0] * blocksPerMatX * out.info.dims[2], + local[1] * blocksPerMatY * out.info.dims[3], 1); - CL_DEBUG_FINISH(getQueue()); - } - } + const int oElem = out.info.dims[0] * out.info.dims[1] * out.info.dims[2] * out.info.dims[3]; + + diffOp(EnqueueArgs(getQueue(), global, local), + *out.data, *in.data, out.info, in.info, oElem, blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/exampleFunction.hpp b/src/backend/opencl/kernel/exampleFunction.hpp index 2af050e33b..acad35b831 100644 --- a/src/backend/opencl/kernel/exampleFunction.hpp +++ b/src/backend/opencl/kernel/exampleFunction.hpp @@ -18,8 +18,12 @@ // Following c++ standard library headers are needed to maintain // OpenCL cl::Kernel & cl::Program objects #include -#include -#include + +#include // Has the definitions of functions such as the following + // used in caching and fetching kernels. + // * kernelCache - used to fetch existing kernel from cache + // if any + // * addKernelToCache - push new kernels into cache #include // common utility header for CUDA & OpenCL backends // has the divup macro @@ -43,54 +47,51 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; template void exampleFunc(Param c, const Param a, const Param b, const af_someenum_t p) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map egProgs; - static std::map egKernels; + std::string refName = + std::string("example_") + //_ + std::string(dtype_traits::getName()); + // std::string("encode template parameters one after one"); + // If you have numericals, you can use std::to_string to convert + // them into std::strings int device = getActiveDeviceId(); - - // std::call_once is used to ensure OpenCL kernels - // are compiled only once for any given device and combination - // of template parameters to this kernel wrapper function 'exampleFunc' - std::call_once( compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - // You can pass any template parameters as compile options - // to kernel the compilation step. This is equivalent of - // having templated kernels in CUDA - - // The following option is passed to kernel compilation - // if template parameter T is double or complex double - // to enable FP64 extension - if (std::is_same::value || + kc_entry_t entry = kernelCache(device, refName); + + // Make sure OpenCL kernel isn't already available before + // compiling for given device and combination of template + // parameters to this kernel wrapper function 'exampleFunc' + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + // You can pass any template parameters as compile options + // to kernel the compilation step. This is equivalent of + // having templated kernels in CUDA + + // The following option is passed to kernel compilation + // if template parameter T is double or complex double + // to enable FP64 extension + if (std::is_same::value || std::is_same::value) { - options << " -D USE_DOUBLE"; - } + options << " -D USE_DOUBLE"; + } - Program prog; - // below helper function 'buildProgram' uses the option string - // we just created and compiles the kernel string - // 'example_cl' which was created by our opencl kernel code obfuscation - // stage - buildProgram(prog, example_cl, example_cl_len, options.str()); + const char *ker_strs[] = {example_cl}; + const int ker_lens[] = {example_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "example"); - // create a cl::Program object on heap - egProgs[device] = new Program(prog); - - // create a cl::Kernel object on heap - egKernels[device] = new Kernel(*egProgs[device], "example"); - }); + addKernelToCache(device, refName, entry); + } // configure work group parameters NDRange local(THREADS_X, THREADS_Y); @@ -104,18 +105,16 @@ void exampleFunc(Param c, const Param a, const Param b, const af_someenum_t p) // create a kernel functor from the cl::Kernel object // corresponding to the device on which current execution // is happending. - auto exampleFuncOp = KernelFunctor(*egKernels[device]); + auto exampleFuncOp = KernelFunctor< Buffer, KParam, Buffer, KParam, + Buffer, KParam, int>(*entry.ker); // launch the kernel exampleFuncOp(EnqueueArgs(getQueue(), global, local), - *c.data, c.info, *a.data, a.info, *b.data, b.info, (int)p); + *c.data, c.info, *a.data, a.info, *b.data, b.info, (int)p); // Below Macro activates validations ONLY in DEBUG // mode as its name indicates CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/fast.hpp b/src/backend/opencl/kernel/fast.hpp index ec66867b93..bf719e810d 100644 --- a/src/backend/opencl/kernel/fast.hpp +++ b/src/backend/opencl/kernel/fast.hpp @@ -56,33 +56,31 @@ void fast(const unsigned arc_length, std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - kc_t::iterator cache_idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (cache_idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D ARC_LENGTH=" << arc_length - << " -D NONMAX=" << static_cast(nonmax); + if (entry.prog==0 && entry.ker==0) { - if (std::is_same::value || + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D ARC_LENGTH=" << arc_length + << " -D NONMAX=" << static_cast(nonmax); + + if (std::is_same::value || std::is_same::value) { - options << " -D USE_DOUBLE"; - } + options << " -D USE_DOUBLE"; + } - cl::Program prog; - buildProgram(prog, fast_cl, fast_cl_len, options.str()); - entry.prog = new Program(prog); - entry.ker = new Kernel[3]; + cl::Program prog; + buildProgram(prog, fast_cl, fast_cl_len, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel[3]; - entry.ker[0] = Kernel(*entry.prog, "locate_features"); - entry.ker[1] = Kernel(*entry.prog, "non_max_counts"); - entry.ker[2] = Kernel(*entry.prog, "get_features"); + entry.ker[0] = Kernel(*entry.prog, "locate_features"); + entry.ker[1] = Kernel(*entry.prog, "non_max_counts"); + entry.ker[2] = Kernel(*entry.prog, "get_features"); - kernelCaches[device][ref_name] = entry; - } else { - entry = cache_idx -> second; + addKernelToCache(device, ref_name, entry); } const unsigned max_feat = ceil(in.info.dims[0] * in.info.dims[1] * feature_ratio); diff --git a/src/backend/opencl/kernel/fftconvolve.hpp b/src/backend/opencl/kernel/fftconvolve.hpp index aa1449d1fc..1204047fe5 100644 --- a/src/backend/opencl/kernel/fftconvolve.hpp +++ b/src/backend/opencl/kernel/fftconvolve.hpp @@ -11,13 +11,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include using cl::Buffer; using cl::Program; @@ -29,10 +29,8 @@ using cl::NDRange; namespace opencl { - namespace kernel { - static const int THREADS = 256; void calcParamSizes(Param& sig_tmp, @@ -81,33 +79,37 @@ void packDataHelper(Param packed, const int baseDim, AF_BATCH_KIND kind) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map fftconvolveProgs; - static std::map pdKernel; - static std::map paKernel; + std::string refName = + std::string("pack_data_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(isDouble); int device = getActiveDeviceId(); + kc_entry_t pdkEntry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { + if (pdkEntry.prog==0 && pdkEntry.ker==0) { + std::ostringstream options; - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); + options << " -D T=" << dtype_traits::getName(); - if ((af_dtype) dtype_traits::af_type == c32) { - options << " -D CONVT=float"; - } - else if ((af_dtype) dtype_traits::af_type == c64 && isDouble) { - options << " -D CONVT=double" - << " -D USE_DOUBLE"; - } + if ((af_dtype) dtype_traits::af_type == c32) { + options << " -D CONVT=float"; + } + else if ((af_dtype) dtype_traits::af_type == c64 && isDouble) { + options << " -D CONVT=double" + << " -D USE_DOUBLE"; + } - cl::Program prog; - buildProgram(prog, fftconvolve_pack_cl, fftconvolve_pack_cl_len, options.str()); - fftconvolveProgs[device] = new Program(prog); + const char* ker_strs[] = {fftconvolve_pack_cl}; + const int ker_lens[] = {fftconvolve_pack_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + pdkEntry.prog = new Program(prog); + pdkEntry.ker = new Kernel(*pdkEntry.prog, "pack_data"); - pdKernel[device] = new Kernel(*fftconvolveProgs[device], "pack_data"); - paKernel[device] = new Kernel(*fftconvolveProgs[device], "pad_array"); - }); + addKernelToCache(device, refName, pdkEntry); + } Param sig_tmp, filter_tmp; calcParamSizes(sig_tmp, filter_tmp, packed, sig, filter, baseDim, kind); @@ -127,25 +129,53 @@ void packDataHelper(Param packed, // Pack signal in a complex matrix where first dimension is half the input // (allows faster FFT computation) and pad array to a power of 2 with 0s - auto pdOp = KernelFunctor (*pdKernel[device]); + auto pdOp = KernelFunctor< Buffer, KParam, Buffer, KParam, const int, const int > (*pdkEntry.ker); pdOp(EnqueueArgs(getQueue(), global, local), - *sig_tmp.data, sig_tmp.info, *sig.data, sig.info, - sig_half_d0, sig_half_d0_odd); + *sig_tmp.data, sig_tmp.info, *sig.data, sig.info, sig_half_d0, sig_half_d0_odd); + CL_DEBUG_FINISH(getQueue()); + refName = + std::string("pack_array_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(isDouble); + + kc_entry_t pakEntry = kernelCache(device, refName); + + if (pakEntry.prog==0 && pakEntry.ker==0) { + std::ostringstream options; + + options << " -D T=" << dtype_traits::getName(); + + if ((af_dtype) dtype_traits::af_type == c32) { + options << " -D CONVT=float"; + } + else if ((af_dtype) dtype_traits::af_type == c64 && isDouble) { + options << " -D CONVT=double" + << " -D USE_DOUBLE"; + } + + const char* ker_strs[] = {fftconvolve_pack_cl}; + const int ker_lens[] = {fftconvolve_pack_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + pakEntry.prog = new Program(prog); + pakEntry.ker = new Kernel(*pakEntry.prog, "pad_array"); + + addKernelToCache(device, refName, pakEntry); + } + blocks = divup(filter_packed_elem, THREADS); global = NDRange(blocks * THREADS); // Pad filter array with 0s - auto paOp = KernelFunctor (*paKernel[device]); + auto paOp = KernelFunctor< Buffer, KParam, Buffer, KParam > (*pakEntry.ker); paOp(EnqueueArgs(getQueue(), global, local), - *filter_tmp.data, filter_tmp.info, - *filter.data, filter.info); + *filter_tmp.data, filter_tmp.info, *filter.data, filter.info); + CL_DEBUG_FINISH(getQueue()); } @@ -156,38 +186,40 @@ void complexMultiplyHelper(Param packed, const int baseDim, AF_BATCH_KIND kind) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map fftconvolveProgs; - static std::map cmKernel; + std::string refName = + std::string("complex_multiply_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(isDouble); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + + options << " -D T=" << dtype_traits::getName() + << " -D AF_BATCH_NONE=" << (int)AF_BATCH_NONE + << " -D AF_BATCH_LHS=" << (int)AF_BATCH_LHS + << " -D AF_BATCH_RHS=" << (int)AF_BATCH_RHS + << " -D AF_BATCH_SAME=" << (int)AF_BATCH_SAME; + + if ((af_dtype) dtype_traits::af_type == c32) { + options << " -D CONVT=float"; + } else if ((af_dtype) dtype_traits::af_type == c64 && isDouble) { + options << " -D CONVT=double" + << " -D USE_DOUBLE"; + } + + const char* ker_strs[] = {fftconvolve_multiply_cl}; + const int ker_lens[] = {fftconvolve_multiply_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "complex_multiply"); - std::call_once( compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D AF_BATCH_NONE=" << (int)AF_BATCH_NONE - << " -D AF_BATCH_LHS=" << (int)AF_BATCH_LHS - << " -D AF_BATCH_RHS=" << (int)AF_BATCH_RHS - << " -D AF_BATCH_SAME=" << (int)AF_BATCH_SAME; - - if ((af_dtype) dtype_traits::af_type == c32) { - options << " -D CONVT=float"; - } - else if ((af_dtype) dtype_traits::af_type == c64 && isDouble) { - options << " -D CONVT=double" - << " -D USE_DOUBLE"; - } - - cl::Program prog; - buildProgram(prog, - fftconvolve_multiply_cl, - fftconvolve_multiply_cl_len, - options.str()); - fftconvolveProgs[device] = new Program(prog); - - cmKernel[device] = new Kernel(*fftconvolveProgs[device], "complex_multiply"); - }); + addKernelToCache(device, refName, entry); + } Param sig_tmp, filter_tmp; calcParamSizes(sig_tmp, filter_tmp, packed, sig, filter, baseDim, kind); @@ -203,16 +235,13 @@ void complexMultiplyHelper(Param packed, NDRange global(blocks * THREADS); // Multiply filter and signal FFT arrays - auto cmOp = KernelFunctor (*cmKernel[device]); + auto cmOp = KernelFunctor< Buffer, KParam, Buffer, KParam, Buffer, KParam, + const int, const int > (*entry.ker); cmOp(EnqueueArgs(getQueue(), global, local), - *packed.data, packed.info, - *sig_tmp.data, sig_tmp.info, - *filter_tmp.data, filter_tmp.info, - mul_elem, (int)kind); + *packed.data, packed.info, *sig_tmp.data, sig_tmp.info, + *filter_tmp.data, filter_tmp.info, mul_elem, (int)kind); + CL_DEBUG_FINISH(getQueue()); } @@ -224,42 +253,46 @@ void reorderOutputHelper(Param out, const int baseDim, AF_BATCH_KIND kind) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map fftconvolveProgs; - static std::map roKernel; + std::string refName = + std::string("reorder_output_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(isDouble) + + std::to_string(roundOut) + + std::to_string(expand); - int fftScale = 1; + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - // Calculate the scale by which to divide clFFT results - for (int k = 0; k < baseDim; k++) - fftScale *= packed.info.dims[k]; + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; - int device = getActiveDeviceId(); + options << " -D T=" << dtype_traits::getName() + << " -D ROUND_OUT=" << (int)roundOut + << " -D EXPAND=" << (int)expand; - std::call_once( compileFlags[device], [device] () { + if ((af_dtype) dtype_traits::af_type == c32) { + options << " -D CONVT=float"; + } else if ((af_dtype) dtype_traits::af_type == c64 && isDouble) { + options << " -D CONVT=double" + << " -D USE_DOUBLE"; + } - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D ROUND_OUT=" << (int)roundOut - << " -D EXPAND=" << (int)expand; + const char* ker_strs[] = {fftconvolve_reorder_cl}; + const int ker_lens[] = {fftconvolve_reorder_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "reorder_output"); - if ((af_dtype) dtype_traits::af_type == c32) { - options << " -D CONVT=float"; - } - else if ((af_dtype) dtype_traits::af_type == c64 && isDouble) { - options << " -D CONVT=double" - << " -D USE_DOUBLE"; - } + addKernelToCache(device, refName, entry); + } - cl::Program prog; - buildProgram(prog, - fftconvolve_reorder_cl, - fftconvolve_reorder_cl_len, - options.str()); - fftconvolveProgs[device] = new Program(prog); + int fftScale = 1; - roKernel[device] = new Kernel(*fftconvolveProgs[device], "reorder_output"); - }); + // Calculate the scale by which to divide clFFT results + for (int k = 0; k < baseDim; k++) + fftScale *= packed.info.dims[k]; Param sig_tmp, filter_tmp; calcParamSizes(sig_tmp, filter_tmp, packed, sig, filter, baseDim, kind); @@ -272,26 +305,18 @@ void reorderOutputHelper(Param out, NDRange local(THREADS); NDRange global(blocks * THREADS); - auto roOp = KernelFunctor (*roKernel[device]); + auto roOp = KernelFunctor< Buffer, KParam, Buffer, KParam, KParam, const int, + const int, const int > (*entry.ker); if (kind == AF_BATCH_RHS) { - roOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, - *filter_tmp.data, filter_tmp.info, - filter.info, sig_half_d0, baseDim, fftScale); - } - else { - roOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, - *sig_tmp.data, sig_tmp.info, - filter.info, sig_half_d0, baseDim, fftScale); + roOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, + *filter_tmp.data, filter_tmp.info, filter.info, sig_half_d0, baseDim, fftScale); + } else { + roOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, + *sig_tmp.data, sig_tmp.info, filter.info, sig_half_d0, baseDim, fftScale); } + CL_DEBUG_FINISH(getQueue()); } - } // namespace kernel - } // namespace opencl diff --git a/src/backend/opencl/kernel/gradient.hpp b/src/backend/opencl/kernel/gradient.hpp index 4dcca9bc94..393fa16743 100644 --- a/src/backend/opencl/kernel/gradient.hpp +++ b/src/backend/opencl/kernel/gradient.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -31,62 +30,64 @@ using std::string; namespace opencl { - namespace kernel - { - // Kernel Launch Config Values - static const int TX = 32; - static const int TY = 8; +namespace kernel +{ +// Kernel Launch Config Values +static const int TX = 32; +static const int TY = 8; - template - void gradient(Param grad0, Param grad1, const Param in) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map gradProgs; - static std::map gradKernels; +template +void gradient(Param grad0, Param grad1, const Param in) +{ + std::string refName = std::string("gradient_kernel_") + std::string(dtype_traits::getName()); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - ToNumStr toNumStr; - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D TX=" << TX - << " -D TY=" << TY - << " -D ZERO=" << toNumStr(scalar(0)); + if (entry.prog==0 && entry.ker==0) { + ToNumStr toNumStr; + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D TX=" << TX + << " -D TY=" << TY + << " -D ZERO=" << toNumStr(scalar(0)); - if((af_dtype) dtype_traits::af_type == c32 || - (af_dtype) dtype_traits::af_type == c64) { - options << " -D CPLX=1"; - } else { - options << " -D CPLX=0"; - } - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, gradient_cl, gradient_cl_len, options.str()); - gradProgs[device] = new Program(prog); - gradKernels[device] = new Kernel(*gradProgs[device], "gradient_kernel"); - }); + if((af_dtype) dtype_traits::af_type == c32 || + (af_dtype) dtype_traits::af_type == c64) { + options << " -D CPLX=1"; + } else { + options << " -D CPLX=0"; + } + if (std::is_same::value || + std::is_same::value) { + options << " -D USE_DOUBLE"; + } - auto gradOp = KernelFunctor - (*gradKernels[device]); + const char* ker_strs[] = {gradient_cl}; + const int ker_lens[] = {gradient_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "gradient_kernel"); - NDRange local(TX, TY, 1); + addKernelToCache(device, refName, entry); + } - int blocksPerMatX = divup(in.info.dims[0], TX); - int blocksPerMatY = divup(in.info.dims[1], TY); - NDRange global(local[0] * blocksPerMatX * in.info.dims[2], - local[1] * blocksPerMatY * in.info.dims[3], - 1); + auto gradOp = KernelFunctor< Buffer, const KParam, Buffer, const KParam, + const Buffer, const KParam, const int, const int >(*entry.ker); - gradOp(EnqueueArgs(getQueue(), global, local), - *grad0.data, grad0.info, *grad1.data, grad1.info, - *in.data, in.info, blocksPerMatX, blocksPerMatY); + NDRange local(TX, TY, 1); - CL_DEBUG_FINISH(getQueue()); - } - } + int blocksPerMatX = divup(in.info.dims[0], TX); + int blocksPerMatY = divup(in.info.dims[1], TY); + NDRange global(local[0] * blocksPerMatX * in.info.dims[2], + local[1] * blocksPerMatY * in.info.dims[3], 1); + + gradOp(EnqueueArgs(getQueue(), global, local), + *grad0.data, grad0.info, *grad1.data, grad1.info, + *in.data, in.info, blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/harris.hpp b/src/backend/opencl/kernel/harris.hpp index bfa12a0eab..b2f834f971 100644 --- a/src/backend/opencl/kernel/harris.hpp +++ b/src/backend/opencl/kernel/harris.hpp @@ -19,7 +19,8 @@ #include #include #include -#include +#include +#include using cl::Buffer; using cl::Program; @@ -30,10 +31,8 @@ using cl::NDRange; namespace opencl { - namespace kernel { - static const unsigned HARRIS_THREADS_PER_GROUP = 256; static const unsigned HARRIS_THREADS_X = 16; static const unsigned HARRIS_THREADS_Y = HARRIS_THREADS_PER_GROUP / HARRIS_THREADS_X; @@ -85,6 +84,55 @@ void conv_helper(Param &ixx, Param &ixy, Param &iyy, Param &filter) bufferFree(iyy_tmp.data); } +template +std::tuple +getHarrisKernels() +{ + static const std::string kernelNames[4] = + {"second_order_deriv", "keep_corners", "harris_responses", "non_maximal"}; + + kc_entry_t entries[4]; + + int device = getActiveDeviceId(); + + std::string checkName = kernelNames[0] + std::string("_") + std::string(dtype_traits::getName()); + + entries[0] = kernelCache(device, checkName); + + if (entries[0].prog==0 && entries[0].ker==0) + { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {harris_cl}; + const int ker_lens[] = {harris_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + + for (int i=0; i<4; ++i) + { + entries[i].prog = new Program(prog); + entries[i].ker = new Kernel(*entries[i].prog, kernelNames[i].c_str()); + + std::string name = kernelNames[i] + + std::string("_") + std::string(dtype_traits::getName()); + + addKernelToCache(device, name, entries[i]); + } + } else { + for (int i=1; i<4; ++i) { + std::string name = kernelNames[i] + + std::string("_") + std::string(dtype_traits::getName()); + + entries[i] = kernelCache(device, name); + } + } + + return std::make_tuple(entries[0].ker, entries[1].ker, entries[2].ker, entries[3].ker); +} + template void harris(unsigned* corners_out, Param &x_out, @@ -97,34 +145,7 @@ void harris(unsigned* corners_out, const unsigned filter_len, const float k_thr) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map harrisProgs; - static std::map soKernel; - static std::map kcKernel; - static std::map hrKernel; - static std::map nmKernel; - - int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - cl::Program prog; - buildProgram(prog, harris_cl, harris_cl_len, options.str()); - harrisProgs[device] = new Program(prog); - - soKernel[device] = new Kernel(*harrisProgs[device], "second_order_deriv"); - kcKernel[device] = new Kernel(*harrisProgs[device], "keep_corners"); - hrKernel[device] = new Kernel(*harrisProgs[device], "harris_responses"); - nmKernel[device] = new Kernel(*harrisProgs[device], "non_maximal"); - }); + auto kernels = getHarrisKernels(); // Window filter convAccT* h_filter = new convAccT[filter_len]; @@ -132,8 +153,7 @@ void harris(unsigned* corners_out, if (sigma < 0.5f) { for (unsigned i = 0; i < filter_len; i++) h_filter[i] = (T)1.f / (filter_len); - } - else { + } else { gaussian1D(h_filter, (int)filter_len, sigma); } @@ -181,13 +201,13 @@ void harris(unsigned* corners_out, const NDRange local_so(HARRIS_THREADS_PER_GROUP, 1); const NDRange global_so(blk_x_so * HARRIS_THREADS_PER_GROUP, 1); - auto soOp = KernelFunctor (*soKernel[device]); + auto soOp = KernelFunctor< Buffer, Buffer, Buffer, + unsigned, Buffer, Buffer > (*std::get<0>(kernels)); // Compute second-order derivatives soOp(EnqueueArgs(getQueue(), global_so, local_so), - *ixx.data, *ixy.data, *iyy.data, - in.info.dims[3] * in.info.strides[3], *ix.data, *iy.data); + *ixx.data, *ixy.data, *iyy.data, + in.info.dims[3] * in.info.strides[3], *ix.data, *iy.data); CL_DEBUG_FINISH(getQueue()); bufferFree(ix.data); @@ -205,14 +225,13 @@ void harris(unsigned* corners_out, const NDRange local_hr(HARRIS_THREADS_X, HARRIS_THREADS_Y); const NDRange global_hr(blk_x_hr * HARRIS_THREADS_X, blk_y_hr * HARRIS_THREADS_Y); - auto hrOp = KernelFunctor (*hrKernel[device]); + auto hrOp = KernelFunctor< Buffer, unsigned, unsigned, Buffer, Buffer, Buffer, + float, unsigned> (*std::get<2>(kernels)); // Calculate Harris responses for all pixels hrOp(EnqueueArgs(getQueue(), global_hr, local_hr), - *d_responses, in.info.dims[0], in.info.dims[1], - *ixx.data, *ixy.data, *iyy.data, k_thr, border_len); + *d_responses, in.info.dims[0], in.info.dims[1], + *ixx.data, *ixy.data, *iyy.data, k_thr, border_len); CL_DEBUG_FINISH(getQueue()); bufferFree(ixx.data); @@ -233,15 +252,14 @@ void harris(unsigned* corners_out, const float min_r = (max_corners > 0) ? 0.f : min_response; - auto nmOp = KernelFunctor (*nmKernel[device]); + auto nmOp = KernelFunctor< Buffer, Buffer, Buffer, Buffer, Buffer, unsigned, unsigned, + float, unsigned, unsigned> (*std::get<3>(kernels)); // Perform non-maximal suppression nmOp(EnqueueArgs(getQueue(), global_hr, local_hr), - *d_x_corners, *d_y_corners, *d_resp_corners, *d_corners_found, - *d_responses, in.info.dims[0], in.info.dims[1], - min_r, border_len, corner_lim); + *d_x_corners, *d_y_corners, *d_resp_corners, *d_corners_found, + *d_responses, in.info.dims[0], in.info.dims[1], + min_r, border_len, corner_lim); CL_DEBUG_FINISH(getQueue()); getQueue().enqueueReadBuffer(*d_corners_found, CL_TRUE, 0, sizeof(unsigned), &corners_found); @@ -295,16 +313,15 @@ void harris(unsigned* corners_out, const NDRange local_kc(HARRIS_THREADS_PER_GROUP, 1); const NDRange global_kc(blk_x_kc * HARRIS_THREADS_PER_GROUP, 1); - auto kcOp = KernelFunctor (*kcKernel[device]); + auto kcOp = KernelFunctor< Buffer, Buffer, Buffer, Buffer, Buffer, Buffer, Buffer, + unsigned> (*std::get<1>(kernels)); // Keep only the first corners_to_keep corners with higher Harris // responses kcOp(EnqueueArgs(getQueue(), global_kc, local_kc), - *x_out.data, *y_out.data, *resp_out.data, - *d_x_corners, *d_y_corners, *harris_resp.data, *harris_idx.data, - *corners_out); + *x_out.data, *y_out.data, *resp_out.data, + *d_x_corners, *d_y_corners, *harris_resp.data, *harris_idx.data, + *corners_out); CL_DEBUG_FINISH(getQueue()); bufferFree(d_x_corners); @@ -330,7 +347,5 @@ void harris(unsigned* corners_out, resp_out.data = d_resp_corners; } } - } //namespace kernel - } //namespace opencl diff --git a/src/backend/opencl/kernel/histogram.hpp b/src/backend/opencl/kernel/histogram.hpp index 28b0e1ad3d..6a998b6621 100644 --- a/src/backend/opencl/kernel/histogram.hpp +++ b/src/backend/opencl/kernel/histogram.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -23,10 +22,8 @@ using cl::KernelFunctor; namespace opencl { - namespace kernel { - static const unsigned MAX_BINS = 4000; static const int THREADS_X = 256; static const int THRD_LOAD = 16; @@ -34,34 +31,38 @@ static const int THRD_LOAD = 16; template void histogram(Param out, const Param in, int nbins, float minval, float maxval) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map histProgs; - static std::map histKernels; + std::string refName = std::string("histogram_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(isLinear); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D inType=" << dtype_traits::getName() - << " -D outType=" << dtype_traits::getName() - << " -D THRD_LOAD=" << THRD_LOAD; - if (isLinear) - options << " -D IS_LINEAR"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D inType=" << dtype_traits::getName() + << " -D outType=" << dtype_traits::getName() + << " -D THRD_LOAD=" << THRD_LOAD; + if (isLinear) + options << " -D IS_LINEAR"; + if (std::is_same::value || + std::is_same::value) { + options << " -D USE_DOUBLE"; + } - Program prog; - buildProgram(prog, histogram_cl, histogram_cl_len, options.str()); - histProgs[device] = new Program(prog); - histKernels[device] = new Kernel(*histProgs[device], "histogram"); - }); + const char* ker_strs[] = {histogram_cl}; + const int ker_lens[] = {histogram_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "histogram"); - auto histogramOp = KernelFunctor(*histKernels[device]); + addKernelToCache(device, refName, entry); + } + + auto histogramOp = KernelFunctor< Buffer, KParam, Buffer, KParam, cl::LocalSpaceArg, + int, int, float, float, int >(*entry.ker); int nElems = in.info.dims[0]*in.info.dims[1]; int blk_x = divup(nElems, THRD_LOAD*THREADS_X); @@ -71,11 +72,10 @@ void histogram(Param out, const Param in, int nbins, float minval, float maxval) NDRange global(blk_x*in.info.dims[2]*THREADS_X, in.info.dims[3]); histogramOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, - cl::Local(locSize), nElems, nbins, minval, maxval, blk_x); + *out.data, out.info, *in.data, in.info, + cl::Local(locSize), nElems, nbins, minval, maxval, blk_x); CL_DEBUG_FINISH(getQueue()); } - } } diff --git a/src/backend/opencl/kernel/homography.hpp b/src/backend/opencl/kernel/homography.hpp index a5f6faf096..572c21bdca 100644 --- a/src/backend/opencl/kernel/homography.hpp +++ b/src/backend/opencl/kernel/homography.hpp @@ -17,6 +17,7 @@ #include #include #include +#include using cl::Buffer; using cl::Program; @@ -28,68 +29,88 @@ using std::vector; namespace opencl { - namespace kernel { - const int HG_THREADS_X = 16; const int HG_THREADS_Y = 16; const int HG_THREADS = 256; template -int computeH( - Param bestH, - Param H, - Param err, - Param x_src, - Param y_src, - Param x_dst, - Param y_dst, - Param rnd, - const unsigned iterations, - const unsigned nsamples, - const float inlier_thr) +std::array getHomographyKernels() { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map hgProgs; - static std::map chKernel; - static std::map ehKernel; - static std::map cmKernel; - static std::map fmKernel; - static std::map clKernel; + static const unsigned NUM_KERNELS = 5; + static const std::string kernelNames[NUM_KERNELS] = + {"compute_homography", "eval_homography", "compute_median", + "find_min_median", "compute_lmeds_inliers"}; + + kc_entry_t entries[NUM_KERNELS]; int device = getActiveDeviceId(); - std::call_once( compileFlags[device], [device] () { + std::string checkName = kernelNames[0] + std::string("_") + + std::string(dtype_traits::getName()) + + std::to_string(htype); + + entries[0] = kernelCache(device, checkName); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); + if (entries[0].prog==0 && entries[0].ker==0) + { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); - if (std::is_same::value) { - options << " -D USE_DOUBLE"; - options << " -D EPS=" << DBL_EPSILON; - } else - options << " -D EPS=" << FLT_EPSILON; + if (std::is_same::value) { + options << " -D USE_DOUBLE"; + options << " -D EPS=" << DBL_EPSILON; + } else + options << " -D EPS=" << FLT_EPSILON; + + if (htype == AF_HOMOGRAPHY_RANSAC) + options << " -D RANSAC"; + else if (htype == AF_HOMOGRAPHY_LMEDS) + options << " -D LMEDS"; + + if (getActiveDeviceType() == CL_DEVICE_TYPE_CPU) { + options << " -D IS_CPU"; + } - if (htype == AF_HOMOGRAPHY_RANSAC) - options << " -D RANSAC"; - else if (htype == AF_HOMOGRAPHY_LMEDS) - options << " -D LMEDS"; + cl::Program prog; + buildProgram(prog, homography_cl, homography_cl_len, options.str()); + + for (unsigned i=0; i::getName()) + + std::to_string(htype); + + addKernelToCache(device, name, entries[i]); + } + } else { + for (unsigned i=1; i::getName()) + + std::to_string(htype); + + entries[i] = kernelCache(device, name); + } + } - if (getActiveDeviceType() == CL_DEVICE_TYPE_CPU) { - options << " -D IS_CPU"; - } + std::array retVal; + for (unsigned i=0; i +int computeH(Param bestH, Param H, Param err, Param x_src, Param y_src, + Param x_dst, Param y_dst, Param rnd, const unsigned iterations, + const unsigned nsamples, + const float inlier_thr) +{ + auto kernels = getHomographyKernels(); const int blk_x_ch = 1; const int blk_y_ch = divup(iterations, HG_THREADS_Y); @@ -97,14 +118,12 @@ int computeH( const NDRange global_ch(blk_x_ch * HG_THREADS_X, blk_y_ch * HG_THREADS_Y); // Build linear system and solve SVD - auto chOp = KernelFunctor(*chKernel[device]); - - chOp(EnqueueArgs(getQueue(), global_ch, local_ch), - *H.data, H.info, - *x_src.data, *y_src.data, *x_dst.data, *y_dst.data, - *rnd.data, rnd.info, iterations); + auto chOp = KernelFunctor< Buffer, KParam, Buffer, Buffer, Buffer, Buffer, + Buffer, KParam, unsigned>(*kernels[0]); + + chOp(EnqueueArgs(getQueue(), global_ch, local_ch), *H.data, H.info, + *x_src.data, *y_src.data, *x_dst.data, *y_dst.data, *rnd.data, rnd.info, iterations); + CL_DEBUG_FINISH(getQueue()); const int blk_x_eh = divup(iterations, HG_THREADS); @@ -132,16 +151,14 @@ int computeH( median.data = bufferAlloc(sizeof(float)); // Compute (and for RANSAC, evaluate) homographies - auto ehOp = KernelFunctor(*ehKernel[device]); - - ehOp(EnqueueArgs(getQueue(), global_eh, local_eh), - *inliers.data, *idx.data, *H.data, H.info, - *err.data, err.info, - *x_src.data, *y_src.data, *x_dst.data, *y_dst.data, - *rnd.data, iterations, nsamples, inlier_thr); + auto ehOp = KernelFunctor< Buffer, Buffer, Buffer, KParam, Buffer, KParam, + Buffer, Buffer, Buffer, Buffer, + Buffer, unsigned, unsigned, float>(*kernels[1]); + + ehOp(EnqueueArgs(getQueue(), global_eh, local_eh), *inliers.data, *idx.data, *H.data, H.info, + *err.data, err.info, *x_src.data, *y_src.data, *x_dst.data, *y_dst.data, + *rnd.data, iterations, nsamples, inlier_thr); + CL_DEBUG_FINISH(getQueue()); unsigned inliersH, idxH; @@ -154,12 +171,11 @@ int computeH( float minMedian; // Compute median of every iteration - auto cmOp = KernelFunctor(*cmKernel[device]); + auto cmOp = KernelFunctor(*kernels[2]); cmOp(EnqueueArgs(getQueue(), global_eh, local_eh), - *median.data, *idx.data, *err.data, err.info, - iterations); + *median.data, *idx.data, *err.data, err.info, iterations); + CL_DEBUG_FINISH(getQueue()); // Reduce medians, only in case iterations > 256 @@ -170,12 +186,11 @@ int computeH( cl::Buffer* finalMedian = bufferAlloc(sizeof(float)); cl::Buffer* finalIdx = bufferAlloc(sizeof(unsigned)); - auto fmOp = KernelFunctor(*fmKernel[device]); + auto fmOp = KernelFunctor(*kernels[3]); fmOp(EnqueueArgs(getQueue(), global_fm, local_fm), - *finalMedian, *finalIdx, *median.data, median.info, - *idx.data); + *finalMedian, *finalIdx, *median.data, median.info, *idx.data); + CL_DEBUG_FINISH(getQueue()); getQueue().enqueueReadBuffer(*finalMedian, CL_TRUE, 0, sizeof(float), &minMedian); @@ -196,14 +211,12 @@ int computeH( const NDRange local_cl(HG_THREADS); const NDRange global_cl(blk_x_cl * HG_THREADS); - auto clOp = KernelFunctor(*clKernel[device]); + auto clOp = KernelFunctor< Buffer, Buffer, Buffer, Buffer, Buffer, Buffer, + float, unsigned >(*kernels[4]); + + clOp(EnqueueArgs(getQueue(), global_cl, local_cl), *inliers.data, *bestH.data, + *x_src.data, *y_src.data, *x_dst.data, *y_dst.data, minMedian, nsamples); - clOp(EnqueueArgs(getQueue(), global_cl, local_cl), - *inliers.data, *bestH.data, - *x_src.data, *y_src.data, *x_dst.data, *y_dst.data, - minMedian, nsamples); CL_DEBUG_FINISH(getQueue()); // Adds up the total number of inliers @@ -234,7 +247,5 @@ int computeH( return (int)inliersH; } - } // namespace kernel - } // namespace cuda diff --git a/src/backend/opencl/kernel/hsv_rgb.hpp b/src/backend/opencl/kernel/hsv_rgb.hpp index 569912c836..0165512173 100644 --- a/src/backend/opencl/kernel/hsv_rgb.hpp +++ b/src/backend/opencl/kernel/hsv_rgb.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,37 +27,36 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; template void hsv2rgb_convert(Param out, const Param in) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map hrProgs; - static std::map hrKernels; + std::string refName = std::string("hsvrgb_convert_") + + std::string(dtype_traits::getName()) + std::to_string(isHSV2RGB); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); + if(isHSV2RGB) options << " -D isHSV2RGB"; + if (std::is_same::value) options << " -D USE_DOUBLE"; - if(isHSV2RGB) options << " -D isHSV2RGB"; + const char* ker_strs[] = {hsv_rgb_cl}; + const int ker_lens[] = {hsv_rgb_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "convert"); - if (std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, hsv_rgb_cl, hsv_rgb_cl_len, options.str()); - hrProgs[device] = new Program(prog); - hrKernels[device] = new Kernel(*hrProgs[device], "convert"); - }); + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); @@ -69,14 +67,11 @@ void hsv2rgb_convert(Param out, const Param in) // parameter would be along 4th dimension NDRange global(blk_x * in.info.dims[3] * THREADS_X, blk_y * THREADS_Y); - auto hsvrgbOp = KernelFunctor (*hrKernels[device]); + auto hsvrgbOp = KernelFunctor (*entry.ker); - hsvrgbOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, blk_x); + hsvrgbOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *in.data, in.info, blk_x); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/identity.hpp b/src/backend/opencl/kernel/identity.hpp index 3882de037c..49197ba2d3 100644 --- a/src/backend/opencl/kernel/identity.hpp +++ b/src/backend/opencl/kernel/identity.hpp @@ -13,8 +13,7 @@ #include #include #include -#include -#include +#include #include #include "config.hpp" @@ -30,49 +29,46 @@ using af::scalar_to_option; namespace opencl { - namespace kernel { +template +static void identity(Param out) +{ + std::string refName = std::string("identity_kernel") + std::string(dtype_traits::getName()); - template - static void identity(Param out) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map identityProgs; - static std::map identityKernels; + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - int device = getActiveDeviceId(); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D ONE=(T)(" << scalar_to_option(scalar(1)) << ")" + << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")"; + if (std::is_same::value || + std::is_same::value) { + options << " -D USE_DOUBLE"; + } - std::call_once( compileFlags[device], [device] () { - ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D ONE=(T)(" << scalar_to_option(scalar(1)) << ")" - << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, identity_cl, identity_cl_len, options.str()); - identityProgs[device] = new Program(prog); - identityKernels[device] = new Kernel(*identityProgs[device], "identity_kernel"); - }); + const char* ker_strs[] = {identity_cl}; + const int ker_lens[] = {identity_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "identity_kernel"); - NDRange local(32, 8); - int groups_x = divup(out.info.dims[0], local[0]); - int groups_y = divup(out.info.dims[1], local[1]); - NDRange global(groups_x * out.info.dims[2] * local[0], - groups_y * out.info.dims[3] * local[1]); + addKernelToCache(device, refName, entry); + } - auto identityOp = KernelFunctor (*identityKernels[device]); + NDRange local(32, 8); + int groups_x = divup(out.info.dims[0], local[0]); + int groups_y = divup(out.info.dims[1], local[1]); + NDRange global(groups_x * out.info.dims[2] * local[0], groups_y * out.info.dims[3] * local[1]); - identityOp(EnqueueArgs(getQueue(), global, local), - *(out.data), out.info, groups_x, groups_y); - CL_DEBUG_FINISH(getQueue()); + auto identityOp = KernelFunctor (*entry.ker); - } + identityOp(EnqueueArgs(getQueue(), global, local), *(out.data), out.info, groups_x, groups_y); + CL_DEBUG_FINISH(getQueue()); +} } - } diff --git a/src/backend/opencl/kernel/iir.hpp b/src/backend/opencl/kernel/iir.hpp index d9b220195b..ef3025e7f6 100644 --- a/src/backend/opencl/kernel/iir.hpp +++ b/src/backend/opencl/kernel/iir.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -30,69 +29,59 @@ using af::scalar_to_option; namespace opencl { +namespace kernel +{ +template +void iir(Param y, Param c, Param a) +{ + //FIXME: This is a temporary fix. Ideally the local memory should be allocted outside + static const int MAX_A_SIZE = (1024 * sizeof(double)) / sizeof(T); - namespace kernel - { - template - void iir(Param y, Param c, Param a) - { - - //FIXME: This is a temporary fix. Ideally the local memory should be allocted outside - static const int MAX_A_SIZE = (1024 * sizeof(double)) / sizeof(T); - - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map iirProgs; - static std::map iirKernels; - - int device = getActiveDeviceId(); - - std::call_once(compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D MAX_A_SIZE=" << MAX_A_SIZE - << " -D BATCH_A=" << batch_a - << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")" - << " -D T=" << dtype_traits::getName(); - - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - cl::Program prog; - buildProgram(prog, iir_cl, iir_cl_len, options.str()); - iirProgs[device] = new Program(prog); + std::string refName = std::string("iir_kernel_") + + std::string(dtype_traits::getName()) + std::to_string(batch_a); - iirKernels[device] = new Kernel(*iirProgs[device], "iir_kernel"); - }); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D MAX_A_SIZE=" << MAX_A_SIZE + << " -D BATCH_A=" << batch_a + << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")" + << " -D T=" << dtype_traits::getName(); - const int groups_y = y.info.dims[1]; - const int groups_x = y.info.dims[2]; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - int threads = 256; - while (threads > (int)y.info.dims[0] && threads > 32) threads /= 2; + const char* ker_strs[] = {iir_cl}; + const int ker_lens[] = {iir_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "iir_kernel"); + addKernelToCache(device, refName, entry); + } - NDRange local(threads, 1); - NDRange global(groups_x * local[0], - groups_y * y.info.dims[3] * local[1]); + const int groups_y = y.info.dims[1]; + const int groups_x = y.info.dims[2]; - auto iirOp = KernelFunctor(*iirKernels[device]); + int threads = 256; + while (threads > (int)y.info.dims[0] && threads > 32) threads /= 2; - try { - iirOp(EnqueueArgs(getQueue(), global, local), - *y.data, y.info, *c.data, c.info, *a.data, a.info, groups_y); - } catch(cl::Error &clerr) { - AF_ERROR("Size of a too big for this datatype", - AF_ERR_SIZE); - } + NDRange local(threads, 1); + NDRange global(groups_x * local[0], groups_y * y.info.dims[3] * local[1]); - CL_DEBUG_FINISH(getQueue()); - } + auto iirOp = KernelFunctor(*entry.ker); + try { + iirOp(EnqueueArgs(getQueue(), global, local), + *y.data, y.info, *c.data, c.info, *a.data, a.info, groups_y); + } catch(cl::Error &clerr) { + AF_ERROR("Size of a too big for this datatype", AF_ERR_SIZE); } + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/index.hpp b/src/backend/opencl/kernel/index.hpp index ae39e1f64a..4b41c82172 100644 --- a/src/backend/opencl/kernel/index.hpp +++ b/src/backend/opencl/kernel/index.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,10 +27,8 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 32; static const int THREADS_Y = 8; @@ -44,37 +41,37 @@ typedef struct { template void index(Param out, const Param in, const IndexKernelParam_t& p, Buffer *bPtr[4]) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map idxProgs; - static std::map idxKernels; + std::string refName = std::string("indexKernel_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; - if (std::is_same::value || - std::is_same::value) { + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, index_cl, index_cl_len, options.str()); - idxProgs[device] = new Program(prog); - idxKernels[device] = new Kernel(*idxProgs[device], "indexKernel"); - }); + const char* ker_strs[] = {index_cl}; + const int ker_lens[] = {index_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "indexKernel"); + + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); int blk_x = divup(out.info.dims[0], THREADS_X); int blk_y = divup(out.info.dims[1], THREADS_Y); - NDRange global(blk_x * out.info.dims[2] * THREADS_X, - blk_y * out.info.dims[3] * THREADS_Y); + NDRange global(blk_x * out.info.dims[2] * THREADS_X, blk_y * out.info.dims[3] * THREADS_Y); auto indexOp = KernelFunctor(*idxKernels[device]); + Buffer, Buffer, Buffer, Buffer, int, int>(*entry.ker); indexOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *in.data, in.info, p, @@ -82,7 +79,5 @@ void index(Param out, const Param in, const IndexKernelParam_t& p, Buffer *bPtr[ CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/iota.hpp b/src/backend/opencl/kernel/iota.hpp index d018357ab2..8e0d85e41e 100644 --- a/src/backend/opencl/kernel/iota.hpp +++ b/src/backend/opencl/kernel/iota.hpp @@ -13,8 +13,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -29,54 +28,56 @@ using std::string; namespace opencl { - namespace kernel - { - // Kernel Launch Config Values - static const int IOTA_TX = 32; - static const int IOTA_TY = 8; - static const int TILEX = 512; - static const int TILEY = 32; +namespace kernel +{ +// Kernel Launch Config Values +static const int IOTA_TX = 32; +static const int IOTA_TY = 8; +static const int TILEX = 512; +static const int TILEY = 32; - template - void iota(Param out, const af::dim4 &sdims, const af::dim4 &tdims) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map iotaProgs; - static std::map iotaKernels; +template +void iota(Param out, const af::dim4 &sdims, const af::dim4 &tdims) +{ + std::string refName = std::string("iota_kernel_") + std::string(dtype_traits::getName()); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, iota_cl, iota_cl_len, options.str()); - iotaProgs[device] = new Program(prog); - iotaKernels[device] = new Kernel(*iotaProgs[device], "iota_kernel"); - }); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; - auto iotaOp = KernelFunctor (*iotaKernels[device]); + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - NDRange local(IOTA_TX, IOTA_TY, 1); + const char* ker_strs[] = {iota_cl}; + const int ker_lens[] = {iota_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "iota_kernel"); - int blocksPerMatX = divup(out.info.dims[0], TILEX); - int blocksPerMatY = divup(out.info.dims[1], TILEY); - NDRange global(local[0] * blocksPerMatX * out.info.dims[2], - local[1] * blocksPerMatY * out.info.dims[3], - 1); + addKernelToCache(device, refName, entry); + } - iotaOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, sdims[0], sdims[1], sdims[2], sdims[3], - tdims[0], tdims[1], tdims[2], tdims[3], blocksPerMatX, blocksPerMatY); + auto iotaOp = KernelFunctor (*entry.ker); - CL_DEBUG_FINISH(getQueue()); - } - } + NDRange local(IOTA_TX, IOTA_TY, 1); + + int blocksPerMatX = divup(out.info.dims[0], TILEX); + int blocksPerMatY = divup(out.info.dims[1], TILEY); + NDRange global(local[0] * blocksPerMatX * out.info.dims[2], + local[1] * blocksPerMatY * out.info.dims[3], 1); + + iotaOp(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, sdims[0], sdims[1], sdims[2], sdims[3], + tdims[0], tdims[1], tdims[2], tdims[3], blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/ireduce.hpp b/src/backend/opencl/kernel/ireduce.hpp index 2c76f2720f..ab71beb6df 100644 --- a/src/backend/opencl/kernel/ireduce.hpp +++ b/src/backend/opencl/kernel/ireduce.hpp @@ -62,39 +62,37 @@ namespace kernel std::to_string(threads_y); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); - Binary ireduce; - ToNumStr toNumStr; + if (entry.prog==0 && entry.ker==0) { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D dim=" << dim - << " -D DIMY=" << threads_y - << " -D THREADS_X=" << THREADS_X - << " -D init=" << toNumStr(ireduce.init()) - << " -D " << binOpName() - << " -D CPLX=" << af::iscplx() - << " -D IS_FIRST=" << is_first; + Binary ireduce; + ToNumStr toNumStr; + + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D dim=" << dim + << " -D DIMY=" << threads_y + << " -D THREADS_X=" << THREADS_X + << " -D init=" << toNumStr(ireduce.init()) + << " -D " << binOpName() + << " -D CPLX=" << af::iscplx() + << " -D IS_FIRST=" << is_first; - if (std::is_same::value || + if (std::is_same::value || std::is_same::value) { - options << " -D USE_DOUBLE"; - } + options << " -D USE_DOUBLE"; + } - const char *ker_strs[] = {iops_cl, ireduce_dim_cl}; - const int ker_lens[] = {iops_cl_len, ireduce_dim_cl_len}; - Program prog; - buildProgram(prog, 2, ker_strs, ker_lens, options.str()); - entry.prog = new Program(prog); - entry.ker = new Kernel(*entry.prog, "ireduce_dim_kernel"); + const char *ker_strs[] = {iops_cl, ireduce_dim_cl}; + const int ker_lens[] = {iops_cl_len, ireduce_dim_cl_len}; + Program prog; + buildProgram(prog, 2, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "ireduce_dim_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } NDRange local(THREADS_X, threads_y); @@ -174,10 +172,10 @@ namespace kernel std::to_string(threads_x); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { Binary ireduce; ToNumStr toNumStr; @@ -203,9 +201,7 @@ namespace kernel entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "ireduce_first_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } NDRange local(threads_x, THREADS_PER_GROUP / threads_x); diff --git a/src/backend/opencl/kernel/join.hpp b/src/backend/opencl/kernel/join.hpp index 52cfcaeb98..a450f22fcf 100644 --- a/src/backend/opencl/kernel/join.hpp +++ b/src/backend/opencl/kernel/join.hpp @@ -28,59 +28,62 @@ using std::string; namespace opencl { - namespace kernel - { - // Kernel Launch Config Values - static const int TX = 32; - static const int TY = 8; - static const int TILEX = 256; - static const int TILEY = 32; +namespace kernel +{ +// Kernel Launch Config Values +static const int TX = 32; +static const int TY = 8; +static const int TILEX = 256; +static const int TILEY = 32; - template - void join(Param out, const Param in, const af::dim4 offset) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map joinProgs; - static std::map joinKernels; +template +void join(Param out, const Param in, const af::dim4 offset) +{ + std::string refName = std::string("join_kernel_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(dim); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D To=" << dtype_traits::getName() - << " -D Ti=" << dtype_traits::getName() - << " -D dim=" << dim; + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D To=" << dtype_traits::getName() + << " -D Ti=" << dtype_traits::getName() + << " -D dim=" << dim; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } else if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + if (std::is_same::value || std::is_same::value) { + options << " -D USE_DOUBLE"; + } else if (std::is_same::value || std::is_same::value) { + options << " -D USE_DOUBLE"; + } - Program prog; - buildProgram(prog, join_cl, join_cl_len, options.str()); - joinProgs[device] = new Program(prog); - joinKernels[device] = new Kernel(*joinProgs[device], "join_kernel"); - }); + const char* ker_strs[] = {join_cl}; + const int ker_lens[] = {join_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "join_kernel"); - auto joinOp = KernelFunctor (*joinKernels[device]); + addKernelToCache(device, refName, entry); + } - NDRange local(TX, TY, 1); + auto joinOp = KernelFunctor (*entry.ker); - int blocksPerMatX = divup(in.info.dims[0], TILEX); - int blocksPerMatY = divup(in.info.dims[1], TILEY); - NDRange global(local[0] * blocksPerMatX * in.info.dims[2], - local[1] * blocksPerMatY * in.info.dims[3], - 1); + NDRange local(TX, TY, 1); - joinOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *in.data, in.info, - offset[0], offset[1], offset[2], offset[3], blocksPerMatX, blocksPerMatY); + int blocksPerMatX = divup(in.info.dims[0], TILEX); + int blocksPerMatY = divup(in.info.dims[1], TILEY); + NDRange global(local[0] * blocksPerMatX * in.info.dims[2], + local[1] * blocksPerMatY * in.info.dims[3], 1); - CL_DEBUG_FINISH(getQueue()); - } - } + joinOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *in.data, in.info, + offset[0], offset[1], offset[2], offset[3], blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/laset.hpp b/src/backend/opencl/kernel/laset.hpp index 8f5fc1f432..612df24383 100644 --- a/src/backend/opencl/kernel/laset.hpp +++ b/src/backend/opencl/kernel/laset.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,13 +27,10 @@ using cl::EnqueueArgs; using cl::NDRange; using std::string; - namespace opencl { - namespace kernel { - static const int BLK_X = 64; static const int BLK_Y = 32; @@ -49,46 +45,45 @@ void laset(int m, int n, T offdiag, T diag, cl_mem dA, size_t dA_offset, magma_int_t ldda) { - - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map setProgs; - static std::map setKernels; + std::string refName = laset_name() + std::string("_") + + std::string(dtype_traits::getName()) + + std::to_string(uplo); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once(compileFlags[device], [device] () { + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D BLK_X=" << BLK_X + << " -D BLK_Y=" << BLK_Y + << " -D IS_CPLX=" << af::iscplx(); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D BLK_X=" << BLK_X - << " -D BLK_Y=" << BLK_Y - << " -D IS_CPLX=" << af::iscplx(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + const char* ker_strs[] = {laset_cl}; + const int ker_lens[] = {laset_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, laset_name()); - cl::Program prog; - buildProgram(prog, laset_cl, laset_cl_len, options.str()); - setProgs[device] = new Program(prog); - setKernels[device] = new Kernel(*setProgs[device], laset_name()); - }); + addKernelToCache(device, refName, entry); + } int groups_x = (m - 1) / BLK_X + 1; int groups_y = (n - 1) / BLK_Y + 1; NDRange local(BLK_X, 1); - NDRange global(groups_x * local[0], - groups_y * local[1]); + NDRange global(groups_x * local[0], groups_y * local[1]); // retain the cl_mem object during cl::Buffer creation cl::Buffer dAObj(dA, true); - auto lasetOp = KernelFunctor(*setKernels[device]); - lasetOp(EnqueueArgs(getQueue(), global, local), - m, n, offdiag, diag, dAObj, dA_offset, ldda); -} + auto lasetOp = KernelFunctor(*entry.ker); + lasetOp(EnqueueArgs(getQueue(), global, local), m, n, offdiag, diag, dAObj, dA_offset, ldda); +} } } diff --git a/src/backend/opencl/kernel/laset_band.hpp b/src/backend/opencl/kernel/laset_band.hpp index 915be6f560..645622c279 100644 --- a/src/backend/opencl/kernel/laset_band.hpp +++ b/src/backend/opencl/kernel/laset_band.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,13 +27,10 @@ using cl::EnqueueArgs; using cl::NDRange; using std::string; - namespace opencl { - namespace kernel { - #if 0 // Needs to be enabled when unmqr2 is enabled static const int NB = 64; template @@ -47,30 +43,31 @@ void laset_band(int m, int n, int k, T offdiag, T diag, cl_mem dA, size_t dA_offset, magma_int_t ldda) { - - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map setProgs; - static std::map setKernels; + std::string refName = laset_band_name() + std::string("_") + + std::string(dtype_traits::getName()) + + std::to_string(uplo); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once(compileFlags[device], [device] () { + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D NB=" << NB + << " -D IS_CPLX=" << af::iscplx(); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D NB=" << NB - << " -D IS_CPLX=" << af::iscplx(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + const char* ker_strs[] = {laset_band_cl}; + const int ker_lens[] = {laset_band_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, laset_band_name()); - cl::Program prog; - buildProgram(prog, laset_band_cl, laset_band_cl_len, options.str()); - setProgs[device] = new Program(prog); - setKernels[device] = new Kernel(*setProgs[device], laset_band_name()); - }); + addKernelToCache(device, refName, entry); + } int threads = 1; int groups = 1; @@ -86,12 +83,10 @@ void laset_band(int m, int n, int k, NDRange local(threads, 1); NDRange global(threads * groups, 1); - auto lasetBandOp = KernelFunctor(*setKernels[device]); + auto lasetBandOp = KernelFunctor(*entry.ker); - lasetBandOp(EnqueueArgs(getQueue(), global, local), - m, n, offdiag, diag, dA, dA_offset, ldda); + lasetBandOp(EnqueueArgs(getQueue(), global, local), m, n, offdiag, diag, dA, dA_offset, ldda); } #endif - } } diff --git a/src/backend/opencl/kernel/laswp.hpp b/src/backend/opencl/kernel/laswp.hpp index 99eb4096ed..9d6b486c3b 100644 --- a/src/backend/opencl/kernel/laswp.hpp +++ b/src/backend/opencl/kernel/laswp.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -27,13 +26,10 @@ using cl::EnqueueArgs; using cl::NDRange; using std::string; - namespace opencl { - namespace kernel { - static const int NTHREADS = 256; static const int MAX_PIVOTS = 32; @@ -42,35 +38,31 @@ typedef struct { int ipiv[MAX_PIVOTS]; } zlaswp_params_t; - template -void laswp(int n, cl_mem in, size_t offset, int ldda, - int k1, int k2, const int *ipiv, int inci) +void laswp(int n, cl_mem in, size_t offset, int ldda, int k1, int k2, const int *ipiv, int inci) { - - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map swpProgs; - static std::map swpKernels; + std::string refName = std::string("laswp_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once(compileFlags[device], [device] () { + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D MAX_PIVOTS=" << MAX_PIVOTS; - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D MAX_PIVOTS=" << MAX_PIVOTS; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + const char* ker_strs[] = {laswp_cl}; + const int ker_lens[] = {laswp_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "laswp"); - cl::Program prog; - buildProgram(prog, laswp_cl, laswp_cl_len, options.str()); - swpProgs[device] = new Program(prog); - - swpKernels[device] = new Kernel(*swpProgs[device], "laswp"); - }); + addKernelToCache(device, refName, entry); + } int groups = divup(n, NTHREADS); NDRange local(NTHREADS); @@ -80,26 +72,20 @@ void laswp(int n, cl_mem in, size_t offset, int ldda, //retain the cl_mem object during cl::Buffer creation cl::Buffer inObj(in, true); - auto laswpOp = KernelFunctor(*swpKernels[device]); + auto laswpOp = KernelFunctor(*entry.ker); for( int k = k1-1; k < k2; k += MAX_PIVOTS ) { - int pivots_left = k2-k; params.npivots = pivots_left > MAX_PIVOTS ? MAX_PIVOTS : pivots_left; - for( int j = 0; j < params.npivots; ++j ) { + for( int j = 0; j < params.npivots; ++j ) params.ipiv[j] = ipiv[(k+j)*inci] - k - 1; - } unsigned long long k_offset = offset + k*ldda; - laswpOp(EnqueueArgs(getQueue(), global, local), - n, inObj, k_offset, ldda, params); + laswpOp(EnqueueArgs(getQueue(), global, local), n, inObj, k_offset, ldda, params); } - } - } } diff --git a/src/backend/opencl/kernel/lookup.hpp b/src/backend/opencl/kernel/lookup.hpp index 995dffbadf..9528680366 100644 --- a/src/backend/opencl/kernel/lookup.hpp +++ b/src/backend/opencl/kernel/lookup.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,59 +27,57 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 32; static const int THREADS_Y = 8; template void lookup(Param out, const Param in, const Param indices, int nDims) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map aiProgs; - static std::map aiKernels; + std::string refName = std::string("lookupND_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(dim); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D in_t=" << dtype_traits::getName() - << " -D idx_t=" << dtype_traits::getName() - << " -D DIM=" <::value || - std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - Program prog; - buildProgram(prog, lookup_cl, lookup_cl_len, options.str()); - aiProgs[device] = new Program(prog); - aiKernels[device] = new Kernel(*aiProgs[device], "lookupND"); - }); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D in_t=" << dtype_traits::getName() + << " -D idx_t=" << dtype_traits::getName() + << " -D DIM=" <::value || + std::is_same::value || + std::is_same::value) { + options << " -D USE_DOUBLE"; + } + + const char* ker_strs[] = {lookup_cl}; + const int ker_lens[] = {lookup_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "lookupND"); + + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); int blk_x = divup(out.info.dims[0], THREADS_X); int blk_y = divup(out.info.dims[1], THREADS_Y); - NDRange global(blk_x * out.info.dims[2] * THREADS_X, - blk_y * out.info.dims[3] * THREADS_Y); + NDRange global(blk_x * out.info.dims[2] * THREADS_X, blk_y * out.info.dims[3] * THREADS_Y); - auto arrIdxOp = KernelFunctor(*aiKernels[device]); + auto arrIdxOp = KernelFunctor(*entry.ker); arrIdxOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, *indices.data, indices.info, blk_x, blk_y); + *out.data, out.info, *in.data, in.info, *indices.data, indices.info, blk_x, blk_y); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/lu_split.hpp b/src/backend/opencl/kernel/lu_split.hpp index 6784039614..858b931660 100644 --- a/src/backend/opencl/kernel/lu_split.hpp +++ b/src/backend/opencl/kernel/lu_split.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -31,10 +30,8 @@ using af::scalar_to_option; namespace opencl { - namespace kernel { - // Kernel Launch Config Values static const unsigned TX = 32; static const unsigned TY = 8; @@ -44,51 +41,46 @@ static const unsigned TILEY = 32; template void lu_split_launcher(Param lower, Param upper, const Param in) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map splitProgs; - static std::map splitKernels; + std::string refName = std::string("lu_split_kernel_") + + std::string(dtype_traits::getName()) + + std::to_string(same_dims); int device = getActiveDeviceId(); - - std::call_once(compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D same_dims=" << same_dims - << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")" - << " -D ONE=(T)(" << scalar_to_option(scalar(1)) << ")"; - - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - cl::Program prog; - buildProgram(prog, lu_split_cl, lu_split_cl_len, options.str()); - splitProgs[device] = new Program(prog); - - splitKernels[device] = new Kernel(*splitProgs[device], "lu_split_kernel"); - }); - + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D same_dims=" << same_dims + << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")" + << " -D ONE=(T)(" << scalar_to_option(scalar(1)) << ")"; + + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {lu_split_cl}; + const int ker_lens[] = {lu_split_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "lu_split_kernel"); + + addKernelToCache(device, refName, entry); + } NDRange local(TX, TY); int groups_x = divup(in.info.dims[0], TILEX); int groups_y = divup(in.info.dims[1], TILEY); - NDRange global(groups_x * local[0] * in.info.dims[2], - groups_y * local[1] * in.info.dims[3]); + NDRange global(groups_x * local[0] * in.info.dims[2], groups_y * local[1] * in.info.dims[3]); - auto lu_split_op = KernelFunctor (*splitKernels[device]); + auto lu_split_op = KernelFunctor (*entry.ker); lu_split_op(EnqueueArgs(getQueue(), global, local), - *lower.data, lower.info, - *upper.data, upper.info, - *in.data, in.info, - groups_x, groups_y); + *lower.data, lower.info, *upper.data, upper.info, + *in.data, in.info, groups_x, groups_y); CL_DEBUG_FINISH(getQueue()); } @@ -106,7 +98,5 @@ void lu_split(Param lower, Param upper, const Param in) lu_split_launcher(lower, upper, in); } } - } - } diff --git a/src/backend/opencl/kernel/match_template.hpp b/src/backend/opencl/kernel/match_template.hpp index 4922abb784..4cd5965371 100644 --- a/src/backend/opencl/kernel/match_template.hpp +++ b/src/backend/opencl/kernel/match_template.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,46 +27,49 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; template void matchTemplate(Param out, const Param srch, const Param tmplt) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map mtProgs; - static std::map mtKernels; + std::string refName = std::string("matchTemplate_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(mType) + std::to_string(needMean); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D inType=" << dtype_traits::getName() - << " -D outType=" << dtype_traits::getName() - << " -D MATCH_T=" << mType - << " -D NEEDMEAN="<< needMean - << " -D AF_SAD=" << AF_SAD - << " -D AF_ZSAD=" << AF_ZSAD - << " -D AF_LSAD=" << AF_LSAD - << " -D AF_SSD=" << AF_SSD - << " -D AF_ZSSD=" << AF_ZSSD - << " -D AF_LSSD=" << AF_LSSD - << " -D AF_NCC=" << AF_NCC - << " -D AF_ZNCC=" << AF_ZNCC - << " -D AF_SHD=" << AF_SHD; - if (std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, matchTemplate_cl, matchTemplate_cl_len, options.str()); - mtProgs[device] = new Program(prog); - mtKernels[device] = new Kernel(*mtProgs[device], "matchTemplate"); - }); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D inType=" << dtype_traits::getName() + << " -D outType=" << dtype_traits::getName() + << " -D MATCH_T=" << mType + << " -D NEEDMEAN="<< needMean + << " -D AF_SAD=" << AF_SAD + << " -D AF_ZSAD=" << AF_ZSAD + << " -D AF_LSAD=" << AF_LSAD + << " -D AF_SSD=" << AF_SSD + << " -D AF_ZSSD=" << AF_ZSSD + << " -D AF_LSSD=" << AF_LSSD + << " -D AF_NCC=" << AF_NCC + << " -D AF_ZNCC=" << AF_ZNCC + << " -D AF_SHD=" << AF_SHD; + if (std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {matchTemplate_cl}; + const int ker_lens[] = {matchTemplate_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "matchTemplate"); + + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); @@ -76,17 +78,13 @@ void matchTemplate(Param out, const Param srch, const Param tmplt) NDRange global(blk_x * srch.info.dims[2] * THREADS_X, blk_y * srch.info.dims[3] * THREADS_Y); - auto matchImgOp = KernelFunctor (*mtKernels[device]); + auto matchImgOp = KernelFunctor (*entry.ker); matchImgOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *srch.data, srch.info, *tmplt.data, tmplt.info, blk_x, blk_y); + *out.data, out.info, *srch.data, srch.info, *tmplt.data, tmplt.info, blk_x, blk_y); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/meanshift.hpp b/src/backend/opencl/kernel/meanshift.hpp index ddab5ed330..cb82021c3d 100644 --- a/src/backend/opencl/kernel/meanshift.hpp +++ b/src/backend/opencl/kernel/meanshift.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -30,43 +29,39 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; template void meanshift(Param out, const Param in, float s_sigma, float c_sigma, uint iter) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map msProgs; - static std::map msKernels; + std::string refName = std::string("meanshift_") + + std::string(dtype_traits::getName()) + std::to_string(is_color); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D MAX_CHANNELS=" << (is_color ? 3 : 1); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {meanshift_cl}; + const int ker_lens[] = {meanshift_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "meanshift"); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D MAX_CHANNELS=" << (is_color ? 3 : 1); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, meanshift_cl, meanshift_cl_len, options.str()); - msProgs[device] = new Program(prog); - msKernels[device] = new Kernel(*msProgs[device], "meanshift"); - }); - - auto meanshiftOp = KernelFunctor(*msKernels[device]); + addKernelToCache(device, refName, entry); + } + + auto meanshiftOp = KernelFunctor(*entry.ker); NDRange local(THREADS_X, THREADS_Y); @@ -92,7 +87,5 @@ void meanshift(Param out, const Param in, float s_sigma, float c_sigma, uint ite CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/medfilt.hpp b/src/backend/opencl/kernel/medfilt.hpp index 493071d7e9..3a7a471825 100644 --- a/src/backend/opencl/kernel/medfilt.hpp +++ b/src/backend/opencl/kernel/medfilt.hpp @@ -13,8 +13,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -29,10 +28,8 @@ using std::string; namespace opencl { - namespace kernel { - static const int MAX_MEDFILTER2_LEN = 15; static const int MAX_MEDFILTER1_LEN = 121; @@ -42,50 +39,47 @@ static const int THREADS_Y = 16; template void medfilt1(Param out, const Param in, unsigned w_wid) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map mfProgs; - static std::map mfKernels; + std::string refName = std::string("medfilt1_") + + std::string(dtype_traits::getName()) + std::to_string(pad); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device, w_wid] () { - - const int ARR_SIZE = (w_wid-w_wid/2) + 1; - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D pad="<< pad - << " -D AF_PAD_ZERO="<< AF_PAD_ZERO - << " -D AF_PAD_SYM="<< AF_PAD_SYM - << " -D ARR_SIZE="<< ARR_SIZE - << " -D w_wid=" << w_wid; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, medfilt1_cl, medfilt1_cl_len, options.str()); - mfProgs[device] = new Program(prog); - mfKernels[device] = new Kernel(*mfProgs[device], "medfilt1"); - }); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + const int ARR_SIZE = (w_wid-w_wid/2) + 1; + + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D pad="<< pad + << " -D AF_PAD_ZERO="<< AF_PAD_ZERO + << " -D AF_PAD_SYM="<< AF_PAD_SYM + << " -D ARR_SIZE="<< ARR_SIZE + << " -D w_wid=" << w_wid; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {medfilt1_cl}; + const int ker_lens[] = {medfilt1_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "medfilt1"); + + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, 1, 1); int blk_x = divup(in.info.dims[0], THREADS_X); - NDRange global(blk_x * in.info.dims[1] * THREADS_X, - in.info.dims[2], - in.info.dims[3]); + NDRange global(blk_x * in.info.dims[1] * THREADS_X, in.info.dims[2], in.info.dims[3]); - auto medfiltOp = KernelFunctor (*mfKernels[device]); + auto medfiltOp = KernelFunctor (*entry.ker); size_t loc_size = (THREADS_X+w_wid-1)*sizeof(T); medfiltOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, cl::Local(loc_size), blk_x); + *out.data, out.info, *in.data, in.info, cl::Local(loc_size), blk_x); CL_DEBUG_FINISH(getQueue()); } @@ -93,55 +87,53 @@ void medfilt1(Param out, const Param in, unsigned w_wid) template void medfilt2(Param out, const Param in) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map mfProgs; - static std::map mfKernels; + std::string refName = std::string("medfilt2_") + + std::string(dtype_traits::getName()) + + std::to_string(pad) + std::to_string(w_len) + std::to_string(w_wid); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - - const int ARR_SIZE = w_len * (w_wid-w_wid/2); - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D pad="<< pad - << " -D AF_PAD_ZERO="<< AF_PAD_ZERO - << " -D AF_PAD_SYM="<< AF_PAD_SYM - << " -D ARR_SIZE="<< ARR_SIZE - << " -D w_len="<< w_len - << " -D w_wid=" << w_wid; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, medfilt2_cl, medfilt2_cl_len, options.str()); - mfProgs[device] = new Program(prog); - mfKernels[device] = new Kernel(*mfProgs[device], "medfilt2"); - }); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + const int ARR_SIZE = w_len * (w_wid-w_wid/2); + + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D pad="<< pad + << " -D AF_PAD_ZERO="<< AF_PAD_ZERO + << " -D AF_PAD_SYM="<< AF_PAD_SYM + << " -D ARR_SIZE="<< ARR_SIZE + << " -D w_len="<< w_len + << " -D w_wid=" << w_wid; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {medfilt2_cl}; + const int ker_lens[] = {medfilt2_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "medfilt2"); + + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); int blk_x = divup(in.info.dims[0], THREADS_X); int blk_y = divup(in.info.dims[1], THREADS_Y); - NDRange global(blk_x * in.info.dims[2] * THREADS_X, - blk_y * in.info.dims[3] * THREADS_Y); + NDRange global(blk_x * in.info.dims[2] * THREADS_X, blk_y * in.info.dims[3] * THREADS_Y); - auto medfiltOp = KernelFunctor (*mfKernels[device]); + auto medfiltOp = KernelFunctor (*entry.ker); size_t loc_size = (THREADS_X+w_len-1)*(THREADS_Y+w_wid-1)*sizeof(T); medfiltOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, cl::Local(loc_size), blk_x, blk_y); + *out.data, out.info, *in.data, in.info, cl::Local(loc_size), blk_x, blk_y); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/memcopy.hpp b/src/backend/opencl/kernel/memcopy.hpp index 7b413b0a10..6ee1733ecb 100644 --- a/src/backend/opencl/kernel/memcopy.hpp +++ b/src/backend/opencl/kernel/memcopy.hpp @@ -14,8 +14,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -29,135 +29,135 @@ using std::string; namespace opencl { - namespace kernel { +typedef struct +{ + dim_t dim[4]; +} dims_t; + +static const uint DIM0 = 32; +static const uint DIM1 = 8; + +template +void memcopy(cl::Buffer out, const dim_t *ostrides, + const cl::Buffer in, const dim_t *idims, + const dim_t *istrides, int offset, uint ndims) +{ + std::string refName = std::string("memcopy_") + std::string(dtype_traits::getName()); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; - typedef struct - { - dim_t dim[4]; - } dims_t; - - static const uint DIM0 = 32; - static const uint DIM1 = 8; - - template - void memcopy(cl::Buffer out, const dim_t *ostrides, - const cl::Buffer in, const dim_t *idims, - const dim_t *istrides, int offset, uint ndims) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map cpyProgs; - static std::map cpyKernels; - - int device = getActiveDeviceId(); - - std::call_once(compileFlags[device], [&]() { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, memcopy_cl, memcopy_cl_len, options.str()); - cpyProgs[device] = new Program(prog); - cpyKernels[device] = new Kernel(*cpyProgs[device], "memcopy_kernel"); - }); - - dims_t _ostrides = {{ostrides[0], ostrides[1], ostrides[2], ostrides[3]}}; - dims_t _istrides = {{istrides[0], istrides[1], istrides[2], istrides[3]}}; - dims_t _idims = {{idims[0], idims[1], idims[2], idims[3]}}; - - size_t local_size[2] = {DIM0, DIM1}; - if (ndims == 1) { - local_size[0] *= local_size[1]; - local_size[1] = 1; - } - - int groups_0 = divup(idims[0], local_size[0]); - int groups_1 = divup(idims[1], local_size[1]); - - NDRange local(local_size[0], local_size[1]); - NDRange global(groups_0 * idims[2] * local_size[0], - groups_1 * idims[3] * local_size[1]); - - auto memcopy_kernel = KernelFunctor< Buffer, dims_t, - Buffer, dims_t, - dims_t, int, - int, int >(*cpyKernels[device]); - - memcopy_kernel(EnqueueArgs(getQueue(), global, local), - out, _ostrides, in, _idims, _istrides, offset, groups_0, groups_1); - CL_DEBUG_FINISH(getQueue()); + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {memcopy_cl}; + const int ker_lens[] = {memcopy_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "memcopy_kernel"); + + addKernelToCache(device, refName, entry); } - template - void copy(Param dst, const Param src, int ndims, outType default_value, double factor) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map cpyProgs; - static std::map cpyKernels; - - int device = getActiveDeviceId(); - - std::call_once(compileFlags[device], [&]() { - - std::ostringstream options; - options << " -D inType=" << dtype_traits::getName() - << " -D outType=" << dtype_traits::getName() - << " -D inType_" << dtype_traits::getName() - << " -D outType_" << dtype_traits::getName() - << " -D SAME_DIMS=" << same_dims; - if (std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - Program prog; - buildProgram(prog, copy_cl, copy_cl_len, options.str()); - cpyProgs[device] = new Program(prog); - cpyKernels[device] = new Kernel(*cpyProgs[device], "copy"); - }); - - NDRange local(DIM0, DIM1); - size_t local_size[] = {DIM0, DIM1}; + dims_t _ostrides = {{ostrides[0], ostrides[1], ostrides[2], ostrides[3]}}; + dims_t _istrides = {{istrides[0], istrides[1], istrides[2], istrides[3]}}; + dims_t _idims = {{idims[0], idims[1], idims[2], idims[3]}}; + size_t local_size[2] = {DIM0, DIM1}; + if (ndims == 1) { local_size[0] *= local_size[1]; - if (ndims == 1) { - local_size[1] = 1; - } - - int blk_x = divup(dst.info.dims[0], local_size[0]); - int blk_y = divup(dst.info.dims[1], local_size[1]); - - NDRange global(blk_x * dst.info.dims[2] * DIM0, - blk_y * dst.info.dims[3] * DIM1); - - dims_t trgt_dims; - if (same_dims) { - trgt_dims= {{dst.info.dims[0], dst.info.dims[1], dst.info.dims[2], dst.info.dims[3]}}; - } else { - dim_t trgt_l = std::min(dst.info.dims[3], src.info.dims[3]); - dim_t trgt_k = std::min(dst.info.dims[2], src.info.dims[2]); - dim_t trgt_j = std::min(dst.info.dims[1], src.info.dims[1]); - dim_t trgt_i = std::min(dst.info.dims[0], src.info.dims[0]); - trgt_dims= {{trgt_i, trgt_j, trgt_k, trgt_l}}; - } - - auto copyOp = KernelFunctor(*cpyKernels[device]); - - copyOp(EnqueueArgs(getQueue(), global, local), - *dst.data, dst.info, *src.data, src.info, - default_value, (float)factor, trgt_dims, blk_x, blk_y); - CL_DEBUG_FINISH(getQueue()); + local_size[1] = 1; } + int groups_0 = divup(idims[0], local_size[0]); + int groups_1 = divup(idims[1], local_size[1]); + + NDRange local(local_size[0], local_size[1]); + NDRange global(groups_0 * idims[2] * local_size[0], groups_1 * idims[3] * local_size[1]); + + auto memCpyOp = KernelFunctor< Buffer, dims_t, Buffer, dims_t, + dims_t, int, int, int >(*entry.ker); + + memCpyOp(EnqueueArgs(getQueue(), global, local), + out, _ostrides, in, _idims, _istrides, offset, groups_0, groups_1); + + CL_DEBUG_FINISH(getQueue()); } +template +void copy(Param dst, const Param src, int ndims, outType default_value, double factor) +{ + std::string refName = + std::string("copy_") + + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + + std::to_string(same_dims); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + + options << " -D inType=" << dtype_traits::getName() + << " -D outType=" << dtype_traits::getName() + << " -D inType_" << dtype_traits::getName() + << " -D outType_" << dtype_traits::getName() + << " -D SAME_DIMS=" << same_dims; + + if (std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {copy_cl}; + const int ker_lens[] = {copy_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "copy"); + + addKernelToCache(device, refName, entry); + } + + NDRange local(DIM0, DIM1); + size_t local_size[] = {DIM0, DIM1}; + + local_size[0] *= local_size[1]; + if (ndims == 1) { + local_size[1] = 1; + } + + int blk_x = divup(dst.info.dims[0], local_size[0]); + int blk_y = divup(dst.info.dims[1], local_size[1]); + + NDRange global(blk_x * dst.info.dims[2] * DIM0, blk_y * dst.info.dims[3] * DIM1); + + dims_t trgt_dims; + if (same_dims) { + trgt_dims= {{dst.info.dims[0], dst.info.dims[1], dst.info.dims[2], dst.info.dims[3]}}; + } else { + dim_t trgt_l = std::min(dst.info.dims[3], src.info.dims[3]); + dim_t trgt_k = std::min(dst.info.dims[2], src.info.dims[2]); + dim_t trgt_j = std::min(dst.info.dims[1], src.info.dims[1]); + dim_t trgt_i = std::min(dst.info.dims[0], src.info.dims[0]); + trgt_dims= {{trgt_i, trgt_j, trgt_k, trgt_l}}; + } + + auto copyOp = KernelFunctor< Buffer, KParam, Buffer, KParam, + outType, float, dims_t, int, int >(*entry.ker); + + copyOp(EnqueueArgs(getQueue(), global, local), + *dst.data, dst.info, *src.data, src.info, + default_value, (float)factor, trgt_dims, blk_x, blk_y); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/moments.hpp b/src/backend/opencl/kernel/moments.hpp index 10200e2aa7..a4fafe2b5e 100644 --- a/src/backend/opencl/kernel/moments.hpp +++ b/src/backend/opencl/kernel/moments.hpp @@ -49,10 +49,9 @@ namespace opencl std::to_string(out.info.dims[0]); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); + kc_entry_t entry = kernelCache(device, ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); options << " -D MOMENTS_SZ=" << out.info.dims[0]; @@ -69,9 +68,7 @@ namespace opencl entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "moments_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } diff --git a/src/backend/opencl/kernel/morph.hpp b/src/backend/opencl/kernel/morph.hpp index 3f129ffa9e..8c84033b56 100644 --- a/src/backend/opencl/kernel/morph.hpp +++ b/src/backend/opencl/kernel/morph.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -32,10 +31,8 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; @@ -44,47 +41,50 @@ static const int CUBE_Y = 8; static const int CUBE_Z = 4; template -void morph(Param out, - const Param in, - const Param mask) +std::string generateOptionsString() { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map morProgs; - static std::map morKernels; + ToNumStr toNumStr; + T init = isDilation ? Binary().init() : Binary().init(); + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D isDilation="<< isDilation + << " -D init=" << toNumStr(init) + << " -D windLen=" << windLen; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + return options.str(); +} - int device = getActiveDeviceId(); +template +void morph(Param out, const Param in, const Param mask) +{ + std::string refName = std::string("morph_") + + std::string(dtype_traits::getName()) + + std::to_string(isDilation) + std::to_string(windLen); - std::call_once( compileFlags[device], [device] () { - ToNumStr toNumStr; - T init = isDilation ? Binary().init() : Binary().init(); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D isDilation="<< isDilation - << " -D init=" << toNumStr(init) - << " -D windLen=" << windLen; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, morph_cl, morph_cl_len, options.str()); - morProgs[device] = new Program(prog); - morKernels[device] = new Kernel(*morProgs[device], "morph"); - }); - - auto morphOp = KernelFunctor(*morKernels[device]); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::string options = generateOptionsString(); + const char* ker_strs[] = {morph_cl}; + const int ker_lens[] = {morph_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "morph"); + addKernelToCache(device, refName, entry); + } + + auto morphOp = KernelFunctor< Buffer, KParam, Buffer, KParam, Buffer, cl::LocalSpaceArg, + int, int >(*entry.ker); NDRange local(THREADS_X, THREADS_Y); int blk_x = divup(in.info.dims[0], THREADS_X); int blk_y = divup(in.info.dims[1], THREADS_Y); // launch batch * blk_x blocks along x dimension - NDRange global(blk_x * THREADS_X * in.info.dims[2], - blk_y * THREADS_Y * in.info.dims[3]); + NDRange global(blk_x * THREADS_X * in.info.dims[2], blk_y * THREADS_Y * in.info.dims[3]); // copy mask/filter to constant memory cl_int se_size = sizeof(T)*windLen*windLen; @@ -111,34 +111,26 @@ void morph3d(Param out, const Param in, const Param mask) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map morProgs; - static std::map morKernels; + std::string refName = std::string("morph3d_") + + std::string(dtype_traits::getName()) + + std::to_string(isDilation) + std::to_string(windLen); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - ToNumStr toNumStr; - T init = isDilation ? Binary().init() : Binary().init(); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D isDilation="<< isDilation - << " -D init=" << toNumStr(init) - << " -D windLen=" << windLen; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, morph_cl, morph_cl_len, options.str()); - morProgs[device] = new Program(prog); - morKernels[device] = new Kernel(*morProgs[device], "morph3d"); - }); - - auto morphOp = KernelFunctor(*morKernels[device]); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::string options = generateOptionsString(); + const char* ker_strs[] = {morph_cl}; + const int ker_lens[] = {morph_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "morph3d"); + addKernelToCache(device, refName, entry); + } + + auto morphOp = KernelFunctor< Buffer, KParam, Buffer, KParam, Buffer, + cl::LocalSpaceArg, int >(*entry.ker); NDRange local(CUBE_X, CUBE_Y, CUBE_Z); @@ -146,9 +138,7 @@ void morph3d(Param out, int blk_y = divup(in.info.dims[1], CUBE_Y); int blk_z = divup(in.info.dims[2], CUBE_Z); // launch batch * blk_x blocks along x dimension - NDRange global(blk_x * CUBE_X * in.info.dims[3], - blk_y * CUBE_Y, - blk_z * CUBE_Z); + NDRange global(blk_x * CUBE_X * in.info.dims[3], blk_y * CUBE_Y, blk_z * CUBE_Z); // copy mask/filter to constant memory cl_int se_size = sizeof(T)*windLen*windLen*windLen; @@ -169,7 +159,5 @@ void morph3d(Param out, bufferFree(mBuff); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/nearest_neighbour.hpp b/src/backend/opencl/kernel/nearest_neighbour.hpp index 328708cfe9..7280508a63 100644 --- a/src/backend/opencl/kernel/nearest_neighbour.hpp +++ b/src/backend/opencl/kernel/nearest_neighbour.hpp @@ -61,49 +61,47 @@ void nearest_neighbour(Param idx, std::to_string(unroll_len); int device = getActiveDeviceId(); - kc_t::iterator cache_idx = kernelCaches[device].find(ref_name); - - kc_entry_t entry; - if (cache_idx == kernelCaches[device].end()) { - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D To=" << dtype_traits::getName() - << " -D THREADS=" << THREADS - << " -D FEAT_LEN=" << unroll_len; - - switch(dist_type) { - case AF_SAD: options <<" -D DISTOP=_sad_"; break; - case AF_SSD: options <<" -D DISTOP=_ssd_"; break; - case AF_SHD: options <<" -D DISTOP=_shd_ -D __SHD__"; - break; - default: break; - } - - if (std::is_same::value || + + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { + + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D To=" << dtype_traits::getName() + << " -D THREADS=" << THREADS + << " -D FEAT_LEN=" << unroll_len; + + switch(dist_type) { + case AF_SAD: options <<" -D DISTOP=_sad_"; break; + case AF_SSD: options <<" -D DISTOP=_ssd_"; break; + case AF_SHD: options <<" -D DISTOP=_shd_ -D __SHD__"; + break; + default: break; + } + + if (std::is_same::value || std::is_same::value) { - options << " -D USE_DOUBLE"; - } + options << " -D USE_DOUBLE"; + } - if (use_lmem) - options << " -D USE_LOCAL_MEM"; + if (use_lmem) + options << " -D USE_LOCAL_MEM"; - cl::Program prog; - buildProgram(prog, - nearest_neighbour_cl, - nearest_neighbour_cl_len, - options.str()); + cl::Program prog; + buildProgram(prog, + nearest_neighbour_cl, + nearest_neighbour_cl_len, + options.str()); - entry.prog = new Program(prog); - entry.ker = new Kernel[3]; + entry.prog = new Program(prog); + entry.ker = new Kernel[3]; - entry.ker[0] = Kernel(*entry.prog, "nearest_neighbour_unroll"); - entry.ker[1] = Kernel(*entry.prog, "nearest_neighbour"); - entry.ker[2] = Kernel(*entry.prog, "select_matches"); + entry.ker[0] = Kernel(*entry.prog, "nearest_neighbour_unroll"); + entry.ker[1] = Kernel(*entry.prog, "nearest_neighbour"); + entry.ker[2] = Kernel(*entry.prog, "select_matches"); - kernelCaches[device][ref_name] = entry; - } else { - entry = cache_idx->second; + addKernelToCache(device, ref_name, entry); } const dim_t sample_dim = (dist_dim == 0) ? 1 : 0; diff --git a/src/backend/opencl/kernel/orb.hpp b/src/backend/opencl/kernel/orb.hpp index 5c312db5e1..cd3da4ed60 100644 --- a/src/backend/opencl/kernel/orb.hpp +++ b/src/backend/opencl/kernel/orb.hpp @@ -20,6 +20,7 @@ #include #include #include +#include using cl::Buffer; using cl::Program; @@ -50,10 +51,8 @@ using std::vector; namespace opencl { - namespace kernel { - static const int ORB_THREADS = 256; static const int ORB_THREADS_X = 16; static const int ORB_THREADS_Y = 16; @@ -86,50 +85,64 @@ void gaussian1D(T* out, const int dim, double sigma=0.0) out[k] /= sum; } -template -void orb(unsigned* out_feat, - Param& x_out, - Param& y_out, - Param& score_out, - Param& ori_out, - Param& size_out, - Param& desc_out, - Param image, - const float fast_thr, - const unsigned max_feat, - const float scl_fctr, - const unsigned levels, - const bool blur_img) +template +std::tuple +getOrbKernels() { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map orbProgs; - static std::map hrKernel; - static std::map kfKernel; - static std::map caKernel; - static std::map eoKernel; + static const std::string kernelNames[4] = + {"harris_response", "keep_features", "centroid_angle", "extract_orb"}; + + kc_entry_t entries[4]; int device = getActiveDeviceId(); - std::call_once( compileFlags[device], [device] () { + std::string checkName = kernelNames[0] + std::string("_") + std::string(dtype_traits::getName()); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D BLOCK_SIZE=" << ORB_THREADS_X; + entries[0] = kernelCache(device, checkName); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + if (entries[0].prog==0 && entries[0].ker==0) + { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D BLOCK_SIZE=" << ORB_THREADS_X; + + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {orb_cl}; + const int ker_lens[] = {orb_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + + for (int i=0; i<4; ++i) + { + entries[i].prog = new Program(prog); + entries[i].ker = new Kernel(*entries[i].prog, kernelNames[i].c_str()); - cl::Program prog; - buildProgram(prog, orb_cl, orb_cl_len, options.str()); - orbProgs[device] = new Program(prog); + std::string name = kernelNames[i] + + std::string("_") + std::string(dtype_traits::getName()); - hrKernel[device] = new Kernel(*orbProgs[device], "harris_response"); - kfKernel[device] = new Kernel(*orbProgs[device], "keep_features"); - caKernel[device] = new Kernel(*orbProgs[device], "centroid_angle"); - eoKernel[device] = new Kernel(*orbProgs[device], "extract_orb"); - }); + addKernelToCache(device, name, entries[i]); + } + } else { + for (int i=1; i<4; ++i) { + std::string name = kernelNames[i] + + std::string("_") + std::string(dtype_traits::getName()); + + entries[i] = kernelCache(device, name); + } + } + + return std::make_tuple(entries[0].ker, entries[1].ker, entries[2].ker, entries[3].ker); +} + +template +void orb(unsigned* out_feat, Param& x_out, Param& y_out, Param& score_out, + Param& ori_out, Param& size_out, Param& desc_out, Param image, + const float fast_thr, const unsigned max_feat, const float scl_fctr, + const unsigned levels, const bool blur_img) +{ + auto kernels = getOrbKernels(); unsigned patch_size = REF_PAT_SIZE; @@ -253,7 +266,7 @@ void orb(unsigned* out_feat, auto hrOp = KernelFunctor (*hrKernel[device]); + const unsigned, const float, const unsigned> (*std::get<0>(kernels)); hrOp(EnqueueArgs(getQueue(), global, local), *d_x_harris, *d_y_harris, *d_score_harris, @@ -321,7 +334,7 @@ void orb(unsigned* out_feat, auto kfOp = KernelFunctor (*kfKernel[device]); + const unsigned> (*std::get<1>(kernels)); kfOp(EnqueueArgs(getQueue(), global_keep, local_keep), *d_x_lvl, *d_y_lvl, *d_score_lvl, @@ -344,7 +357,7 @@ void orb(unsigned* out_feat, auto caOp = KernelFunctor (*caKernel[device]); + const unsigned> (*std::get<2>(kernels)); caOp(EnqueueArgs(getQueue(), global_centroid, local_centroid), *d_x_lvl, *d_y_lvl, *d_ori_lvl, @@ -396,7 +409,7 @@ void orb(unsigned* out_feat, auto eoOp = KernelFunctor (*eoKernel[device]); + const float, const unsigned> (*std::get<3>(kernels)); if (blur_img) { eoOp(EnqueueArgs(getQueue(), global_centroid, local_centroid), @@ -514,9 +527,7 @@ void orb(unsigned* out_feat, // Sets number of output features *out_feat = total_feat; } - } //namespace kernel - } //namespace opencl #if defined(__clang__) diff --git a/src/backend/opencl/kernel/random_engine.hpp b/src/backend/opencl/kernel/random_engine.hpp index 8829b15a68..827f56e552 100644 --- a/src/backend/opencl/kernel/random_engine.hpp +++ b/src/backend/opencl/kernel/random_engine.hpp @@ -83,9 +83,10 @@ namespace opencl "_" + string(dtype_traits::getName()) + "_" + to_string(kerIdx); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName() << " -D THREADS=" << THREADS @@ -104,9 +105,8 @@ namespace opencl buildProgram(prog, 2, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "generate"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } return *entry.ker; @@ -121,17 +121,17 @@ namespace opencl int ker_len = random_engine_mersenne_init_cl_len; string ref_name = "mersenne_init"; int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { std::string emptyOptionString; cl::Program prog; buildProgram(prog, 1, &ker_str, &ker_len, emptyOptionString); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "initState"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } return *entry.ker; diff --git a/src/backend/opencl/kernel/range.hpp b/src/backend/opencl/kernel/range.hpp index 0ea609f558..a1bb664b92 100644 --- a/src/backend/opencl/kernel/range.hpp +++ b/src/backend/opencl/kernel/range.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,51 +27,51 @@ using std::string; namespace opencl { - namespace kernel - { - // Kernel Launch Config Values - static const int RANGE_TX = 32; - static const int RANGE_TY = 8; - static const int RANGE_TILEX = 512; - static const int RANGE_TILEY = 32; +namespace kernel +{ +// Kernel Launch Config Values +static const int RANGE_TX = 32; +static const int RANGE_TY = 8; +static const int RANGE_TILEX = 512; +static const int RANGE_TILEY = 32; - template - void range(Param out, const int dim) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map rangeProgs; - static std::map rangeKernels; +template +void range(Param out, const int dim) +{ + std::string refName = std::string("range_kernel_") + std::string(dtype_traits::getName()); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, range_cl, range_cl_len, options.str()); - rangeProgs[device] = new Program(prog); - rangeKernels[device] = new Kernel(*rangeProgs[device], "range_kernel"); - }); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - auto rangeOp = KernelFunctor (*rangeKernels[device]); + const char* ker_strs[] = {range_cl}; + const int ker_lens[] = {range_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "range_kernel"); - NDRange local(RANGE_TX, RANGE_TY, 1); + addKernelToCache(device, refName, entry); + } - int blocksPerMatX = divup(out.info.dims[0], RANGE_TILEX); - int blocksPerMatY = divup(out.info.dims[1], RANGE_TILEY); - NDRange global(local[0] * blocksPerMatX * out.info.dims[2], - local[1] * blocksPerMatY * out.info.dims[3], - 1); + auto rangeOp = KernelFunctor< Buffer, const KParam, const int, const int, const int > (*entry.ker); - rangeOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, dim, blocksPerMatX, blocksPerMatY); + NDRange local(RANGE_TX, RANGE_TY, 1); - CL_DEBUG_FINISH(getQueue()); - } - } + int blocksPerMatX = divup(out.info.dims[0], RANGE_TILEX); + int blocksPerMatY = divup(out.info.dims[1], RANGE_TILEY); + NDRange global(local[0] * blocksPerMatX * out.info.dims[2], + local[1] * blocksPerMatY * out.info.dims[3], 1); + + rangeOp(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, dim, blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/reduce.hpp b/src/backend/opencl/kernel/reduce.hpp index 31038a3080..d9c67c90fe 100644 --- a/src/backend/opencl/kernel/reduce.hpp +++ b/src/backend/opencl/kernel/reduce.hpp @@ -61,10 +61,9 @@ namespace kernel std::to_string(threads_y); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); + kc_entry_t entry = kernelCache(device, ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { Binary reduce; ToNumStr toNumStr; @@ -92,9 +91,7 @@ namespace kernel entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "reduce_dim_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } NDRange local(THREADS_X, threads_y); @@ -179,10 +176,10 @@ namespace kernel std::to_string(threads_x); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { Binary reduce; ToNumStr toNumStr; @@ -209,9 +206,7 @@ namespace kernel entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "reduce_first_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } NDRange local(threads_x, THREADS_PER_GROUP / threads_x); diff --git a/src/backend/opencl/kernel/regions.hpp b/src/backend/opencl/kernel/regions.hpp index e381399342..35dd4bec52 100644 --- a/src/backend/opencl/kernel/regions.hpp +++ b/src/backend/opencl/kernel/regions.hpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #pragma GCC diagnostic push @@ -43,57 +43,86 @@ namespace compute = boost::compute; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; template -void regions(Param out, Param in) +std::tuple +getRegionsKernels() { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map regionsProgs; - static std::map ilKernel; - static std::map frKernel; - static std::map ueKernel; - - int device = getActiveDeviceId(); static const int block_dim = 16; static const int num_warps = 8; + static const unsigned NUM_KERNELS = 3; + static const std::string kernelNames[NUM_KERNELS] = + {"initial_label", "final_relabel", "update_equiv"}; - std::call_once( compileFlags[device], [device] () { - ToNumStr toNumStr; - std::ostringstream options; - if (full_conn) { - options << " -D T=" << dtype_traits::getName() - << " -D BLOCK_DIM=" << block_dim - << " -D NUM_WARPS=" << num_warps - << " -D N_PER_THREAD=" << n_per_thread - << " -D LIMIT_MAX=" << toNumStr(maxval()) - << " -D FULL_CONN"; - } - else { - options << " -D T=" << dtype_traits::getName() - << " -D BLOCK_DIM=" << block_dim - << " -D NUM_WARPS=" << num_warps - << " -D N_PER_THREAD=" << n_per_thread - << " -D LIMIT_MAX=" << toNumStr(maxval()); - } - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + kc_entry_t entries[NUM_KERNELS]; + + int device = getActiveDeviceId(); + + std::string checkName = kernelNames[0] + std::string("_") + + std::string(dtype_traits::getName()) + + std::to_string(full_conn) + std::to_string(n_per_thread); + + entries[0] = kernelCache(device, checkName); + + if (entries[0].prog==0 && entries[0].ker==0) + { + ToNumStr toNumStr; + std::ostringstream options; + if (full_conn) { + options << " -D T=" << dtype_traits::getName() + << " -D BLOCK_DIM=" << block_dim + << " -D NUM_WARPS=" << num_warps + << " -D N_PER_THREAD=" << n_per_thread + << " -D LIMIT_MAX=" << toNumStr(maxval()) + << " -D FULL_CONN"; + } + else { + options << " -D T=" << dtype_traits::getName() + << " -D BLOCK_DIM=" << block_dim + << " -D NUM_WARPS=" << num_warps + << " -D N_PER_THREAD=" << n_per_thread + << " -D LIMIT_MAX=" << toNumStr(maxval()); + } + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {regions_cl}; + const int ker_lens[] = {regions_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); - Program prog; - buildProgram(prog, regions_cl, regions_cl_len, options.str()); - regionsProgs[device] = new Program(prog); + for (unsigned i=0; i::getName()) + + std::to_string(full_conn) + std::to_string(n_per_thread); - ilKernel[device] = new Kernel(*regionsProgs[device], "initial_label"); - frKernel[device] = new Kernel(*regionsProgs[device], "final_relabel"); - ueKernel[device] = new Kernel(*regionsProgs[device], "update_equiv"); - }); + addKernelToCache(device, name, entries[i]); + } + } else { + for (unsigned i=1; i::getName()) + + std::to_string(full_conn) + std::to_string(n_per_thread); + + entries[i] = kernelCache(device, name); + } + } + + return std::make_tuple(entries[0].ker, entries[1].ker, entries[2].ker); +} + +template +void regions(Param out, Param in) +{ + auto kernels = getRegionsKernels(); const NDRange local(THREADS_X, THREADS_Y); @@ -102,11 +131,9 @@ void regions(Param out, Param in) const NDRange global(blk_x * THREADS_X, blk_y * THREADS_Y); - auto ilOp = KernelFunctor (*ilKernel[device]); + auto ilOp = KernelFunctor (*std::get<0>(kernels)); - ilOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info); + ilOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *in.data, in.info); CL_DEBUG_FINISH(getQueue()); @@ -117,8 +144,7 @@ void regions(Param out, Param in) h_continue = 0; getQueue().enqueueWriteBuffer(*d_continue, CL_TRUE, 0, sizeof(int), &h_continue); - auto ueOp = KernelFunctor (*ueKernel[device]); + auto ueOp = KernelFunctor (*std::get<2>(kernels)); ueOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *d_continue); @@ -149,7 +175,8 @@ void regions(Param out, Param in) // compute. //int num_bins = tmp[size - 1] + 1; T last_label; - clEnqueueReadBuffer(getQueue()(), tmp.get_buffer().get(), CL_TRUE, (size - 1) * sizeof(T), sizeof(T), &last_label, 0, NULL, NULL); + clEnqueueReadBuffer(getQueue()(), tmp.get_buffer().get(), CL_TRUE, + (size - 1) * sizeof(T), sizeof(T), &last_label, 0, NULL, NULL); int num_bins = (int)last_label + 1; Buffer labels(getContext(), CL_MEM_READ_WRITE, num_bins * sizeof(T)); @@ -176,7 +203,6 @@ void regions(Param out, Param in) start = i + 1; } } - return start; }); @@ -186,33 +212,22 @@ void regions(Param out, Param in) }); compute::transform(search_begin, search_begin + num_bins, - labels_begin, - upper_bound_closure, - c_queue); + labels_begin, upper_bound_closure, c_queue); compute::adjacent_difference(labels_begin, labels_end, labels_begin, c_queue); // Perform the scan -- this can computes the correct labels for each // component - compute::transform(labels_begin, labels_end, - labels_begin, - clamp_to_one, - c_queue); - compute::exclusive_scan(labels_begin, - labels_end, - labels_begin, - c_queue); + compute::transform(labels_begin, labels_end, labels_begin, clamp_to_one, c_queue); + + compute::exclusive_scan(labels_begin, labels_end, labels_begin, c_queue); // Apply the correct labels to the equivalency map - auto frOp = KernelFunctor (*frKernel[device]); + auto frOp = KernelFunctor (*std::get<1>(kernels)); //Buffer labels_buf(tmp.get_buffer().get()); - frOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, labels); + frOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *in.data, in.info, labels); + CL_DEBUG_FINISH(getQueue()); } - } //namespace kernel - } //namespace opencl diff --git a/src/backend/opencl/kernel/reorder.hpp b/src/backend/opencl/kernel/reorder.hpp index 5bd7690e6d..87a9e725f3 100644 --- a/src/backend/opencl/kernel/reorder.hpp +++ b/src/backend/opencl/kernel/reorder.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,54 +27,55 @@ using std::string; namespace opencl { - namespace kernel - { - // Kernel Launch Config Values - static const int TX = 32; - static const int TY = 8; - static const int TILEX = 512; - static const int TILEY = 32; +namespace kernel +{ +// Kernel Launch Config Values +static const int TX = 32; +static const int TY = 8; +static const int TILEX = 512; +static const int TILEY = 32; - template - void reorder(Param out, const Param in, const dim_t *rdims) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map reorderProgs; - static std::map reorderKernels; +template +void reorder(Param out, const Param in, const dim_t *rdims) +{ + std::string refName = std::string("reorder_kernel_") + std::string(dtype_traits::getName()); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, reorder_cl, reorder_cl_len, options.str()); - reorderProgs[device] = new Program(prog); - reorderKernels[device] = new Kernel(*reorderProgs[device], "reorder_kernel"); - }); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - auto reorderOp = KernelFunctor (*reorderKernels[device]); + const char* ker_strs[] = {reorder_cl}; + const int ker_lens[] = {reorder_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "reorder_kernel"); - NDRange local(TX, TY, 1); + addKernelToCache(device, refName, entry); + } - int blocksPerMatX = divup(out.info.dims[0], TILEX); - int blocksPerMatY = divup(out.info.dims[1], TILEY); - NDRange global(local[0] * blocksPerMatX * out.info.dims[2], - local[1] * blocksPerMatY * out.info.dims[3], - 1); + auto reorderOp = KernelFunctor< Buffer, const Buffer, const KParam, const KParam, + const int, const int, const int, const int, + const int, const int >(*entry.ker); - reorderOp(EnqueueArgs(getQueue(), global, local), - *out.data, *in.data, out.info, in.info, - rdims[0], rdims[1], rdims[2], rdims[3], - blocksPerMatX, blocksPerMatY); + NDRange local(TX, TY, 1); - CL_DEBUG_FINISH(getQueue()); - } - } + int blocksPerMatX = divup(out.info.dims[0], TILEX); + int blocksPerMatY = divup(out.info.dims[1], TILEY); + NDRange global(local[0] * blocksPerMatX * out.info.dims[2], + local[1] * blocksPerMatY * out.info.dims[3], 1); + + reorderOp(EnqueueArgs(getQueue(), global, local), + *out.data, *in.data, out.info, in.info, + rdims[0], rdims[1], rdims[2], rdims[3], + blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/resize.hpp b/src/backend/opencl/kernel/resize.hpp index bb4216c6a9..c58f343e5f 100644 --- a/src/backend/opencl/kernel/resize.hpp +++ b/src/backend/opencl/kernel/resize.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,86 +27,84 @@ using std::string; namespace opencl { - namespace kernel - { - static const int RESIZE_TX = 16; - static const int RESIZE_TY = 16; - - using std::conditional; - using std::is_same; - template - using wtype_t = typename conditional::value, double, float>::type; - - template - using vtype_t = typename conditional::value, - T, wtype_t - >::type; - - template - void resize(Param out, const Param in) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map resizeProgs; - static std::map resizeKernels; - - int device = getActiveDeviceId(); - - typedef typename dtype_traits::base_type BT; - - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - options << " -D VT=" << dtype_traits>::getName(); - options << " -D WT=" << dtype_traits>::getName(); - - switch(method) { - case AF_INTERP_NEAREST: options <<" -D INTERP=NEAREST" ; break; - case AF_INTERP_BILINEAR: options <<" -D INTERP=BILINEAR"; break; - case AF_INTERP_LOWER: options <<" -D INTERP=LOWER" ; break; - default: break; - } - - if((af_dtype) dtype_traits::af_type == c32 || - (af_dtype) dtype_traits::af_type == c64) { - options << " -D CPLX=1"; - options << " -D TB=" << dtype_traits::getName(); - } else { - options << " -D CPLX=0"; - } - - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - Program prog; - buildProgram(prog, resize_cl, resize_cl_len, options.str()); - resizeProgs[device] = new Program(prog); - resizeKernels[device] = new Kernel(*resizeProgs[device], "resize_kernel"); - }); - - auto resizeOp = KernelFunctor - (*resizeKernels[device]); - - NDRange local(RESIZE_TX, RESIZE_TY, 1); - - int blocksPerMatX = divup(out.info.dims[0], local[0]); - int blocksPerMatY = divup(out.info.dims[1], local[1]); - NDRange global(local[0] * blocksPerMatX * in.info.dims[2], - local[1] * blocksPerMatY * in.info.dims[3], - 1); - - double xd = (double)in.info.dims[0] / (double)out.info.dims[0]; - double yd = (double)in.info.dims[1] / (double)out.info.dims[1]; - - float xf = (float)xd, yf = (float)yd; - - resizeOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, blocksPerMatX, blocksPerMatY, xf, yf); - - CL_DEBUG_FINISH(getQueue()); +namespace kernel +{ +static const int RESIZE_TX = 16; +static const int RESIZE_TY = 16; + +using std::conditional; +using std::is_same; +template +using wtype_t = typename conditional::value, double, float>::type; + +template +using vtype_t = typename conditional< is_complex::value, T, wtype_t >::type; + +template +void resize(Param out, const Param in) +{ + typedef typename dtype_traits::base_type BT; + + std::string refName = std::string("reorder_kernel_") + + std::string(dtype_traits::getName()) + + std::to_string(method); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + options << " -D VT=" << dtype_traits>::getName(); + options << " -D WT=" << dtype_traits>::getName(); + + switch(method) { + case AF_INTERP_NEAREST: options <<" -D INTERP=NEAREST" ; break; + case AF_INTERP_BILINEAR: options <<" -D INTERP=BILINEAR"; break; + case AF_INTERP_LOWER: options <<" -D INTERP=LOWER" ; break; + default: break; } + + if((af_dtype) dtype_traits::af_type == c32 || + (af_dtype) dtype_traits::af_type == c64) { + options << " -D CPLX=1"; + options << " -D TB=" << dtype_traits::getName(); + } else { + options << " -D CPLX=0"; + } + + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {resize_cl}; + const int ker_lens[] = {resize_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "resize_kernel"); + + addKernelToCache(device, refName, entry); } + + auto resizeOp = KernelFunctor< Buffer, const KParam, const Buffer, const KParam, + const int, const int, const float, const float > (*entry.ker); + + NDRange local(RESIZE_TX, RESIZE_TY, 1); + + int blocksPerMatX = divup(out.info.dims[0], local[0]); + int blocksPerMatY = divup(out.info.dims[1], local[1]); + NDRange global(local[0] * blocksPerMatX * in.info.dims[2], + local[1] * blocksPerMatY * in.info.dims[3], 1); + + double xd = (double)in.info.dims[0] / (double)out.info.dims[0]; + double yd = (double)in.info.dims[1] / (double)out.info.dims[1]; + + float xf = (float)xd, yf = (float)yd; + + resizeOp(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, *in.data, in.info, blocksPerMatX, blocksPerMatY, xf, yf); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/rotate.hpp b/src/backend/opencl/kernel/rotate.hpp index 521383524d..4f595b5ec4 100644 --- a/src/backend/opencl/kernel/rotate.hpp +++ b/src/backend/opencl/kernel/rotate.hpp @@ -13,8 +13,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -33,122 +32,119 @@ using std::string; namespace opencl { - namespace kernel - { - static const int TX = 16; - static const int TY = 16; - // Used for batching images - static const int TI = 4; - - typedef struct { - float tmat[6]; - } tmat_t; - - using std::conditional; - using std::is_same; - template - using wtype_t = typename conditional::value, double, float>::type; - - template - using vtype_t = typename conditional::value, - T, wtype_t - >::type; - - template - void rotate(Param out, const Param in, const float theta, af_interp_type method) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map rotateProgs; - static std::map rotateKernels; - - int device = getActiveDeviceId(); - typedef typename dtype_traits::base_type BT; - - std::call_once( compileFlags[device], [device] () { - ToNumStr toNumStr; - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - options << " -D ZERO=" << toNumStr(scalar(0)); - options << " -D InterpInTy=" << dtype_traits::getName(); - options << " -D InterpValTy=" << dtype_traits>::getName(); - options << " -D InterpPosTy=" << dtype_traits>::getName(); - - if((af_dtype) dtype_traits::af_type == c32 || - (af_dtype) dtype_traits::af_type == c64) { - options << " -D IS_CPLX=1"; - options << " -D TB=" << dtype_traits::getName(); - } else { - options << " -D IS_CPLX=0"; - } - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - options << " -D INTERP_ORDER=" << order; - addInterpEnumOptions(options); - - const char *ker_strs[] = {interp_cl, rotate_cl}; - const int ker_lens[] = {interp_cl_len, rotate_cl_len}; - Program prog; - buildProgram(prog, 2, ker_strs, ker_lens, options.str()); - rotateProgs[device] = new Program(prog); - rotateKernels[device] = new Kernel(*rotateProgs[device], "rotate_kernel"); - }); - - auto rotateOp = KernelFunctor(*rotateKernels[device]); - - const float c = cos(-theta), s = sin(-theta); - float tx, ty; - { - const float nx = 0.5 * (in.info.dims[0] - 1); - const float ny = 0.5 * (in.info.dims[1] - 1); - const float mx = 0.5 * (out.info.dims[0] - 1); - const float my = 0.5 * (out.info.dims[1] - 1); - const float sx = (mx * c + my *-s); - const float sy = (mx * s + my * c); - tx = -(sx - nx); - ty = -(sy - ny); - } - - // Rounding error. Anything more than 3 decimal points wont make a diff - tmat_t t; - t.tmat[0] = round( c * 1000) / 1000.0f; - t.tmat[1] = round(-s * 1000) / 1000.0f; - t.tmat[2] = round(tx * 1000) / 1000.0f; - t.tmat[3] = round( s * 1000) / 1000.0f; - t.tmat[4] = round( c * 1000) / 1000.0f; - t.tmat[5] = round(ty * 1000) / 1000.0f; - - - NDRange local(TX, TY, 1); - - int nimages = in.info.dims[2]; - int nbatches = in.info.dims[3]; - int global_x = local[0] * divup(out.info.dims[0], local[0]); - int global_y = local[1] * divup(out.info.dims[1], local[1]); - const int blocksXPerImage = global_x / local[0]; - const int blocksYPerImage = global_y / local[1]; - - if(nimages > TI) { - int tile_images = divup(nimages, TI); - nimages = TI; - global_x = global_x * tile_images; - } - global_y *= nbatches; - - NDRange global(global_x, global_y, 1); - - rotateOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, t, nimages, nbatches, - blocksXPerImage, blocksYPerImage, (int)method); - - CL_DEBUG_FINISH(getQueue()); +namespace kernel +{ +static const int TX = 16; +static const int TY = 16; +// Used for batching images +static const int TI = 4; + +typedef struct { + float tmat[6]; +} tmat_t; + +using std::conditional; +using std::is_same; +template +using wtype_t = typename conditional::value, double, float>::type; + +template +using vtype_t = typename conditional< is_complex::value, T, wtype_t >::type; + +template +void rotate(Param out, const Param in, const float theta, af_interp_type method) +{ + typedef typename dtype_traits::base_type BT; + + std::string refName = std::string("rotate_kernel_") + + std::string(dtype_traits::getName()) + + std::to_string(order); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + ToNumStr toNumStr; + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + options << " -D ZERO=" << toNumStr(scalar(0)); + options << " -D InterpInTy=" << dtype_traits::getName(); + options << " -D InterpValTy=" << dtype_traits>::getName(); + options << " -D InterpPosTy=" << dtype_traits>::getName(); + + if((af_dtype) dtype_traits::af_type == c32 || + (af_dtype) dtype_traits::af_type == c64) { + options << " -D IS_CPLX=1"; + options << " -D TB=" << dtype_traits::getName(); + } else { + options << " -D IS_CPLX=0"; } + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + options << " -D INTERP_ORDER=" << order; + addInterpEnumOptions(options); + + const char *ker_strs[] = {interp_cl, rotate_cl}; + const int ker_lens[] = {interp_cl_len, rotate_cl_len}; + Program prog; + buildProgram(prog, 2, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "rotate_kernel"); + + addKernelToCache(device, refName, entry); } + + auto rotateOp = KernelFunctor(*entry.ker); + + const float c = cos(-theta), s = sin(-theta); + float tx, ty; + { + const float nx = 0.5 * (in.info.dims[0] - 1); + const float ny = 0.5 * (in.info.dims[1] - 1); + const float mx = 0.5 * (out.info.dims[0] - 1); + const float my = 0.5 * (out.info.dims[1] - 1); + const float sx = (mx * c + my *-s); + const float sy = (mx * s + my * c); + tx = -(sx - nx); + ty = -(sy - ny); + } + + // Rounding error. Anything more than 3 decimal points wont make a diff + tmat_t t; + t.tmat[0] = round( c * 1000) / 1000.0f; + t.tmat[1] = round(-s * 1000) / 1000.0f; + t.tmat[2] = round(tx * 1000) / 1000.0f; + t.tmat[3] = round( s * 1000) / 1000.0f; + t.tmat[4] = round( c * 1000) / 1000.0f; + t.tmat[5] = round(ty * 1000) / 1000.0f; + + + NDRange local(TX, TY, 1); + + int nimages = in.info.dims[2]; + int nbatches = in.info.dims[3]; + int global_x = local[0] * divup(out.info.dims[0], local[0]); + int global_y = local[1] * divup(out.info.dims[1], local[1]); + const int blocksXPerImage = global_x / local[0]; + const int blocksYPerImage = global_y / local[1]; + + if(nimages > TI) { + int tile_images = divup(nimages, TI); + nimages = TI; + global_x = global_x * tile_images; + } + global_y *= nbatches; + + NDRange global(global_x, global_y, 1); + + rotateOp(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, *in.data, in.info, t, nimages, nbatches, + blocksXPerImage, blocksYPerImage, (int)method); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/scan_dim.hpp b/src/backend/opencl/kernel/scan_dim.hpp index 46ad2d1673..a48d271a5d 100644 --- a/src/backend/opencl/kernel/scan_dim.hpp +++ b/src/backend/opencl/kernel/scan_dim.hpp @@ -55,11 +55,10 @@ namespace kernel std::to_string(int(inclusive_scan)); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + if (entry.prog==0 && entry.ker==0) { Binary scan; ToNumStr toNumStr; @@ -91,10 +90,8 @@ namespace kernel entry.ker[0] = Kernel(*entry.prog, "scan_dim_kernel"); entry.ker[1] = Kernel(*entry.prog, "bcast_dim_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } return entry.ker[kerIdx]; diff --git a/src/backend/opencl/kernel/scan_dim_by_key_impl.hpp b/src/backend/opencl/kernel/scan_dim_by_key_impl.hpp index a8adbff7d3..217153d18c 100644 --- a/src/backend/opencl/kernel/scan_dim_by_key_impl.hpp +++ b/src/backend/opencl/kernel/scan_dim_by_key_impl.hpp @@ -57,10 +57,10 @@ namespace kernel std::to_string(int(inclusive_scan)); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + + if (entry.prog==0 && entry.ker==0) { Binary scan; ToNumStr toNumStr; @@ -95,10 +95,7 @@ namespace kernel entry.ker[1] = Kernel(*entry.prog, "scan_dim_by_key_nonfinal_kernel"); entry.ker[2] = Kernel(*entry.prog, "bcast_dim_kernel"); - kernelCaches[device][ref_name] = entry; - - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } return entry.ker[kerIdx]; diff --git a/src/backend/opencl/kernel/scan_first.hpp b/src/backend/opencl/kernel/scan_first.hpp index 85f32ec2e9..58587c1ba9 100644 --- a/src/backend/opencl/kernel/scan_first.hpp +++ b/src/backend/opencl/kernel/scan_first.hpp @@ -56,11 +56,10 @@ namespace kernel std::to_string(int(inclusive_scan)); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + if (entry.prog==0 && entry.ker==0) { const uint threads_y = THREADS_PER_GROUP / threads_x; const uint SHARED_MEM_SIZE = THREADS_PER_GROUP; @@ -95,10 +94,7 @@ namespace kernel entry.ker[0] = Kernel(*entry.prog, "scan_first_kernel"); entry.ker[1] = Kernel(*entry.prog, "bcast_first_kernel"); - kernelCaches[device][ref_name] = entry; - - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } return entry.ker[kerIdx]; diff --git a/src/backend/opencl/kernel/scan_first_by_key_impl.hpp b/src/backend/opencl/kernel/scan_first_by_key_impl.hpp index 9417c1a777..771dfca8f4 100644 --- a/src/backend/opencl/kernel/scan_first_by_key_impl.hpp +++ b/src/backend/opencl/kernel/scan_first_by_key_impl.hpp @@ -58,10 +58,9 @@ namespace kernel std::to_string(int(inclusive_scan)); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); + kc_entry_t entry = kernelCache(device, ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { const uint threads_y = THREADS_PER_GROUP / threads_x; const uint SHARED_MEM_SIZE = THREADS_PER_GROUP; @@ -99,10 +98,7 @@ namespace kernel entry.ker[1] = Kernel(*entry.prog, "scan_first_by_key_nonfinal_kernel"); entry.ker[2] = Kernel(*entry.prog, "bcast_first_kernel"); - kernelCaches[device][ref_name] = entry; - - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } return entry.ker[kerIdx]; diff --git a/src/backend/opencl/kernel/select.hpp b/src/backend/opencl/kernel/select.hpp index 12d55fa60a..ed8fe02640 100644 --- a/src/backend/opencl/kernel/select.hpp +++ b/src/backend/opencl/kernel/select.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -30,143 +29,120 @@ using std::string; namespace opencl { - namespace kernel - { - static const uint DIMX = 32; - static const uint DIMY = 8; - static const int REPEAT = 64; - - template - void select_launcher(Param out, Param cond, Param a, Param b, int ndims) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map selProgs; - static std::map selKernels; - - int device = getActiveDeviceId(); - - std::call_once(compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D is_same=" << is_same - << " -D T=" << dtype_traits::getName(); - - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - cl::Program prog; - buildProgram(prog, select_cl, select_cl_len, options.str()); - selProgs[device] = new Program(prog); - - selKernels[device] = new Kernel(*selProgs[device], "select_kernel"); - }); - - - int threads[] = {DIMX, DIMY}; - - if (ndims == 1) { - threads[0] *= threads[1]; - threads[1] = 1; - } - - NDRange local(threads[0], - threads[1]); - - - int groups_0 = divup(out.info.dims[0], REPEAT * local[0]); - int groups_1 = divup(out.info.dims[1], local[1]); - - NDRange global(groups_0 * out.info.dims[2] * local[0], - groups_1 * out.info.dims[3] * local[1]); - - auto selectOp = KernelFunctor(*selKernels[device]); - - selectOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, - *cond.data, cond.info, - *a.data, a.info, - *b.data, b.info, - groups_0, groups_1); +namespace kernel +{ +static const uint DIMX = 32; +static const uint DIMY = 8; +static const int REPEAT = 64; - } +template +void select_launcher(Param out, Param cond, Param a, Param b, int ndims) +{ + std::string refName = std::string("select_kernel_") + + std::string(dtype_traits::getName()) + std::to_string(is_same); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D is_same=" << is_same << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {select_cl}; + const int ker_lens[] = {select_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "select_kernel"); + + addKernelToCache(device, refName, entry); + } - template - void select(Param out, Param cond, Param a, Param b, int ndims) - { - bool is_same = true; - for (int i = 0; i < 4; i++) { - is_same &= (a.info.dims[i] == b.info.dims[i]); - } + int threads[] = {DIMX, DIMY}; - if (is_same) { - select_launcher(out, cond, a, b, ndims); - } else { - select_launcher(out, cond, a, b, ndims); - } - } + if (ndims == 1) { + threads[0] *= threads[1]; + threads[1] = 1; + } - template - void select_scalar(Param out, Param cond, Param a, const double b, int ndims) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map selProgs; - static std::map selKernels; + NDRange local(threads[0], threads[1]); - int device = getActiveDeviceId(); + int groups_0 = divup(out.info.dims[0], REPEAT * local[0]); + int groups_1 = divup(out.info.dims[1], local[1]); - std::call_once(compileFlags[device], [device] () { + NDRange global(groups_0 * out.info.dims[2] * local[0], groups_1 * out.info.dims[3] * local[1]); - std::ostringstream options; - options << " -D flip=" << flip - << " -D T=" << dtype_traits::getName(); + auto selectOp = KernelFunctor< Buffer, KParam, Buffer, KParam, Buffer, KParam, + Buffer, KParam, int, int>(*entry.ker); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + selectOp(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, *cond.data, cond.info, *a.data, a.info, + *b.data, b.info, groups_0, groups_1); +} - cl::Program prog; - buildProgram(prog, select_cl, select_cl_len, options.str()); - selProgs[device] = new Program(prog); +template +void select(Param out, Param cond, Param a, Param b, int ndims) +{ + bool is_same = true; + for (int i = 0; i < 4; i++) { + is_same &= (a.info.dims[i] == b.info.dims[i]); + } - selKernels[device] = new Kernel(*selProgs[device], "select_scalar_kernel"); - }); + if (is_same) { + select_launcher(out, cond, a, b, ndims); + } else { + select_launcher(out, cond, a, b, ndims); + } +} +template +void select_scalar(Param out, Param cond, Param a, const double b, int ndims) +{ + std::string refName = std::string("select_scalar_kernel_") + + std::string(dtype_traits::getName()) + + std::to_string(flip); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D flip=" << flip << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {select_cl}; + const int ker_lens[] = {select_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "select_scalar_kernel"); + + addKernelToCache(device, refName, entry); + } - int threads[] = {DIMX, DIMY}; + int threads[] = {DIMX, DIMY}; - if (ndims == 1) { - threads[0] *= threads[1]; - threads[1] = 1; - } + if (ndims == 1) { + threads[0] *= threads[1]; + threads[1] = 1; + } - NDRange local(threads[0], - threads[1]); + NDRange local(threads[0], threads[1]); - int groups_0 = divup(out.info.dims[0], REPEAT * local[0]); - int groups_1 = divup(out.info.dims[1], local[1]); + int groups_0 = divup(out.info.dims[0], REPEAT * local[0]); + int groups_1 = divup(out.info.dims[1], local[1]); - NDRange global(groups_0 * out.info.dims[2] * local[0], - groups_1 * out.info.dims[3] * local[1]); + NDRange global(groups_0 * out.info.dims[2] * local[0], groups_1 * out.info.dims[3] * local[1]); - auto selectOp = KernelFunctor(*selKernels[device]); + auto selectOp = KernelFunctor< Buffer, KParam, Buffer, KParam, Buffer, KParam, + T, int, int>(*entry.ker); - selectOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, - *cond.data, cond.info, - *a.data, a.info, - scalar(b), - groups_0, groups_1); - } - } + selectOp(EnqueueArgs(getQueue(), global, local), + *out.data, out.info, *cond.data, cond.info, + *a.data, a.info, scalar(b), groups_0, groups_1); +} +} } diff --git a/src/backend/opencl/kernel/shift.hpp b/src/backend/opencl/kernel/shift.hpp index 1bbfbe9fdc..7a6fb81650 100644 --- a/src/backend/opencl/kernel/shift.hpp +++ b/src/backend/opencl/kernel/shift.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -29,63 +28,64 @@ using std::string; namespace opencl { - namespace kernel - { - // Kernel Launch Config Values - static const int TX = 32; - static const int TY = 8; - static const int TILEX = 128; - static const int TILEY = 32; +namespace kernel +{ +// Kernel Launch Config Values +static const int TX = 32; +static const int TY = 8; +static const int TILEX = 128; +static const int TILEY = 32; - template - void shift(Param out, const Param in, const int *sdims) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map shiftProgs; - static std::map shiftKernels; +template +void shift(Param out, const Param in, const int *sdims) +{ + std::string refName = std::string("shift_kernel_") + std::string(dtype_traits::getName()); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, shift_cl, shift_cl_len, options.str()); - shiftProgs[device] = new Program(prog); - shiftKernels[device] = new Kernel(*shiftProgs[device], "shift_kernel"); - }); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - auto shiftOp = KernelFunctor (*shiftKernels[device]); + const char* ker_strs[] = {shift_cl}; + const int ker_lens[] = {shift_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "shift_kernel"); - NDRange local(TX, TY, 1); + addKernelToCache(device, refName, entry); + } - int blocksPerMatX = divup(out.info.dims[0], TILEX); - int blocksPerMatY = divup(out.info.dims[1], TILEY); - NDRange global(local[0] * blocksPerMatX * out.info.dims[2], - local[1] * blocksPerMatY * out.info.dims[3], - 1); + auto shiftOp = KernelFunctor< Buffer, const Buffer, const KParam, const KParam, + const int, const int, const int, const int, + const int, const int> (*entry.ker); - int sdims_[4]; - // Need to do this because we are mapping output to input in the kernel - for(int i = 0; i < 4; i++) { - // sdims_[i] will always be positive and always [0, oDims[i]]. - // Negative shifts are converted to position by going the other way round - sdims_[i] = -(sdims[i] % (int)out.info.dims[i]) + out.info.dims[i] * (sdims[i] > 0); - assert(sdims_[i] >= 0 && sdims_[i] <= out.info.dims[i]); - } + NDRange local(TX, TY, 1); - shiftOp(EnqueueArgs(getQueue(), global, local), - *out.data, *in.data, out.info, in.info, - sdims_[0], sdims_[1], sdims_[2], sdims_[3], - blocksPerMatX, blocksPerMatY); + int blocksPerMatX = divup(out.info.dims[0], TILEX); + int blocksPerMatY = divup(out.info.dims[1], TILEY); + NDRange global(local[0] * blocksPerMatX * out.info.dims[2], + local[1] * blocksPerMatY * out.info.dims[3], 1); - CL_DEBUG_FINISH(getQueue()); - } + int sdims_[4]; + // Need to do this because we are mapping output to input in the kernel + for(int i = 0; i < 4; i++) { + // sdims_[i] will always be positive and always [0, oDims[i]]. + // Negative shifts are converted to position by going the other way round + sdims_[i] = -(sdims[i] % (int)out.info.dims[i]) + out.info.dims[i] * (sdims[i] > 0); + assert(sdims_[i] >= 0 && sdims_[i] <= out.info.dims[i]); } + + shiftOp(EnqueueArgs(getQueue(), global, local), + *out.data, *in.data, out.info, in.info, + sdims_[0], sdims_[1], sdims_[2], sdims_[3], + blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/sift_nonfree.hpp b/src/backend/opencl/kernel/sift_nonfree.hpp index 95385f2802..9527ca1735 100644 --- a/src/backend/opencl/kernel/sift_nonfree.hpp +++ b/src/backend/opencl/kernel/sift_nonfree.hpp @@ -93,6 +93,7 @@ #include #include #include +#include #include namespace compute = boost::compute; @@ -107,10 +108,8 @@ using std::vector; namespace opencl { - namespace kernel { - static const int SIFT_THREADS = 256; static const int SIFT_THREADS_X = 32; static const int SIFT_THREADS_Y = 8; @@ -396,59 +395,66 @@ void apply_permutation(compute::buffer_iterator& keys, compute::vector& compute::gather(permutation.begin(), permutation.end(), temp.begin(), keys, queue); } -template -void sift(unsigned* out_feat, - unsigned* out_dlen, - Param& x_out, - Param& y_out, - Param& score_out, - Param& ori_out, - Param& size_out, - Param& desc_out, - Param img, - const unsigned n_layers, - const float contrast_thr, - const float edge_thr, - const float init_sigma, - const bool double_input, - const float img_scale, - const float feature_ratio, - const bool compute_GLOH) +template +std::array getSiftKernels() { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map siftProgs; - static std::map suKernel; - static std::map deKernel; - static std::map ieKernel; - static std::map coKernel; - static std::map rdKernel; - static std::map cdKernel; - static std::map cgKernel; + static const unsigned NUM_KERNELS = 7; + static const std::string kernelNames[NUM_KERNELS] = + {"sub", "detectExtrema", "interpolateExtrema", "calcOrientation", "removeDuplicates", + "computeDescriptor", "computeGLOHDescriptor"}; + + kc_entry_t entries[NUM_KERNELS]; int device = getActiveDeviceId(); - std::call_once( compileFlags[device], [device] () { + std::string checkName = kernelNames[0] + std::string("_") + std::string(dtype_traits::getName()); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); + entries[0] = kernelCache(device, checkName); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + if (entries[0].prog==0 && entries[0].ker==0) + { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - cl::Program prog; - buildProgram(prog, sift_nonfree_cl, sift_nonfree_cl_len, options.str()); - siftProgs[device] = new Program(prog); + cl::Program prog; + buildProgram(prog, sift_nonfree_cl, sift_nonfree_cl_len, options.str()); - suKernel[device] = new Kernel(*siftProgs[device], "sub"); - deKernel[device] = new Kernel(*siftProgs[device], "detectExtrema"); - ieKernel[device] = new Kernel(*siftProgs[device], "interpolateExtrema"); - coKernel[device] = new Kernel(*siftProgs[device], "calcOrientation"); - rdKernel[device] = new Kernel(*siftProgs[device], "removeDuplicates"); - cdKernel[device] = new Kernel(*siftProgs[device], "computeDescriptor"); - cgKernel[device] = new Kernel(*siftProgs[device], "computeGLOHDescriptor"); - }); + for (unsigned i=0; i::getName()); + + addKernelToCache(device, name, entries[i]); + } + } else { + for (unsigned i=1; i::getName()); + + entries[i] = kernelCache(device, name); + } + } + + std::array retVal; + for (unsigned i=0; i +void sift(unsigned* out_feat, unsigned* out_dlen, Param& x_out, Param& y_out, + Param& score_out, Param& ori_out, Param& size_out, Param& desc_out, + Param img, const unsigned n_layers, const float contrast_thr, const float edge_thr, + const float init_sigma, const bool double_input, const float img_scale, + const float feature_ratio, const bool compute_GLOH) +{ + auto kernels = getSiftKernels(); unsigned min_dim = min(img.info.dims[0], img.info.dims[1]); if (double_input) min_dim *= 2; @@ -459,7 +465,7 @@ void sift(unsigned* out_feat, std::vector gauss_pyr = buildGaussPyr(init_img, n_octaves, n_layers, init_sigma); - std::vector dog_pyr = buildDoGPyr(gauss_pyr, n_octaves, n_layers, suKernel[device]); + std::vector dog_pyr = buildDoGPyr(gauss_pyr, n_octaves, n_layers, kernels[0]); std::vector d_x_pyr(n_octaves, NULL); std::vector d_y_pyr(n_octaves, NULL); @@ -506,7 +512,7 @@ void sift(unsigned* out_feat, auto deOp = KernelFunctor (*deKernel[device]); + LocalSpaceArg> (*kernels[1]); deOp(EnqueueArgs(getQueue(), global, local), *d_extrema_x, *d_extrema_y, *d_extrema_layer, *d_count, @@ -542,7 +548,7 @@ void sift(unsigned* out_feat, Buffer, Buffer, Buffer, Buffer, Buffer, Buffer, unsigned, Buffer, KParam, unsigned, unsigned, unsigned, - float, float, float, float> (*ieKernel[device]); + float, float, float, float> (*kernels[2]); ieOp(EnqueueArgs(getQueue(), global_interp, local_interp), *d_interp_x, *d_interp_y, *d_interp_layer, @@ -614,7 +620,7 @@ void sift(unsigned* out_feat, auto rdOp = KernelFunctor (*rdKernel[device]); + unsigned> (*kernels[4]); rdOp(EnqueueArgs(getQueue(), global_nodup, local_nodup), *d_nodup_x, *d_nodup_y, *d_nodup_layer, @@ -650,7 +656,7 @@ void sift(unsigned* out_feat, auto coOp = KernelFunctor (*coKernel[device]); + LocalSpaceArg> (*kernels[3]); coOp(EnqueueArgs(getQueue(), global_ori, local_ori), *d_oriented_x, *d_oriented_y, *d_oriented_layer, @@ -695,20 +701,20 @@ void sift(unsigned* out_feat, auto cgOp = KernelFunctor (*cgKernel[device]); + LocalSpaceArg> (*kernels[6]); cgOp(EnqueueArgs(getQueue(), global_desc, local_desc), - *d_desc, desc_len, histsz, - *d_oriented_x, *d_oriented_y, *d_oriented_layer, - *d_oriented_response, *d_oriented_size, *d_oriented_ori, oriented_feat, - *gauss_pyr[o].data, gauss_pyr[o].info, d, rb, ab, hb, scale, n_layers, - cl::Local(desc_len * (histsz+1) * sizeof(float))); + *d_desc, desc_len, histsz, + *d_oriented_x, *d_oriented_y, *d_oriented_layer, + *d_oriented_response, *d_oriented_size, *d_oriented_ori, oriented_feat, + *gauss_pyr[o].data, gauss_pyr[o].info, d, rb, ab, hb, scale, n_layers, + cl::Local(desc_len * (histsz+1) * sizeof(float))); } else { auto cdOp = KernelFunctor (*cdKernel[device]); + LocalSpaceArg> (*kernels[5]); cdOp(EnqueueArgs(getQueue(), global_desc, local_desc), *d_desc, desc_len, histsz, @@ -817,7 +823,5 @@ void sift(unsigned* out_feat, *out_feat = total_feat; *out_dlen = desc_len; } - } //namespace kernel - } //namespace opencl diff --git a/src/backend/opencl/kernel/sobel.hpp b/src/backend/opencl/kernel/sobel.hpp index 2bec2e085f..f77e3ea911 100644 --- a/src/backend/opencl/kernel/sobel.hpp +++ b/src/backend/opencl/kernel/sobel.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,60 +27,55 @@ using std::string; namespace opencl { - namespace kernel { - static const int THREADS_X = 16; static const int THREADS_Y = 16; template void sobel(Param dx, Param dy, const Param in) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map sobProgs; - static std::map sobKernels; + std::string refName = std::string("sobel3x3_") + std::string(dtype_traits::getName()) + + std::string(dtype_traits::getName()) + std::to_string(ker_size); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D Ti=" << dtype_traits::getName() - << " -D To=" << dtype_traits::getName() - << " -D KER_SIZE="<< ker_size; - if (std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, sobel_cl, sobel_cl_len, options.str()); - sobProgs[device] = new Program(prog); - sobKernels[device] = new Kernel(*sobProgs[device], "sobel3x3"); - }); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D Ti=" << dtype_traits::getName() + << " -D To=" << dtype_traits::getName() + << " -D KER_SIZE="<< ker_size; + if (std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {sobel_cl}; + const int ker_lens[] = {sobel_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "sobel3x3"); + + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); int blk_x = divup(in.info.dims[0], THREADS_X); int blk_y = divup(in.info.dims[1], THREADS_Y); - NDRange global(blk_x * in.info.dims[2] * THREADS_X, - blk_y * in.info.dims[3] * THREADS_Y); + NDRange global(blk_x * in.info.dims[2] * THREADS_X, blk_y * in.info.dims[3] * THREADS_Y); - auto sobelOp = KernelFunctor (*sobKernels[device]); + auto sobelOp = KernelFunctor< Buffer, KParam, Buffer, KParam, Buffer, KParam, + cl::LocalSpaceArg, int, int> (*entry.ker); size_t loc_size = (THREADS_X+ker_size-1)*(THREADS_Y+ker_size-1)*sizeof(Ti); sobelOp(EnqueueArgs(getQueue(), global, local), - *dx.data, dx.info, *dy.data, dy.info, - *in.data, in.info, cl::Local(loc_size), blk_x, blk_y); + *dx.data, dx.info, *dy.data, dy.info, + *in.data, in.info, cl::Local(loc_size), blk_x, blk_y); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/sparse.hpp b/src/backend/opencl/kernel/sparse.hpp index df56d5fedb..67c2650bcb 100644 --- a/src/backend/opencl/kernel/sparse.hpp +++ b/src/backend/opencl/kernel/sparse.hpp @@ -50,10 +50,9 @@ namespace opencl std::to_string(REPEAT); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, ref_name); - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName() << " -D reps=" << REPEAT @@ -68,8 +67,8 @@ namespace opencl buildProgram(prog, coo2dense_cl, coo2dense_cl_len, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "coo2dense_kernel"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); }; auto coo2denseOp = KernelFunctorsecond; + + addKernelToCache(device, ref_name, entry); } NDRange local(threads, 1); @@ -196,10 +194,9 @@ namespace opencl std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, ref_name); - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); @@ -222,9 +219,7 @@ namespace opencl entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "dense2csr_split_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } NDRange local(THREADS_X, THREADS_Y); @@ -258,10 +253,9 @@ namespace opencl std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, ref_name); - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); @@ -274,8 +268,8 @@ namespace opencl buildProgram(prog, csr2coo_cl, csr2coo_cl_len, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "swapIndex_kernel"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); }; auto swapIndexOp = KernelFunctor::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, ref_name); - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); @@ -327,8 +320,8 @@ namespace opencl buildProgram(prog, 1, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "csr2coo"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } cl::Buffer *scratch = bufferAlloc(orowIdx.info.dims[0] * sizeof(int)); @@ -374,10 +367,9 @@ namespace opencl std::string(dtype_traits::getName()); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, ref_name); - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { std::ostringstream options; options << " -D T=" << dtype_traits::getName(); @@ -390,8 +382,8 @@ namespace opencl buildProgram(prog, csr2coo_cl, csr2coo_cl_len, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "csrReduce_kernel"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); }; auto csrReduceOp = KernelFunctor (*entry.ker); diff --git a/src/backend/opencl/kernel/susan.hpp b/src/backend/opencl/kernel/susan.hpp index c4d321e01f..bb008bed21 100644 --- a/src/backend/opencl/kernel/susan.hpp +++ b/src/backend/opencl/kernel/susan.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "config.hpp" using cl::Buffer; @@ -27,10 +27,8 @@ using cl::NDRange; namespace opencl { - namespace kernel { - static const unsigned THREADS_PER_BLOCK = 256; static const unsigned SUSAN_THREADS_X = 16; static const unsigned SUSAN_THREADS_Y = 16; @@ -41,45 +39,41 @@ void susan(cl::Buffer* out, const cl::Buffer* in, const unsigned idim0, const unsigned idim1, const float t, const float g, const unsigned edge) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map suProg; - static std::map suKernel; + std::string refName = std::string("susan_responses_") + + std::string(dtype_traits::getName()) + std::to_string(radius); int device = getActiveDeviceId(); - - std::call_once( compileFlags[device], [device] () { - - const size_t LOCAL_MEM_SIZE = (SUSAN_THREADS_X+2*radius)*(SUSAN_THREADS_Y+2*radius); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D LOCAL_MEM_SIZE=" << LOCAL_MEM_SIZE - << " -D BLOCK_X="<< SUSAN_THREADS_X - << " -D BLOCK_Y="<< SUSAN_THREADS_Y - << " -D RADIUS="<< radius - << " -D RESPONSE"; - - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - cl::Program prog; - buildProgram(prog, susan_cl, susan_cl_len, options.str()); - suProg[device] = new Program(prog); - suKernel[device] = new Kernel(*suProg[device], "susan_responses"); - }); - - auto susanOp = KernelFunctor(*suKernel[device]); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + const size_t LOCAL_MEM_SIZE = (SUSAN_THREADS_X+2*radius)*(SUSAN_THREADS_Y+2*radius); + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D LOCAL_MEM_SIZE=" << LOCAL_MEM_SIZE + << " -D BLOCK_X="<< SUSAN_THREADS_X + << " -D BLOCK_Y="<< SUSAN_THREADS_Y + << " -D RADIUS="<< radius + << " -D RESPONSE"; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {susan_cl}; + const int ker_lens[] = {susan_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "susan_responses"); + + addKernelToCache(device, refName, entry); + } + + auto susanOp = KernelFunctor< Buffer, Buffer, unsigned, unsigned, unsigned, + float, float, unsigned >(*entry.ker); NDRange local(SUSAN_THREADS_X, SUSAN_THREADS_Y); - NDRange global(divup(idim0-2*edge, local[0])*local[0], - divup(idim1-2*edge, local[1])*local[1]); + NDRange global(divup(idim0-2*edge, local[0])*local[0], divup(idim1-2*edge, local[1])*local[1]); susanOp(EnqueueArgs(getQueue(), global, local), *out, *in, in_off, idim0, idim1, t, g, edge); - } template @@ -88,49 +82,45 @@ unsigned nonMaximal(cl::Buffer* x_out, cl::Buffer* y_out, cl::Buffer* resp_out, const unsigned edge, const unsigned max_corners) { unsigned corners_found = 0; - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map nmProg; - static std::map nmKernel; - int device = getActiveDeviceId(); + std::string refName = std::string("non_maximal_") + std::string(dtype_traits::getName()); - std::call_once( compileFlags[device], [device] () { + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D NONMAX"; + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() << " -D NONMAX"; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + const char* ker_strs[] = {susan_cl}; + const int ker_lens[] = {susan_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "non_maximal"); - cl::Program prog; - buildProgram(prog, susan_cl, susan_cl_len, options.str()); - nmProg[device] = new Program(prog); - nmKernel[device] = new Kernel(*nmProg[device], "non_maximal"); - }); + addKernelToCache(device, refName, entry); + } cl::Buffer *d_corners_found = bufferAlloc(sizeof(unsigned)); getQueue().enqueueWriteBuffer(*d_corners_found, CL_TRUE, 0, sizeof(unsigned), &corners_found); - auto nonMaximalOp = KernelFunctor(*nmKernel[device]); + auto nonMaximalOp = KernelFunctor< Buffer, Buffer, Buffer, Buffer, unsigned, unsigned, Buffer, + unsigned, unsigned >(*entry.ker); NDRange local(SUSAN_THREADS_X, SUSAN_THREADS_Y); - NDRange global(divup(idim0-2*edge, local[0])*local[0], - divup(idim1-2*edge, local[1])*local[1]); + NDRange global(divup(idim0-2*edge, local[0])*local[0], divup(idim1-2*edge, local[1])*local[1]); nonMaximalOp(EnqueueArgs(getQueue(), global, local), - *x_out, *y_out, *resp_out, *d_corners_found, - idim0, idim1, *resp_in, edge, max_corners); + *x_out, *y_out, *resp_out, *d_corners_found, + idim0, idim1, *resp_in, edge, max_corners); getQueue().enqueueReadBuffer(*d_corners_found, CL_TRUE, 0, sizeof(unsigned), &corners_found); bufferFree(d_corners_found); + return corners_found; } - } - } diff --git a/src/backend/opencl/kernel/swapdblk.hpp b/src/backend/opencl/kernel/swapdblk.hpp index 0e0f4b16c5..e7c4ab9ed3 100644 --- a/src/backend/opencl/kernel/swapdblk.hpp +++ b/src/backend/opencl/kernel/swapdblk.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -27,41 +26,36 @@ using cl::EnqueueArgs; using cl::NDRange; using std::string; - namespace opencl { - namespace kernel { - template void swapdblk(int n, int nb, cl_mem dA, size_t dA_offset, int ldda, int inca, cl_mem dB, size_t dB_offset, int lddb, int incb) { - - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map swpProgs; - static std::map swpKernels; + std::string refName = std::string("swapdblk_") + std::string(dtype_traits::getName()); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once(compileFlags[device], [device] () { + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + const char* ker_strs[] = {swapdblk_cl}; + const int ker_lens[] = {swapdblk_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "swapdblk"); - cl::Program prog; - buildProgram(prog, swapdblk_cl, swapdblk_cl_len, options.str()); - swpProgs[device] = new Program(prog); - - swpKernels[device] = new Kernel(*swpProgs[device], "swapdblk"); - }); + addKernelToCache(device, refName, entry); + } int nblocks = n / nb; @@ -94,16 +88,11 @@ void swapdblk(int n, int nb, cl::Buffer dAObj(dA, true); cl::Buffer dBObj(dB, true); - auto swapdOp = KernelFunctor(*swpKernels[device]); + auto swapdOp = KernelFunctor(*entry.ker); swapdOp(EnqueueArgs(getQueue(), global, local), - nb, - dAObj, dA_offset, ldda, inca, - dBObj, dB_offset, lddb, incb); - + nb, dAObj, dA_offset, ldda, inca, dBObj, dB_offset, lddb, incb); } - } } diff --git a/src/backend/opencl/kernel/tile.hpp b/src/backend/opencl/kernel/tile.hpp index 3ad71c21f5..7d15384f86 100644 --- a/src/backend/opencl/kernel/tile.hpp +++ b/src/backend/opencl/kernel/tile.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -28,52 +27,52 @@ using std::string; namespace opencl { - namespace kernel - { - // Kernel Launch Config Values - static const int TX = 32; - static const int TY = 8; - static const int TILEX = 512; - static const int TILEY = 32; +namespace kernel +{ +// Kernel Launch Config Values +static const int TX = 32; +static const int TY = 8; +static const int TILEX = 512; +static const int TILEY = 32; - template - void tile(Param out, const Param in) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map tileProgs; - static std::map tileKernels; +template +void tile(Param out, const Param in) +{ + std::string refName = std::string("tile_kernel_") + std::string(dtype_traits::getName()); - int device = getActiveDeviceId(); + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once( compileFlags[device], [device] () { - std::ostringstream options; - options << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, tile_cl, tile_cl_len, options.str()); - tileProgs[device] = new Program(prog); - tileKernels[device] = new Kernel(*tileProgs[device], "tile_kernel"); - }); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - auto tileOp = KernelFunctor (*tileKernels[device]); + const char* ker_strs[] = {tile_cl}; + const int ker_lens[] = {tile_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "tile_kernel"); - NDRange local(TX, TY, 1); + addKernelToCache(device, refName, entry); + } - int blocksPerMatX = divup(out.info.dims[0], TILEX); - int blocksPerMatY = divup(out.info.dims[1], TILEY); - NDRange global(local[0] * blocksPerMatX * out.info.dims[2], - local[1] * blocksPerMatY * out.info.dims[3], - 1); + auto tileOp = KernelFunctor< Buffer, const Buffer, const KParam, const KParam, + const int, const int> (*entry.ker); - tileOp(EnqueueArgs(getQueue(), global, local), - *out.data, *in.data, out.info, in.info, - blocksPerMatX, blocksPerMatY); + NDRange local(TX, TY, 1); - CL_DEBUG_FINISH(getQueue()); - } - } + int blocksPerMatX = divup(out.info.dims[0], TILEX); + int blocksPerMatY = divup(out.info.dims[1], TILEY); + NDRange global(local[0] * blocksPerMatX * out.info.dims[2], + local[1] * blocksPerMatY * out.info.dims[3], 1); + + tileOp(EnqueueArgs(getQueue(), global, local), + *out.data, *in.data, out.info, in.info, blocksPerMatX, blocksPerMatY); + + CL_DEBUG_FINISH(getQueue()); +} +} } diff --git a/src/backend/opencl/kernel/transform.hpp b/src/backend/opencl/kernel/transform.hpp index dc38696d8d..3aa37d97e2 100644 --- a/src/backend/opencl/kernel/transform.hpp +++ b/src/backend/opencl/kernel/transform.hpp @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include #include #include @@ -71,18 +69,17 @@ namespace opencl std::to_string(order); int device = getActiveDeviceId(); - auto idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; + kc_entry_t entry = kernelCache(device, ref_name); - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { ToNumStr toNumStr; std::ostringstream options; options << " -D T=" << dtype_traits::getName() << " -D INVERSE=" << (isInverse ? 1 : 0) << " -D PERSPECTIVE=" << (isPerspective ? 1 : 0) << " -D ZERO=" << toNumStr(scalar(0)); - options << " -D InterpInTy=" << dtype_traits::getName(); - options << " -D InterpValTy=" << dtype_traits>::getName(); + options << " -D InterpInTy=" << dtype_traits::getName(); + options << " -D InterpValTy=" << dtype_traits>::getName(); options << " -D InterpPosTy=" << dtype_traits>::getName(); if((af_dtype) dtype_traits::af_type == c32 || @@ -106,8 +103,8 @@ namespace opencl buildProgram(prog, 2, ker_strs, ker_lens, options.str()); entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "transform_kernel"); - } else { - entry = idx->second; + + addKernelToCache(device, ref_name, entry); } auto transformOp = KernelFunctor #include #include -#include -#include +#include #include #include #include @@ -29,10 +28,8 @@ using std::string; namespace opencl { - namespace kernel { - static const int TILE_DIM = 32; static const int THREADS_X = TILE_DIM; static const int THREADS_Y = 256 / TILE_DIM; @@ -40,33 +37,32 @@ static const int THREADS_Y = 256 / TILE_DIM; template void transpose(Param out, const Param in) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map trsProgs; - static std::map trsKernels; + std::string refName = std::string("transpose_") + std::string(dtype_traits::getName()) + + std::to_string(conjugate) + std::to_string(IS32MULTIPLE); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once(compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D TILE_DIM=" << TILE_DIM - << " -D THREADS_Y=" << THREADS_Y - << " -D IS32MULTIPLE=" << IS32MULTIPLE - << " -D DOCONJUGATE=" << (conjugate && af::iscplx()) - << " -D T=" << dtype_traits::getName(); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D TILE_DIM=" << TILE_DIM + << " -D THREADS_Y=" << THREADS_Y + << " -D IS32MULTIPLE=" << IS32MULTIPLE + << " -D DOCONJUGATE=" << (conjugate && af::iscplx()) + << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - cl::Program prog; - buildProgram(prog, transpose_cl, transpose_cl_len, options.str()); - trsProgs[device] = new Program(prog); - - trsKernels[device] = new Kernel(*trsProgs[device], "transpose"); - }); + const char* ker_strs[] = {transpose_cl}; + const int ker_lens[] = {transpose_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "transpose"); + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); @@ -77,16 +73,13 @@ void transpose(Param out, const Param in) NDRange global(blk_x * local[0] * in.info.dims[2], blk_y * local[1] * in.info.dims[3]); - auto transposeOp = KernelFunctor (*trsKernels[device]); + auto transposeOp = KernelFunctor< Buffer, const KParam, const Buffer, const KParam, + const int, const int> (*entry.ker); transposeOp(EnqueueArgs(getQueue(), global, local), *out.data, out.info, *in.data, in.info, blk_x, blk_y); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/transpose_inplace.hpp b/src/backend/opencl/kernel/transpose_inplace.hpp index 6784eca86f..b9f7b12a01 100644 --- a/src/backend/opencl/kernel/transpose_inplace.hpp +++ b/src/backend/opencl/kernel/transpose_inplace.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -29,10 +28,8 @@ using std::string; namespace opencl { - namespace kernel { - static const int TILE_DIM = 16; static const int THREADS_X = TILE_DIM; static const int THREADS_Y = 256 / TILE_DIM; @@ -40,33 +37,32 @@ static const int THREADS_Y = 256 / TILE_DIM; template void transpose_inplace(Param in) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map transposeProgs; - static std::map transposeKernels; + std::string refName = std::string("transpose_inplace_") + std::string(dtype_traits::getName()) + + std::to_string(conjugate) + std::to_string(IS32MULTIPLE); int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); - std::call_once(compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D TILE_DIM=" << TILE_DIM - << " -D THREADS_Y=" << THREADS_Y - << " -D IS32MULTIPLE=" << IS32MULTIPLE - << " -D DOCONJUGATE=" << (conjugate && af::iscplx()) - << " -D T=" << dtype_traits::getName(); + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D TILE_DIM=" << TILE_DIM + << " -D THREADS_Y=" << THREADS_Y + << " -D IS32MULTIPLE=" << IS32MULTIPLE + << " -D DOCONJUGATE=" << (conjugate && af::iscplx()) + << " -D T=" << dtype_traits::getName(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; - cl::Program prog; - buildProgram(prog, transpose_inplace_cl, transpose_inplace_cl_len, options.str()); - transposeProgs[device] = new Program(prog); - - transposeKernels[device] = new Kernel(*transposeProgs[device], "transpose_inplace"); - }); + const char* ker_strs[] = {transpose_inplace_cl}; + const int ker_lens[] = {transpose_inplace_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "transpose_inplace"); + addKernelToCache(device, refName, entry); + } NDRange local(THREADS_X, THREADS_Y); @@ -74,17 +70,13 @@ void transpose_inplace(Param in) int blk_y = divup(in.info.dims[1], TILE_DIM); // launch batch * blk_x blocks along x dimension - NDRange global(blk_x * local[0] * in.info.dims[2], - blk_y * local[1] * in.info.dims[3]); + NDRange global(blk_x * local[0] * in.info.dims[2], blk_y * local[1] * in.info.dims[3]); - auto transposeOp = KernelFunctor (*transposeKernels[device]); + auto transposeOp = KernelFunctor (*entry.ker); transposeOp(EnqueueArgs(getQueue(), global, local), *in.data, in.info, blk_x, blk_y); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/triangle.hpp b/src/backend/opencl/kernel/triangle.hpp index acfc4424dd..32925d1cbb 100644 --- a/src/backend/opencl/kernel/triangle.hpp +++ b/src/backend/opencl/kernel/triangle.hpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -31,10 +30,8 @@ using af::scalar_to_option; namespace opencl { - namespace kernel { - // Kernel Launch Config Values static const unsigned TX = 32; static const unsigned TY = 8; @@ -44,51 +41,46 @@ static const unsigned TILEY = 32; template void triangle(Param out, const Param in) { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map trgProgs; - static std::map trgKernels; + std::string refName = std::string("triangle_kernel_") + std::string(dtype_traits::getName()) + + std::to_string(is_upper) + std::to_string(is_unit_diag); int device = getActiveDeviceId(); - - std::call_once(compileFlags[device], [device] () { - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D is_upper=" << is_upper - << " -D is_unit_diag=" << is_unit_diag - << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")" - << " -D ONE=(T)(" << scalar_to_option(scalar(1)) << ")"; - - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - - cl::Program prog; - buildProgram(prog, triangle_cl, triangle_cl_len, options.str()); - trgProgs[device] = new Program(prog); - - trgKernels[device] = new Kernel(*trgProgs[device], "triangle_kernel"); - }); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D is_upper=" << is_upper + << " -D is_unit_diag=" << is_unit_diag + << " -D ZERO=(T)(" << scalar_to_option(scalar(0)) << ")" + << " -D ONE=(T)(" << scalar_to_option(scalar(1)) << ")"; + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {triangle_cl}; + const int ker_lens[] = {triangle_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "triangle_kernel"); + + addKernelToCache(device, refName, entry); + } NDRange local(TX, TY); int groups_x = divup(out.info.dims[0], TILEX); int groups_y = divup(out.info.dims[1], TILEY); - NDRange global(groups_x * out.info.dims[2] * local[0], - groups_y * out.info.dims[3] * local[1]); + NDRange global(groups_x * out.info.dims[2] * local[0], groups_y * out.info.dims[3] * local[1]); - auto triangleOp = KernelFunctor (*trgKernels[device]); + auto triangleOp = KernelFunctor< Buffer, KParam, const Buffer, KParam, + const int, const int >(*entry.ker); triangleOp(EnqueueArgs(getQueue(), global, local), - *out.data, out.info, *in.data, in.info, groups_x, groups_y); + *out.data, out.info, *in.data, in.info, groups_x, groups_y); CL_DEBUG_FINISH(getQueue()); } - } - } diff --git a/src/backend/opencl/kernel/unwrap.hpp b/src/backend/opencl/kernel/unwrap.hpp index 0ae3b7002a..100bd9ca66 100644 --- a/src/backend/opencl/kernel/unwrap.hpp +++ b/src/backend/opencl/kernel/unwrap.hpp @@ -48,11 +48,10 @@ namespace opencl std::to_string(is_column); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + kc_entry_t entry = kernelCache(device, ref_name); + if (entry.prog==0 && entry.ker==0) { ToNumStr toNumStr; std::ostringstream options; options << " -D is_column=" << is_column @@ -70,9 +69,7 @@ namespace opencl entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "unwrap_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } dim_t TX = 1, TY = 1; diff --git a/src/backend/opencl/kernel/where.hpp b/src/backend/opencl/kernel/where.hpp index 26f1bb1ae3..e76943e3c2 100644 --- a/src/backend/opencl/kernel/where.hpp +++ b/src/backend/opencl/kernel/where.hpp @@ -9,8 +9,7 @@ #pragma once #include -#include -#include +#include #include #include #include @@ -35,135 +34,122 @@ namespace opencl { namespace kernel { +template +static void get_out_idx(Buffer *out_data, + Param &otmp, Param &rtmp, + Param &in, uint threads_x, + uint groups_x, uint groups_y) +{ + std::string refName = std::string("get_out_idx_kernel_") + std::string(dtype_traits::getName()); + + int device = getActiveDeviceId(); + kc_entry_t entry = kernelCache(device, refName); + + if (entry.prog==0 && entry.ker==0) { + ToNumStr toNumStr; + std::ostringstream options; + options << " -D T=" << dtype_traits::getName() + << " -D zero=" << toNumStr(scalar(0)) + << " -D CPLX=" << af::iscplx(); + if (std::is_same::value || std::is_same::value) + options << " -D USE_DOUBLE"; + + const char* ker_strs[] = {where_cl}; + const int ker_lens[] = {where_cl_len}; + Program prog; + buildProgram(prog, 1, ker_strs, ker_lens, options.str()); + entry.prog = new Program(prog); + entry.ker = new Kernel(*entry.prog, "get_out_idx_kernel"); + + addKernelToCache(device, refName, entry); + } - template - static void get_out_idx(Buffer *out_data, - Param &otmp, Param &rtmp, - Param &in, uint threads_x, - uint groups_x, uint groups_y) - { - static std::once_flag compileFlags[DeviceManager::MAX_DEVICES]; - static std::map whereProgs; - static std::map whereKerns; - - int device= getActiveDeviceId(); - - std::call_once(compileFlags[device], [device] () { - - ToNumStr toNumStr; - - std::ostringstream options; - options << " -D T=" << dtype_traits::getName() - << " -D zero=" << toNumStr(scalar(0)) - << " -D CPLX=" << af::iscplx(); - if (std::is_same::value || - std::is_same::value) { - options << " -D USE_DOUBLE"; - } - Program prog; - buildProgram(prog, where_cl, where_cl_len, options.str()); - whereProgs[device] = new Program(prog); - whereKerns[device] = new Kernel(*whereProgs[device], "get_out_idx_kernel"); - }); - - NDRange local(threads_x, THREADS_PER_GROUP / threads_x); - NDRange global(local[0] * groups_x * in.info.dims[2], - local[1] * groups_y * in.info.dims[3]); - - uint lim = divup(otmp.info.dims[0], (threads_x * groups_x)); - - auto whereOp = KernelFunctor(*whereKerns[device]); - - whereOp(EnqueueArgs(getQueue(), global, local), - *out_data, - *otmp.data, otmp.info, - *rtmp.data, rtmp.info, - *in.data, in.info, - groups_x, groups_y, lim); - - CL_DEBUG_FINISH(getQueue()); + NDRange local(threads_x, THREADS_PER_GROUP / threads_x); + NDRange global(local[0] * groups_x * in.info.dims[2], local[1] * groups_y * in.info.dims[3]); - } + uint lim = divup(otmp.info.dims[0], (threads_x * groups_x)); - template - static void where(Param &out, Param &in) - { - uint threads_x = nextpow2(std::max(32u, (uint)in.info.dims[0])); - threads_x = std::min(threads_x, THREADS_PER_GROUP); - uint threads_y = THREADS_PER_GROUP / threads_x; + auto whereOp = KernelFunctor< Buffer, Buffer, KParam, Buffer, KParam, + Buffer, KParam, uint, uint, uint>(*entry.ker); - uint groups_x = divup(in.info.dims[0], threads_x * REPEAT); - uint groups_y = divup(in.info.dims[1], threads_y); + whereOp(EnqueueArgs(getQueue(), global, local), + *out_data, *otmp.data, otmp.info, + *rtmp.data, rtmp.info, *in.data, in.info, + groups_x, groups_y, lim); - Param rtmp; - Param otmp; + CL_DEBUG_FINISH(getQueue()); +} - rtmp.info.dims[0] = groups_x; - otmp.info.dims[0] = in.info.dims[0]; +template +static void where(Param &out, Param &in) +{ + uint threads_x = nextpow2(std::max(32u, (uint)in.info.dims[0])); + threads_x = std::min(threads_x, THREADS_PER_GROUP); + uint threads_y = THREADS_PER_GROUP / threads_x; - rtmp.info.strides[0] = 1; - otmp.info.strides[0] = 1; + uint groups_x = divup(in.info.dims[0], threads_x * REPEAT); + uint groups_y = divup(in.info.dims[1], threads_y); - rtmp.info.offset = 0; - otmp.info.offset = 0; + Param rtmp; + Param otmp; - for (int k = 1; k < 4; k++) { - rtmp.info.dims[k] = in.info.dims[k]; - rtmp.info.strides[k] = rtmp.info.strides[k - 1] * rtmp.info.dims[k - 1]; + rtmp.info.dims[0] = groups_x; + otmp.info.dims[0] = in.info.dims[0]; - otmp.info.dims[k] = in.info.dims[k]; - otmp.info.strides[k] = otmp.info.strides[k - 1] * otmp.info.dims[k - 1]; - } + rtmp.info.strides[0] = 1; + otmp.info.strides[0] = 1; - int rtmp_elements = rtmp.info.strides[3] * rtmp.info.dims[3]; - rtmp.data = bufferAlloc(rtmp_elements * sizeof(uint)); + rtmp.info.offset = 0; + otmp.info.offset = 0; - int otmp_elements = otmp.info.strides[3] * otmp.info.dims[3]; - otmp.data = bufferAlloc(otmp_elements * sizeof(uint)); + for (int k = 1; k < 4; k++) { + rtmp.info.dims[k] = in.info.dims[k]; + rtmp.info.strides[k] = rtmp.info.strides[k - 1] * rtmp.info.dims[k - 1]; - scan_first_launcher(otmp, rtmp, in, - false, - groups_x, groups_y, - threads_x); + otmp.info.dims[k] = in.info.dims[k]; + otmp.info.strides[k] = otmp.info.strides[k - 1] * otmp.info.dims[k - 1]; + } - // Linearize the dimensions and perform scan - Param ltmp = rtmp; - ltmp.info.offset = 0; - ltmp.info.dims[0] = rtmp_elements; - for (int k = 1; k < 4; k++) { - ltmp.info.dims[k] = 1; - ltmp.info.strides[k] = rtmp_elements; - } + int rtmp_elements = rtmp.info.strides[3] * rtmp.info.dims[3]; + rtmp.data = bufferAlloc(rtmp_elements * sizeof(uint)); - scan_first(ltmp, ltmp); + int otmp_elements = otmp.info.strides[3] * otmp.info.dims[3]; + otmp.data = bufferAlloc(otmp_elements * sizeof(uint)); - // Get output size and allocate output - uint total; - getQueue().enqueueReadBuffer(*rtmp.data, CL_TRUE, - sizeof(uint) * (rtmp_elements - 1), - sizeof(uint), - &total); + scan_first_launcher(otmp, rtmp, in, false, groups_x, groups_y, threads_x); + // Linearize the dimensions and perform scan + Param ltmp = rtmp; + ltmp.info.offset = 0; + ltmp.info.dims[0] = rtmp_elements; + for (int k = 1; k < 4; k++) { + ltmp.info.dims[k] = 1; + ltmp.info.strides[k] = rtmp_elements; + } - out.data = bufferAlloc(total * sizeof(uint)); + scan_first(ltmp, ltmp); - out.info.dims[0] = total; - out.info.strides[0] = 1; - for (int k = 1; k < 4; k++) { - out.info.dims[k] = 1; - out.info.strides[k] = total; - } + // Get output size and allocate output + uint total; + getQueue().enqueueReadBuffer(*rtmp.data, CL_TRUE, + sizeof(uint) * (rtmp_elements - 1), + sizeof(uint), + &total); - if (total > 0) { - get_out_idx(out.data, otmp, rtmp, in, threads_x, groups_x, groups_y); - } + out.data = bufferAlloc(total * sizeof(uint)); - bufferFree(rtmp.data); - bufferFree(otmp.data); + out.info.dims[0] = total; + out.info.strides[0] = 1; + for (int k = 1; k < 4; k++) { + out.info.dims[k] = 1; + out.info.strides[k] = total; } + + if (total > 0) + get_out_idx(out.data, otmp, rtmp, in, threads_x, groups_x, groups_y); + + bufferFree(rtmp.data); + bufferFree(otmp.data); +} } } diff --git a/src/backend/opencl/kernel/wrap.hpp b/src/backend/opencl/kernel/wrap.hpp index 3e35a2fbea..5ad2efe38c 100644 --- a/src/backend/opencl/kernel/wrap.hpp +++ b/src/backend/opencl/kernel/wrap.hpp @@ -48,10 +48,9 @@ namespace opencl std::to_string(is_column); int device = getActiveDeviceId(); - kc_t::iterator idx = kernelCaches[device].find(ref_name); + kc_entry_t entry = kernelCache(device, ref_name); - kc_entry_t entry; - if (idx == kernelCaches[device].end()) { + if (entry.prog==0 && entry.ker==0) { ToNumStr toNumStr; std::ostringstream options; @@ -70,9 +69,7 @@ namespace opencl entry.prog = new Program(prog); entry.ker = new Kernel(*entry.prog, "wrap_kernel"); - kernelCaches[device][ref_name] = entry; - } else { - entry = idx->second; + addKernelToCache(device, ref_name, entry); } dim_t nx = (out.info.dims[0] + 2 * px - wx) / sx + 1; diff --git a/src/backend/opencl/lu.cpp b/src/backend/opencl/lu.cpp index 70b6d97f41..aa8cfeef3d 100644 --- a/src/backend/opencl/lu.cpp +++ b/src/backend/opencl/lu.cpp @@ -71,7 +71,6 @@ Array lu_inplace(Array &in, const bool convert_pivot) return cpu::lu_inplace(in, convert_pivot); } - initBlas(); dim4 iDims = in.dims(); int M = iDims[0]; int N = iDims[1]; diff --git a/src/backend/opencl/magma/magma_blas_clblas.h b/src/backend/opencl/magma/magma_blas_clblas.h index f9cc8d5748..e135c6c6a3 100644 --- a/src/backend/opencl/magma/magma_blas_clblas.h +++ b/src/backend/opencl/magma/magma_blas_clblas.h @@ -45,10 +45,26 @@ clblasSide clblas_side_const ( magma_side_t side ); #define OPENCL_BLAS_NON_UNIT_DIAGONAL clblasNonUnit // Initialization of the OpenCL BLAS library +// Only meant to be once and from constructor +// of DeviceManager singleton +// DONT'T CALL FROM ANY OTHER LOCATION inline void gpu_blas_init() { - static std::once_flag clblasSetupFlag; - call_once(clblasSetupFlag, clblasSetup); + clblasSetup(); +} + +// tear down of the OpenCL BLAS library +// Only meant to be called from destructor +// of DeviceManager singleton +// DONT'T CALL FROM ANY OTHER LOCATION +inline void gpu_blas_deinit() +{ +#ifndef OS_WIN + // FIXME: + // clblasTeardown() causes a "Pure Virtual Function Called" crash on + // Windows for Intel devices. This causes tests to fail. + clblasTeardown(); +#endif } #define clblasSherk(...) clblasSsyrk(__VA_ARGS__) diff --git a/src/backend/opencl/magma/magma_blas_clblast.h b/src/backend/opencl/magma/magma_blas_clblast.h index 7584aeee77..1917347600 100644 --- a/src/backend/opencl/magma/magma_blas_clblast.h +++ b/src/backend/opencl/magma/magma_blas_clblast.h @@ -60,11 +60,23 @@ template <> std::complex inline toCLBlastConstant(cfloat val) { return {v template <> std::complex inline toCLBlastConstant(cdouble val) { return {val.s[0], val.s[1]}; } // Initialization of the OpenCL BLAS library +// Only meant to be once and from constructor +// of DeviceManager singleton +// DONT'T CALL FROM ANY OTHER LOCATION inline void gpu_blas_init() { // Nothing to do here for CLBlast } +// tear down of the OpenCL BLAS library +// Only meant to be called from destructor +// of DeviceManager singleton +// DONT'T CALL FROM ANY OTHER LOCATION +inline void gpu_blas_deinit() +{ + // Nothing to do here for CLBlast +} + template struct gpu_blas_gemm_func { diff --git a/src/backend/opencl/memory.cpp b/src/backend/opencl/memory.cpp index 59f8c31154..097bf1aadd 100644 --- a/src/backend/opencl/memory.cpp +++ b/src/backend/opencl/memory.cpp @@ -8,16 +8,10 @@ ********************************************************/ #include -#include -#include -#include -#include -#include +#include #include #include -#include - #ifndef AF_MEM_DEBUG #define AF_MEM_DEBUG 0 #endif @@ -28,228 +22,104 @@ namespace opencl { - -class MemoryManager : public common::MemoryManager -{ - int getActiveDeviceId(); - size_t getMaxMemorySize(int id); -public: - MemoryManager(); - void *nativeAlloc(const size_t bytes); - void nativeFree(void *ptr); - ~MemoryManager() - { - common::lock_guard_t lock(this->memory_mutex); - for (int n = 0; n < getDeviceCount(); n++) { - opencl::setDevice(n); - this->garbageCollect(); - } - } -}; - -class MemoryManagerPinned : public common::MemoryManager -{ - std::vector< - std::map - > pinned_maps; - int getActiveDeviceId(); - size_t getMaxMemorySize(int id); - -public: - - MemoryManagerPinned(); - - void *nativeAlloc(const size_t bytes); - void nativeFree(void *ptr); - - ~MemoryManagerPinned() - { - common::lock_guard_t lock(this->memory_mutex); - for (int n = 0; n < getDeviceCount(); n++) { - opencl::setDevice(n); - this->garbageCollect(); - auto pinned_curr_iter = pinned_maps[n].begin(); - auto pinned_end_iter = pinned_maps[n].end(); - while (pinned_curr_iter != pinned_end_iter) { - pinned_maps[n].erase(pinned_curr_iter++); - } - } - } -}; - -int MemoryManager::getActiveDeviceId() -{ - return opencl::getActiveDeviceId(); -} - -size_t MemoryManager::getMaxMemorySize(int id) -{ - return opencl::getDeviceMemorySize(id); -} - -MemoryManager::MemoryManager() : - common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, AF_MEM_DEBUG || AF_OPENCL_MEM_DEBUG) -{ - this->setMaxMemorySize(); -} - -void *MemoryManager::nativeAlloc(const size_t bytes) -{ - return (void *)(new cl::Buffer(getContext(), CL_MEM_READ_WRITE, bytes)); -} - -void MemoryManager::nativeFree(void *ptr) -{ - delete (cl::Buffer *)ptr; -} - -static MemoryManager &getMemoryManager() -{ - static MemoryManager instance; - return instance; -} - -int MemoryManagerPinned::getActiveDeviceId() -{ - return opencl::getActiveDeviceId(); -} - -size_t MemoryManagerPinned::getMaxMemorySize(int id) -{ - return opencl::getDeviceMemorySize(id); -} - -MemoryManagerPinned::MemoryManagerPinned() : - common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, AF_MEM_DEBUG || AF_OPENCL_MEM_DEBUG), - pinned_maps(getDeviceCount()) -{ - this->setMaxMemorySize(); -} - -void *MemoryManagerPinned::nativeAlloc(const size_t bytes) -{ - void *ptr = NULL; - cl::Buffer buf= cl::Buffer(getContext(), CL_MEM_ALLOC_HOST_PTR, bytes); - ptr = getQueue().enqueueMapBuffer(buf, true, CL_MAP_READ | CL_MAP_WRITE, 0, bytes); - pinned_maps[opencl::getActiveDeviceId()][ptr] = buf; - return ptr; -} - -void MemoryManagerPinned::nativeFree(void *ptr) -{ - int n = opencl::getActiveDeviceId(); - auto iter = pinned_maps[n].find(ptr); - - if (iter != pinned_maps[n].end()) { - getQueue().enqueueUnmapMemObject(pinned_maps[n][ptr], ptr); - pinned_maps[n].erase(iter); - } -} - -static MemoryManagerPinned &getMemoryManagerPinned() -{ - static MemoryManagerPinned instance; - return instance; -} - void setMemStepSize(size_t step_bytes) { - getMemoryManager().setMemStepSize(step_bytes); + memoryManager().setMemStepSize(step_bytes); } size_t getMemStepSize(void) { - return getMemoryManager().getMemStepSize(); + return memoryManager().getMemStepSize(); } size_t getMaxBytes() { - return getMemoryManager().getMaxBytes(); + return memoryManager().getMaxBytes(); } unsigned getMaxBuffers() { - return getMemoryManager().getMaxBuffers(); + return memoryManager().getMaxBuffers(); } void garbageCollect() { - getMemoryManager().garbageCollect(); + memoryManager().garbageCollect(); } void printMemInfo(const char *msg, const int device) { - getMemoryManager().printInfo(msg, device); + memoryManager().printInfo(msg, device); } template T* memAlloc(const size_t &elements) { - return (T *)getMemoryManager().alloc(elements * sizeof(T), false); + return (T *)memoryManager().alloc(elements * sizeof(T), false); } void* memAllocUser(const size_t &bytes) { - return getMemoryManager().alloc(bytes, true); + return memoryManager().alloc(bytes, true); } template void memFree(T *ptr) { - return getMemoryManager().unlock((void *)ptr, false); + return memoryManager().unlock((void *)ptr, false); } void memFreeUser(void *ptr) { - getMemoryManager().unlock((void *)ptr, true); + memoryManager().unlock((void *)ptr, true); } cl::Buffer *bufferAlloc(const size_t &bytes) { - return (cl::Buffer *)getMemoryManager().alloc(bytes, false); + return (cl::Buffer *)memoryManager().alloc(bytes, false); } void bufferFree(cl::Buffer *buf) { - return getMemoryManager().unlock((void *)buf, false); + return memoryManager().unlock((void *)buf, false); } void memLock(const void *ptr) { - getMemoryManager().userLock((void *)ptr); + memoryManager().userLock((void *)ptr); } void memUnlock(const void *ptr) { - getMemoryManager().userUnlock((void *)ptr); + memoryManager().userUnlock((void *)ptr); } bool isLocked(const void *ptr) { - return getMemoryManager().isUserLocked((void *)ptr); + return memoryManager().isUserLocked((void *)ptr); } void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, size_t *lock_bytes, size_t *lock_buffers) { - getMemoryManager().bufferInfo(alloc_bytes, alloc_buffers, + memoryManager().bufferInfo(alloc_bytes, alloc_buffers, lock_bytes, lock_buffers); } template T* pinnedAlloc(const size_t &elements) { - return (T *)getMemoryManagerPinned().alloc(elements * sizeof(T), false); + return (T *)pinnedMemoryManager().alloc(elements * sizeof(T), false); } template void pinnedFree(T* ptr) { - return getMemoryManagerPinned().unlock((void *)ptr, false); + return pinnedMemoryManager().unlock((void *)ptr, false); } bool checkMemoryLimit() { - return getMemoryManager().checkMemoryLimit(); + return memoryManager().checkMemoryLimit(); } #define INSTANTIATE(T) \ @@ -270,4 +140,99 @@ bool checkMemoryLimit() INSTANTIATE(uintl) INSTANTIATE(short) INSTANTIATE(ushort) + +MemoryManager::MemoryManager() + : common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, + AF_MEM_DEBUG || AF_OPENCL_MEM_DEBUG) +{ + this->setMaxMemorySize(); +} + +MemoryManager::~MemoryManager() +{ + common::lock_guard_t lock(this->memory_mutex); + for (int n = 0; n < opencl::getDeviceCount(); n++) { + try { + opencl::setDevice(n); + garbageCollect(); + } catch(AfError err) { + continue; // Do not throw any errors while shutting down + } + } +} + +int MemoryManager::getActiveDeviceId() +{ + return opencl::getActiveDeviceId(); +} + +size_t MemoryManager::getMaxMemorySize(int id) +{ + return opencl::getDeviceMemorySize(id); +} + +void *MemoryManager::nativeAlloc(const size_t bytes) +{ + return (void *)(new cl::Buffer(getContext(), CL_MEM_READ_WRITE, bytes)); +} + +void MemoryManager::nativeFree(void *ptr) +{ + delete (cl::Buffer *)ptr; +} + +MemoryManagerPinned::MemoryManagerPinned() + : common::MemoryManager(getDeviceCount(), common::MAX_BUFFERS, + AF_MEM_DEBUG || AF_OPENCL_MEM_DEBUG), + pinnedMaps(getDeviceCount()) +{ + this->setMaxMemorySize(); +} + +MemoryManagerPinned::~MemoryManagerPinned() +{ + common::lock_guard_t lock(this->memory_mutex); + for (int n = 0; n < getDeviceCount(); n++) { + opencl::setDevice(n); + this->garbageCollect(); + auto currIterator = pinnedMaps[n].begin(); + auto endIterator = pinnedMaps[n].end(); + while (currIterator != endIterator) { + pinnedMaps[n].erase(currIterator++); + } + } +} + +int MemoryManagerPinned::getActiveDeviceId() +{ + return opencl::getActiveDeviceId(); +} + +size_t MemoryManagerPinned::getMaxMemorySize(int id) +{ + return opencl::getDeviceMemorySize(id); +} + +void *MemoryManagerPinned::nativeAlloc(const size_t bytes) +{ + void *ptr = NULL; + cl::Buffer* buf = new cl::Buffer(getContext(), CL_MEM_ALLOC_HOST_PTR, bytes); + ptr = getQueue().enqueueMapBuffer(*buf, true, CL_MAP_READ | CL_MAP_WRITE, 0, bytes); + pinnedMaps[opencl::getActiveDeviceId()].emplace(ptr, buf); + return ptr; +} + +void MemoryManagerPinned::nativeFree(void *ptr) +{ + int n = opencl::getActiveDeviceId(); + auto map = pinnedMaps[n]; + auto iter = map.find(ptr); + + if (iter != map.end()) { + cl::Buffer* buf = map[ptr]; + getQueue().enqueueUnmapMemObject(*buf, ptr); + delete buf; + map.erase(iter); + } +} } diff --git a/src/backend/opencl/memory.hpp b/src/backend/opencl/memory.hpp index 549dfb3f08..c6af6dfa1d 100644 --- a/src/backend/opencl/memory.hpp +++ b/src/backend/opencl/memory.hpp @@ -8,41 +8,72 @@ ********************************************************/ #pragma once -#include +#include +#include +#include +#include + +namespace cl +{ +class Buffer; //Forward declaration of cl::Buffer from CL/cl2.hpp +} namespace opencl { +cl::Buffer *bufferAlloc(const size_t &bytes); +void bufferFree(cl::Buffer *buf); - cl::Buffer *bufferAlloc(const size_t &bytes); - void bufferFree(cl::Buffer *buf); +template T* memAlloc(const size_t &elements); +void *memAllocUser(const size_t &bytes); - template T* memAlloc(const size_t &elements); - void *memAllocUser(const size_t &bytes); +// Need these as 2 separate function and not a default argument +// This is because it is used as the deleter in shared pointer +// which cannot support default arguments +template void memFree(T* ptr); +void memFreeUser(void* ptr); - // Need these as 2 separate function and not a default argument - // This is because it is used as the deleter in shared pointer - // which cannot support default arguments - template void memFree(T* ptr); - void memFreeUser(void* ptr); +void memLock(const void *ptr); +void memUnlock(const void *ptr); +bool isLocked(const void *ptr); - void memLock(const void *ptr); - void memUnlock(const void *ptr); - bool isLocked(const void *ptr); +template T* pinnedAlloc(const size_t &elements); +template void pinnedFree(T* ptr); - template T* pinnedAlloc(const size_t &elements); - template void pinnedFree(T* ptr); +size_t getMaxBytes(); +unsigned getMaxBuffers(); - size_t getMaxBytes(); - unsigned getMaxBuffers(); +void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, + size_t *lock_bytes, size_t *lock_buffers); +void garbageCollect(); +void pinnedGarbageCollect(); - void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers, - size_t *lock_bytes, size_t *lock_buffers); - void garbageCollect(); - void pinnedGarbageCollect(); +void printMemInfo(const char *msg, const int device); - void printMemInfo(const char *msg, const int device); +void setMemStepSize(size_t step_bytes); +size_t getMemStepSize(void); +bool checkMemoryLimit(); - void setMemStepSize(size_t step_bytes); - size_t getMemStepSize(void); - bool checkMemoryLimit(); +class MemoryManager : public common::MemoryManager +{ + public: + MemoryManager(); + ~MemoryManager(); + int getActiveDeviceId(); + size_t getMaxMemorySize(int id); + void *nativeAlloc(const size_t bytes); + void nativeFree(void *ptr); +}; + +class MemoryManagerPinned : public common::MemoryManager +{ + public: + MemoryManagerPinned(); + ~MemoryManagerPinned(); + int getActiveDeviceId(); + size_t getMaxMemorySize(int id); + void *nativeAlloc(const size_t bytes); + void nativeFree(void *ptr); + private: + std::vector< std::map > pinnedMaps; +}; } diff --git a/src/backend/opencl/platform.cpp b/src/backend/opencl/platform.cpp index c3d5cf7f16..dc7e16b7b4 100644 --- a/src/backend/opencl/platform.cpp +++ b/src/backend/opencl/platform.cpp @@ -25,23 +25,27 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include #include -#include +#include + #include #include -#include -#include -#include -#include #include -#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include using std::string; using std::vector; @@ -55,7 +59,6 @@ using cl::Device; namespace opencl { - #if defined (OS_MAC) static const std::string CL_GL_SHARING_EXT = "cl_APPLE_gl_sharing"; #else @@ -81,34 +84,6 @@ int getBackend() return AF_BACKEND_OPENCL; } -DeviceManager& DeviceManager::getInstance() -{ - static DeviceManager my_instance; - return my_instance; -} - -DeviceManager::~DeviceManager() -{ - //TODO: FIXME: - // OpenCL libs on Windows platforms - // are crashing the application at program exit - // most probably a reference counting issue based - // on the investigation done so far. This problem - // doesn't seem to happen on Linux or MacOSX. - // So, clean up OpenCL resources on non-Windows platforms -#ifndef OS_WIN - for (auto q: mQueues) delete q; - for (auto d : mDevices) delete d; - for (auto c : mContexts) delete c; -#endif -} - -void DeviceManager::setContext(int device) -{ - mActiveQId = device; - mActiveCtxId = device; -} - static inline bool verify_present(std::string pname, const char *ref) { return pname.find(ref) != std::string::npos; @@ -202,7 +177,6 @@ static afcl::deviceType getDeviceTypeEnum(cl::Device dev) return (afcl::deviceType)dev.getInfo(); } - static afcl::platform getPlatformEnum(cl::Device dev) { std::string pname = getPlatformName(dev); @@ -215,132 +189,6 @@ static afcl::platform getPlatformEnum(cl::Device dev) return AFCL_PLATFORM_UNKNOWN; } - -DeviceManager::DeviceManager() - : mUserDeviceOffset(0), mActiveCtxId(0), mActiveQId(0) -{ - std::vector platforms; - Platform::get(&platforms); - - // This is all we need because the sort takes care of the order of devices -#ifdef OS_MAC - cl_device_type DEVICE_TYPES = CL_DEVICE_TYPE_GPU; -#else - cl_device_type DEVICE_TYPES = CL_DEVICE_TYPE_ALL; -#endif - - std::string deviceENV = getEnvVar("AF_OPENCL_DEVICE_TYPE"); - - if (deviceENV.compare("GPU") == 0) { - DEVICE_TYPES = CL_DEVICE_TYPE_GPU; - } else if (deviceENV.compare("CPU") == 0) { - DEVICE_TYPES = CL_DEVICE_TYPE_CPU; - } else if (deviceENV.compare("ACC") >= 0) { - DEVICE_TYPES = CL_DEVICE_TYPE_ACCELERATOR; - } - - - - // Iterate through platforms, get all available devices and store them - for (auto &platform : platforms) { - std::vector current_devices; - - try { - platform.getDevices(DEVICE_TYPES, ¤t_devices); - } catch(const cl::Error &err) { - if (err.err() != CL_DEVICE_NOT_FOUND) { - throw; - } - } - - for (auto dev : current_devices) { - mDevices.push_back(new Device(dev)); - } - } - - int nDevices = mDevices.size(); - - if (nDevices == 0) AF_ERROR("No OpenCL devices found", AF_ERR_RUNTIME); - - // Sort OpenCL devices based on default criteria - std::stable_sort(mDevices.begin(), mDevices.end(), compare_default); - - // Create contexts and queues once the sort is done - for (int i = 0; i < nDevices; i++) { - cl_platform_id device_platform = mDevices[i]->getInfo(); - cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, - (cl_context_properties)(device_platform), - 0}; - - Context *ctx = new Context(*mDevices[i], cps); - CommandQueue *cq = new CommandQueue(*ctx, *mDevices[i]); - mContexts.push_back(ctx); - mQueues.push_back(cq); - mIsGLSharingOn.push_back(false); - mDeviceTypes.push_back(getDeviceTypeEnum(*mDevices[i])); - mPlatforms.push_back(getPlatformEnum(*mDevices[i])); - } - - bool default_device_set = false; - deviceENV = getEnvVar("AF_OPENCL_DEFAULT_DEVICE"); - if(!deviceENV.empty()) { - std::stringstream s(deviceENV); - int def_device = -1; - s >> def_device; - if(def_device < 0 || def_device >= (int)nDevices) { - printf("WARNING: AF_OPENCL_DEFAULT_DEVICE is out of range\n"); - printf("Setting default device as 0\n"); - } else { - setContext(def_device); - default_device_set = true; - } - } - - deviceENV = getEnvVar("AF_OPENCL_DEFAULT_DEVICE_TYPE"); - if (!default_device_set && !deviceENV.empty()) - { - cl_device_type default_device_type = CL_DEVICE_TYPE_GPU; - if (deviceENV.compare("CPU") == 0) { - default_device_type = CL_DEVICE_TYPE_CPU; - } else if (deviceENV.compare("ACC") >= 0) { - default_device_type = CL_DEVICE_TYPE_ACCELERATOR; - } - - bool default_device_set = false; - for (int i = 0; i < nDevices; i++) { - if (mDevices[i]->getInfo() == default_device_type) { - default_device_set = true; - setContext(i); - break; - } - } - - if (!default_device_set) { - printf("WARNING: AF_OPENCL_DEFAULT_DEVICE_TYPE=%s is not available\n", - deviceENV.c_str()); - printf("Using default device as 0\n"); - } - } - -#if defined(WITH_GRAPHICS) - // Define AF_DISABLE_GRAPHICS with any value to disable initialization - std::string noGraphicsENV = getEnvVar("AF_DISABLE_GRAPHICS"); - if(noGraphicsENV.empty()) { // If AF_DISABLE_GRAPHICS is not defined - try { - /* loop over devices and replace contexts with - * OpenGL shared contexts whereever applicable */ - int devCount = mDevices.size(); - forge::Window* wHandle = graphics::ForgeManager::getInstance().getMainWindow(); - for(int i=0; igetInfo()); string dstr = device->getInfo(); @@ -422,60 +274,117 @@ std::string getPlatformName(const cl::Device &device) return platformMap(platStr); } +typedef std::pair device_id_t; + +std::pair& tlocalActiveDeviceId() +{ + // First element is active context id + // Second element is active queue id + thread_local device_id_t activeDeviceId(0, 0); + + return activeDeviceId; +} + +void setActiveContext(int device) +{ + tlocalActiveDeviceId() = std::make_pair(device, device); +} + int getDeviceCount() { - return DeviceManager::getInstance().mQueues.size(); + DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + + return devMngr.mQueues.size(); } int getActiveDeviceId() { - return DeviceManager::getInstance().mActiveQId; + // Second element is the queue id, which is + // what we mean by active device id in opencl backend + return std::get<1>(tlocalActiveDeviceId()); } int getDeviceIdFromNativeId(cl_device_id id) { DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + int nDevices = devMngr.mDevices.size(); int devId = 0; for (devId=0; devIdoperator()()) break; } + return devId; } int getActiveDeviceType() { - DeviceManager &instance = DeviceManager::getInstance(); - return instance.mDeviceTypes[instance.mActiveQId]; + device_id_t& devId = tlocalActiveDeviceId(); + + DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + + return devMngr.mDeviceTypes[std::get<1>(devId)]; } int getActivePlatform() { - DeviceManager &instance = DeviceManager::getInstance(); - return instance.mPlatforms[instance.mActiveQId]; + device_id_t& devId = tlocalActiveDeviceId(); + + DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + + return devMngr.mPlatforms[std::get<1>(devId)]; } const Context& getContext() { + device_id_t& devId = tlocalActiveDeviceId(); + DeviceManager& devMngr = DeviceManager::getInstance(); - return *(devMngr.mContexts[devMngr.mActiveCtxId]); + + common::lock_guard_t lock(devMngr.deviceMutex); + + return *(devMngr.mContexts[std::get<0>(devId)]); } CommandQueue& getQueue() { + device_id_t& devId = tlocalActiveDeviceId(); + DeviceManager& devMngr = DeviceManager::getInstance(); - return *(devMngr.mQueues[devMngr.mActiveQId]); + + common::lock_guard_t lock(devMngr.deviceMutex); + + return *(devMngr.mQueues[std::get<1>(devId)]); } const cl::Device& getDevice(int id) { + device_id_t& devId = tlocalActiveDeviceId(); + + if (id == -1) + id = std::get<1>(devId); + DeviceManager& devMngr = DeviceManager::getInstance(); - if(id == -1) id = devMngr.mActiveQId; + + common::lock_guard_t lock(devMngr.deviceMutex); + return *(devMngr.mDevices[id]); } size_t getDeviceMemorySize(int device) { + DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + const cl::Device& dev = getDevice(device); size_t msize = dev.getInfo(); return msize; @@ -523,13 +432,21 @@ bool OpenCLCPUOffload(bool forceOffloadOSX) bool isGLSharingSupported() { + device_id_t& devId = tlocalActiveDeviceId(); + DeviceManager& devMngr = DeviceManager::getInstance(); - return devMngr.mIsGLSharingOn[devMngr.mActiveQId]; + + common::lock_guard_t lock(devMngr.deviceMutex); + + return devMngr.mIsGLSharingOn[std::get<1>(devId)]; } bool isDoubleSupported(int device) { DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + return (devMngr.mDevices[device]->getInfo()>0); } @@ -539,7 +456,11 @@ void devprop(char* d_name, char* d_platform, char *d_toolkit, char* d_compute) unsigned currActiveDevId = (unsigned)getActiveDeviceId(); bool devset = false; - for (auto context : DeviceManager::getInstance().mContexts) { + DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + + for (auto context : devMngr.mContexts) { vector devices = context->getInfo(); for (auto &device : devices) { @@ -585,14 +506,15 @@ int setDevice(int device) { DeviceManager& devMngr = DeviceManager::getInstance(); + common::lock_guard_t lock(devMngr.deviceMutex); + if (device >= (int)devMngr.mQueues.size() || device>= (int)DeviceManager::MAX_DEVICES) { //throw runtime_error("@setDevice: invalid device index"); return -1; - } - else { - int old = devMngr.mActiveQId; - devMngr.setContext(device); + } else { + int old = getActiveDeviceId(); + setActiveContext(device); return old; } } @@ -621,132 +543,38 @@ bool checkExtnAvailability(const Device &pDevice, std::string pName) return ret_val; } -#if defined(WITH_GRAPHICS) -void DeviceManager::markDeviceForInterop(const int device, const forge::Window* wHandle) +void addDeviceContext(cl_device_id dev, cl_context ctx, cl_command_queue que) { - try { - if (device >= (int)mQueues.size() || - device>= (int)DeviceManager::MAX_DEVICES) { - throw cl::Error(CL_INVALID_DEVICE, "Invalid device passed for CL-GL Interop"); - } else { - mQueues[device]->finish(); + clRetainDevice(dev); + clRetainContext(ctx); + clRetainCommandQueue(que); - // check if the device has CL_GL sharing extension enabled - bool temp = checkExtnAvailability(*mDevices[device], CL_GL_SHARING_EXT); - if (!temp) { - /* return silently if given device has not OpenGL sharing extension - * enabled so that regular queue is used for it */ - return; - } + DeviceManager& devMngr = DeviceManager::getInstance(); - // call forge to get OpenGL sharing context and details - cl::Platform plat(mDevices[device]->getInfo()); + common::lock_guard_t lock(devMngr.deviceMutex); -#ifdef OS_MAC - CGLContextObj cgl_current_ctx = CGLGetCurrentContext(); - CGLShareGroupObj cgl_share_group = CGLGetShareGroup(cgl_current_ctx); + cl::Device* tDevice = new cl::Device(dev); + cl::Context* tContext = new cl::Context(ctx); + cl::CommandQueue* tQueue = (que==NULL ? + new cl::CommandQueue(*tContext, *tDevice) : new cl::CommandQueue(que)); + devMngr.mDevices.push_back(tDevice); + devMngr.mContexts.push_back(tContext); + devMngr.mQueues.push_back(tQueue); + devMngr.mPlatforms.push_back(getPlatformEnum(*tDevice)); + // FIXME: add OpenGL Interop for user provided contexts later + devMngr.mIsGLSharingOn.push_back(false); - cl_context_properties cps[] = { - CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)cgl_share_group, - 0 - }; -#else - cl_context_properties cps[] = { - CL_GL_CONTEXT_KHR, (cl_context_properties)wHandle->context(), -#if defined(_WIN32) || defined(_MSC_VER) - CL_WGL_HDC_KHR, (cl_context_properties)wHandle->display(), -#else - CL_GLX_DISPLAY_KHR, (cl_context_properties)wHandle->display(), -#endif - CL_CONTEXT_PLATFORM, (cl_context_properties)plat(), - 0 - }; - - // Check if current OpenCL device is belongs to the OpenGL context - { - cl_context_properties test_cps[] = { - CL_GL_CONTEXT_KHR, (cl_context_properties)wHandle->context(), - CL_CONTEXT_PLATFORM, (cl_context_properties)plat(), - 0 - }; - - // Load the extension - // If cl_khr_gl_sharing is available, this function should be present - // This has been checked earlier, it comes to this point only if it is found - auto func = (clGetGLContextInfoKHR_fn) - clGetExtensionFunctionAddressForPlatform(plat(), "clGetGLContextInfoKHR"); - - // If the function doesn't load, bail early - if (!func) return; - - // Get all devices associated with opengl context - std::vector devices(16); - size_t ret = 0; - cl_int err = func(test_cps, - CL_DEVICES_FOR_GL_CONTEXT_KHR, - devices.size() * sizeof(cl_device_id), - &devices[0], - &ret); - if (err != CL_SUCCESS) return; - int num = ret / sizeof(cl_device_id); - devices.resize(num); - - // Check if current device is present in the associated devices - cl_device_id current_device = (*mDevices[device])(); - auto res = std::find(std::begin(devices), - std::end(devices), - current_device); - - if (res == std::end(devices)) return; - } -#endif - - // Change current device to use GL sharing - Context * ctx = new Context(*mDevices[device], cps); - CommandQueue * cq = new CommandQueue(*ctx, *mDevices[device]); - - // May be fixes the AMD GL issues we see on windows? -#if !defined(_WIN32) && !defined(_MSC_VER) - delete mContexts[device]; - delete mQueues[device]; -#endif - - mContexts[device] = ctx; - mQueues[device] = cq; - mIsGLSharingOn[device] = true; - } - } catch (const cl::Error &ex) { - /* If replacing the original context with GL shared context - * failes, don't throw an error and instead fall back to - * original context and use copy via host to support graphics - * on that particular OpenCL device. So mark it as no GL sharing */ - } -} -#endif - -void addDeviceContext(cl_device_id dev, cl_context ctx, cl_command_queue que) -{ - clRetainDevice(dev); - clRetainContext(ctx); - clRetainCommandQueue(que); - - DeviceManager& devMngr = DeviceManager::getInstance(); - cl::Device* tDevice = new cl::Device(dev); - cl::Context* tContext = new cl::Context(ctx); - cl::CommandQueue* tQueue = (que==NULL ? - new cl::CommandQueue(*tContext, *tDevice) : new cl::CommandQueue(que)); - devMngr.mDevices.push_back(tDevice); - devMngr.mContexts.push_back(tContext); - devMngr.mQueues.push_back(tQueue); - devMngr.mPlatforms.push_back(getPlatformEnum(*tDevice)); - // FIXME: add OpenGL Interop for user provided contexts later - devMngr.mIsGLSharingOn.push_back(false); + // Last/newly added device needs memory management + memoryManager().addMemoryManagement(devMngr.mDevices.size()-1); } void setDeviceContext(cl_device_id dev, cl_context ctx) { // FIXME: add OpenGL Interop for user provided contexts later DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + const int dCount = devMngr.mDevices.size(); for (int i=0; ioperator()()==dev && @@ -765,6 +593,9 @@ void removeDeviceContext(cl_device_id dev, cl_context ctx) } DeviceManager& devMngr = DeviceManager::getInstance(); + + common::lock_guard_t lock(devMngr.deviceMutex); + const int dCount = devMngr.mDevices.size(); int deleteIdx = -1; for (int i = 0; isecond); +} + +DeviceManager& DeviceManager::getInstance() +{ + static DeviceManager my_instance; + return my_instance; +} + +DeviceManager::~DeviceManager() +{ +#if defined(WITH_GRAPHICS) + for (int i=0; i platforms; + Platform::get(&platforms); + + // This is all we need because the sort takes care of the order of devices +#ifdef OS_MAC + cl_device_type DEVICE_TYPES = CL_DEVICE_TYPE_GPU; +#else + cl_device_type DEVICE_TYPES = CL_DEVICE_TYPE_ALL; +#endif + + std::string deviceENV = getEnvVar("AF_OPENCL_DEVICE_TYPE"); + + if (deviceENV.compare("GPU") == 0) { + DEVICE_TYPES = CL_DEVICE_TYPE_GPU; + } else if (deviceENV.compare("CPU") == 0) { + DEVICE_TYPES = CL_DEVICE_TYPE_CPU; + } else if (deviceENV.compare("ACC") >= 0) { + DEVICE_TYPES = CL_DEVICE_TYPE_ACCELERATOR; + } + + + + // Iterate through platforms, get all available devices and store them + for (auto &platform : platforms) { + std::vector current_devices; + + try { + platform.getDevices(DEVICE_TYPES, ¤t_devices); + } catch(const cl::Error &err) { + if (err.err() != CL_DEVICE_NOT_FOUND) { + throw; + } + } + + for (auto dev : current_devices) { + mDevices.push_back(new Device(dev)); + } + } + + int nDevices = mDevices.size(); + + if (nDevices == 0) AF_ERROR("No OpenCL devices found", AF_ERR_RUNTIME); + + // Sort OpenCL devices based on default criteria + std::stable_sort(mDevices.begin(), mDevices.end(), compare_default); + + // Create contexts and queues once the sort is done + for (int i = 0; i < nDevices; i++) { + cl_platform_id device_platform = mDevices[i]->getInfo(); + cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, + (cl_context_properties)(device_platform), + 0}; + + Context *ctx = new Context(*mDevices[i], cps); + CommandQueue *cq = new CommandQueue(*ctx, *mDevices[i]); + mContexts.push_back(ctx); + mQueues.push_back(cq); + mIsGLSharingOn.push_back(false); + mDeviceTypes.push_back(getDeviceTypeEnum(*mDevices[i])); + mPlatforms.push_back(getPlatformEnum(*mDevices[i])); + } + + bool default_device_set = false; + deviceENV = getEnvVar("AF_OPENCL_DEFAULT_DEVICE"); + if(!deviceENV.empty()) { + std::stringstream s(deviceENV); + int def_device = -1; + s >> def_device; + if(def_device < 0 || def_device >= (int)nDevices) { + printf("WARNING: AF_OPENCL_DEFAULT_DEVICE is out of range\n"); + printf("Setting default device as 0\n"); + } else { + setActiveContext(def_device); + default_device_set = true; + } + } + + deviceENV = getEnvVar("AF_OPENCL_DEFAULT_DEVICE_TYPE"); + if (!default_device_set && !deviceENV.empty()) + { + cl_device_type default_device_type = CL_DEVICE_TYPE_GPU; + if (deviceENV.compare("CPU") == 0) { + default_device_type = CL_DEVICE_TYPE_CPU; + } else if (deviceENV.compare("ACC") >= 0) { + default_device_type = CL_DEVICE_TYPE_ACCELERATOR; + } + + bool default_device_set = false; + for (int i = 0; i < nDevices; i++) { + if (mDevices[i]->getInfo() == default_device_type) { + default_device_set = true; + setActiveContext(i); + break; + } + } + + if (!default_device_set) { + printf("WARNING: AF_OPENCL_DEFAULT_DEVICE_TYPE=%s is not available\n", + deviceENV.c_str()); + printf("Using default device as 0\n"); + } + } + +#if defined(WITH_GRAPHICS) + // Define AF_DISABLE_GRAPHICS with any value to disable initialization + std::string noGraphicsENV = getEnvVar("AF_DISABLE_GRAPHICS"); + if(noGraphicsENV.empty()) { // If AF_DISABLE_GRAPHICS is not defined + try { + /* loop over devices and replace contexts with + * OpenGL shared contexts whereever applicable */ + int devCount = mDevices.size(); + forge::Window* wHandle = graphics::ForgeManager::getInstance().getMainWindow(); + for(int i=0; i= (int)mQueues.size() || + device>= (int)DeviceManager::MAX_DEVICES) { + throw cl::Error(CL_INVALID_DEVICE, "Invalid device passed for CL-GL Interop"); + } else { + mQueues[device]->finish(); + + // check if the device has CL_GL sharing extension enabled + bool temp = checkExtnAvailability(*mDevices[device], CL_GL_SHARING_EXT); + if (!temp) { + /* return silently if given device has not OpenGL sharing extension + * enabled so that regular queue is used for it */ + return; + } + + // call forge to get OpenGL sharing context and details + cl::Platform plat(mDevices[device]->getInfo()); + +#ifdef OS_MAC + CGLContextObj cgl_current_ctx = CGLGetCurrentContext(); + CGLShareGroupObj cgl_share_group = CGLGetShareGroup(cgl_current_ctx); + + cl_context_properties cps[] = { + CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)cgl_share_group, + 0 + }; +#else + cl_context_properties cps[] = { + CL_GL_CONTEXT_KHR, (cl_context_properties)wHandle->context(), +#if defined(_WIN32) || defined(_MSC_VER) + CL_WGL_HDC_KHR, (cl_context_properties)wHandle->display(), +#else + CL_GLX_DISPLAY_KHR, (cl_context_properties)wHandle->display(), +#endif + CL_CONTEXT_PLATFORM, (cl_context_properties)plat(), + 0 + }; + + // Check if current OpenCL device is belongs to the OpenGL context + { + cl_context_properties test_cps[] = { + CL_GL_CONTEXT_KHR, (cl_context_properties)wHandle->context(), + CL_CONTEXT_PLATFORM, (cl_context_properties)plat(), + 0 + }; + + // Load the extension + // If cl_khr_gl_sharing is available, this function should be present + // This has been checked earlier, it comes to this point only if it is found + auto func = (clGetGLContextInfoKHR_fn) + clGetExtensionFunctionAddressForPlatform(plat(), "clGetGLContextInfoKHR"); + + // If the function doesn't load, bail early + if (!func) return; + + // Get all devices associated with opengl context + std::vector devices(16); + size_t ret = 0; + cl_int err = func(test_cps, + CL_DEVICES_FOR_GL_CONTEXT_KHR, + devices.size() * sizeof(cl_device_id), + &devices[0], + &ret); + if (err != CL_SUCCESS) return; + int num = ret / sizeof(cl_device_id); + devices.resize(num); + + // Check if current device is present in the associated devices + cl_device_id current_device = (*mDevices[device])(); + auto res = std::find(std::begin(devices), + std::end(devices), + current_device); + + if (res == std::end(devices)) return; + } +#endif + + // Change current device to use GL sharing + Context * ctx = new Context(*mDevices[device], cps); + CommandQueue * cq = new CommandQueue(*ctx, *mDevices[device]); + + // May be fixes the AMD GL issues we see on windows? +#if !defined(_WIN32) && !defined(_MSC_VER) + delete mContexts[device]; + delete mQueues[device]; +#endif + + mContexts[device] = ctx; + mQueues[device] = cq; + mIsGLSharingOn[device] = true; + } + } catch (const cl::Error &ex) { + /* If replacing the original context with GL shared context + * failes, don't throw an error and instead fall back to + * original context and use copy via host to support graphics + * on that particular OpenCL device. So mark it as no GL sharing */ + } +} +#endif } using namespace opencl; diff --git a/src/backend/opencl/platform.hpp b/src/backend/opencl/platform.hpp index f1ed903974..1d132ca820 100644 --- a/src/backend/opencl/platform.hpp +++ b/src/backend/opencl/platform.hpp @@ -22,20 +22,111 @@ #include #pragma GCC diagnostic pop +#include #include #include +#include +#include +#include +#include +#include + namespace opencl { +int getBackend(); + +std::string getDeviceInfo(); + +int getDeviceCount(); + +int getActiveDeviceId(); + +unsigned getMaxJitSize(); + +const cl::Context& getContext(); + +cl::CommandQueue& getQueue(); + +const cl::Device& getDevice(int id = -1); + +size_t getDeviceMemorySize(int device); + +size_t getHostMemorySize(); + +cl_device_type getDeviceType(); + +bool isHostUnifiedMemory(const cl::Device &device); + +bool OpenCLCPUOffload(bool forceOffloadOSX = true); + +bool isGLSharingSupported(); + +bool isDoubleSupported(int device); + +void devprop(char* d_name, char* d_platform, char *d_toolkit, char* d_compute); + +std::string getPlatformName(const cl::Device &device); + +int setDevice(int device); + +void addDeviceContext(cl_device_id dev, cl_context cxt, cl_command_queue que); + +void setDeviceContext(cl_device_id dev, cl_context cxt); + +void removeDeviceContext(cl_device_id dev, cl_context ctx); + +void sync(int device); + +bool synchronize_calls(); + +int getActiveDeviceType(); +int getActivePlatform(); + +bool& evalFlag(); + +///////////////////////// BEGIN Sub-Managers /////////////////// +// +MemoryManager& memoryManager(); + +MemoryManagerPinned& pinnedMemoryManager(); + +#if defined(WITH_GRAPHICS) +GraphicsResourceManager& interopManager(); +#endif + +PlanCache& fftManager(); + +void addKernelToCache(int device, const std::string& key, const kc_entry_t entry); + +void removeKernelFromCache(int device, const std::string& key); + +kc_entry_t kernelCache(int device, const std::string& key); +// +///////////////////////// END Sub-Managers ///////////////////// class DeviceManager { + friend MemoryManager& memoryManager(); + + friend MemoryManagerPinned& pinnedMemoryManager(); + +#if defined(WITH_GRAPHICS) + friend GraphicsResourceManager& interopManager(); +#endif + + friend PlanCache& fftManager(); + + friend void addKernelToCache(int device, const std::string& key, const kc_entry_t entry); + + friend void removeKernelFromCache(int device, const std::string& key); + + friend kc_entry_t kernelCache(int device, const std::string& key); + friend std::string getDeviceInfo(); friend int getDeviceCount(); - friend int getActiveDeviceId(); - friend int getDeviceIdFromNativeId(cl_device_id id); friend const cl::Context& getContext(); @@ -61,6 +152,7 @@ class DeviceManager friend void removeDeviceContext(cl_device_id dev, cl_context ctx); friend int getActiveDeviceType(); + friend int getActivePlatform(); public: @@ -71,8 +163,6 @@ class DeviceManager ~DeviceManager(); protected: - void setContext(int device); - DeviceManager(); // Following two declarations are required to @@ -87,6 +177,7 @@ class DeviceManager private: // Attributes + common::mutex_t deviceMutex; std::vector mDevices; std::vector mContexts; std::vector mQueues; @@ -95,59 +186,12 @@ class DeviceManager std::vector mPlatforms; unsigned mUserDeviceOffset; - unsigned mActiveCtxId; - unsigned mActiveQId; -}; - -int getBackend(); - -std::string getDeviceInfo(); - -int getDeviceCount(); - -int getActiveDeviceId(); - -unsigned getMaxJitSize(); - -const cl::Context& getContext(); - -cl::CommandQueue& getQueue(); - -const cl::Device& getDevice(int id = -1); - -size_t getDeviceMemorySize(int device); - -size_t getHostMemorySize(); - -cl_device_type getDeviceType(); - -bool isHostUnifiedMemory(const cl::Device &device); - -bool OpenCLCPUOffload(bool forceOffloadOSX = true); - -bool isGLSharingSupported(); - -bool isDoubleSupported(int device); - -void devprop(char* d_name, char* d_platform, char *d_toolkit, char* d_compute); - -std::string getPlatformName(const cl::Device &device); - -int setDevice(int device); - -void addDeviceContext(cl_device_id dev, cl_context cxt, cl_command_queue que); - -void setDeviceContext(cl_device_id dev, cl_context cxt); - -void removeDeviceContext(cl_device_id dev, cl_context ctx); - -void sync(int device); - -bool synchronize_calls(); - -int getActiveDeviceType(); -int getActivePlatform(); - -bool& evalFlag(); + std::unique_ptr memManager; + std::unique_ptr pinnedMemManager; +#if defined(WITH_GRAPHICS) + std::unique_ptr gfxManagers[MAX_DEVICES]; +#endif + clfftSetupData mFFTSetup; +}; } diff --git a/src/backend/opencl/plot.cpp b/src/backend/opencl/plot.cpp index e597dff270..4651d60737 100644 --- a/src/backend/opencl/plot.cpp +++ b/src/backend/opencl/plot.cpp @@ -9,12 +9,12 @@ #if defined (WITH_GRAPHICS) -#include #include -#include -#include #include +#include +#include #include +#include #include #include @@ -32,12 +32,10 @@ void copy_plot(const Array &P, forge::Plot* plot) const cl::Buffer *d_P = P.get(); size_t bytes = plot->verticesSize(); - InteropManager& intrpMngr = InteropManager::getInstance(); - - cl::Buffer **resources = intrpMngr.getBufferResource(plot); + ShrdResVector res = interopManager().getBufferResource(plot); std::vector shared_objects; - shared_objects.push_back(*resources[0]); + shared_objects.push_back(*(res[0].get())); glFinish(); @@ -47,7 +45,7 @@ void copy_plot(const Array &P, forge::Plot* plot) getQueue().enqueueAcquireGLObjects(&shared_objects, NULL, &event); event.wait(); - getQueue().enqueueCopyBuffer(*d_P, *resources[0], 0, 0, bytes, NULL, &event); + getQueue().enqueueCopyBuffer(*d_P, *(res[0].get()), 0, 0, bytes, NULL, &event); getQueue().enqueueReleaseGLObjects(&shared_objects, NULL, &event); event.wait(); diff --git a/src/backend/opencl/qr.cpp b/src/backend/opencl/qr.cpp index cb730122db..2f3df19dee 100644 --- a/src/backend/opencl/qr.cpp +++ b/src/backend/opencl/qr.cpp @@ -32,7 +32,6 @@ void qr(Array &q, Array &r, Array &t, const Array &orig) return cpu::qr(q, r, t, orig); } - initBlas(); dim4 iDims = orig.dims(); int M = iDims[0]; int N = iDims[1]; @@ -86,7 +85,6 @@ Array qr_inplace(Array &in) return cpu::qr_inplace(in); } - initBlas(); dim4 iDims = in.dims(); int M = iDims[0]; int N = iDims[1]; diff --git a/src/backend/opencl/solve.cpp b/src/backend/opencl/solve.cpp index 06b8b67c6e..afe26a3fe1 100644 --- a/src/backend/opencl/solve.cpp +++ b/src/backend/opencl/solve.cpp @@ -306,8 +306,6 @@ Array solve(const Array &a, const Array &b, const af_mat_prop options) return cpu::solve(a, b, options); } - initBlas(); - if (options & AF_MAT_UPPER || options & AF_MAT_LOWER) { return triangleSolve(a, b, options); diff --git a/src/backend/opencl/surface.cpp b/src/backend/opencl/surface.cpp index 8a54a8e3f6..ef7782c561 100644 --- a/src/backend/opencl/surface.cpp +++ b/src/backend/opencl/surface.cpp @@ -9,14 +9,14 @@ #if defined (WITH_GRAPHICS) -#include #include -#include -#include #include +#include +#include #include #include #include +#include using af::dim4; @@ -32,12 +32,10 @@ void copy_surface(const Array &P, forge::Surface* surface) const cl::Buffer *d_P = P.get(); size_t bytes = surface->verticesSize(); - InteropManager& intrpMngr = InteropManager::getInstance(); - - cl::Buffer **resources = intrpMngr.getBufferResource(surface); + ShrdResVector res = interopManager().getBufferResource(surface); std::vector shared_objects; - shared_objects.push_back(*resources[0]); + shared_objects.push_back(*(res[0].get())); glFinish(); @@ -47,7 +45,7 @@ void copy_surface(const Array &P, forge::Surface* surface) getQueue().enqueueAcquireGLObjects(&shared_objects, NULL, &event); event.wait(); - getQueue().enqueueCopyBuffer(*d_P, *resources[0], 0, 0, bytes, NULL, &event); + getQueue().enqueueCopyBuffer(*d_P, *(res[0].get()), 0, 0, bytes, NULL, &event); getQueue().enqueueReleaseGLObjects(&shared_objects, NULL, &event); event.wait(); diff --git a/src/backend/opencl/svd.cpp b/src/backend/opencl/svd.cpp index 61da27bdcd..bc0043ee10 100644 --- a/src/backend/opencl/svd.cpp +++ b/src/backend/opencl/svd.cpp @@ -202,7 +202,6 @@ void svdInPlace(Array &s, Array &u, Array &vt, Array &in) return cpu::svdInPlace(s, u, vt, in); } - initBlas(); svd(u, s, vt, in, true); } diff --git a/src/backend/opencl/types.hpp b/src/backend/opencl/types.hpp index 5742c5d8c9..4490f2bb36 100644 --- a/src/backend/opencl/types.hpp +++ b/src/backend/opencl/types.hpp @@ -23,89 +23,87 @@ namespace opencl { +typedef cl_float2 cfloat; +typedef cl_double2 cdouble; +typedef cl_uchar uchar; +typedef cl_uint uint; +typedef cl_ushort ushort; - typedef cl_float2 cfloat; - typedef cl_double2 cdouble; - typedef cl_uchar uchar; - typedef cl_uint uint; - typedef cl_ushort ushort; +template struct is_complex { static const bool value = false; }; +template<> struct is_complex { static const bool value = true; }; +template<> struct is_complex { static const bool value = true; }; - template struct is_complex { static const bool value = false; }; - template<> struct is_complex { static const bool value = true; }; - template<> struct is_complex { static const bool value = true; }; +template const char *shortname(bool caps=false); - template const char *shortname(bool caps=false); - - template - struct ToNumStr +template +struct ToNumStr +{ + inline std::string operator()(T val) { - inline std::string operator()(T val) - { - ToNum toNum; - return std::to_string(toNum(val)); - } - }; + ToNum toNum; + return std::to_string(toNum(val)); + } +}; - template<> - struct ToNumStr +template<> +struct ToNumStr +{ + inline std::string operator()(float val) { - inline std::string operator()(float val) - { - static const std::string PINF = "+INFINITY"; - static const std::string NINF = "-INFINITY"; - if (std::isinf(val)) { - return val < 0 ? NINF : PINF; - } - return std::to_string(val); + static const std::string PINF = "+INFINITY"; + static const std::string NINF = "-INFINITY"; + if (std::isinf(val)) { + return val < 0 ? NINF : PINF; } - }; + return std::to_string(val); + } +}; - template<> - struct ToNumStr +template<> +struct ToNumStr +{ + inline std::string operator()(double val) { - inline std::string operator()(double val) - { - static const std::string PINF = "+INFINITY"; - static const std::string NINF = "-INFINITY"; - if (std::isinf(val)) { - return val < 0 ? NINF : PINF; - } - return std::to_string(val); + static const std::string PINF = "+INFINITY"; + static const std::string NINF = "-INFINITY"; + if (std::isinf(val)) { + return val < 0 ? NINF : PINF; } - }; + return std::to_string(val); + } +}; - template<> - struct ToNumStr +template<> +struct ToNumStr +{ + inline std::string operator()(cfloat val) { - inline std::string operator()(cfloat val) - { - ToNumStr realStr; - static const std::string INF = "INFINITY"; - std::stringstream s; - s << "{"; - s << realStr(val.s[0]); - s << ","; - s << realStr(val.s[1]); - s << "}"; - return s.str(); - } - }; + ToNumStr realStr; + static const std::string INF = "INFINITY"; + std::stringstream s; + s << "{"; + s << realStr(val.s[0]); + s << ","; + s << realStr(val.s[1]); + s << "}"; + return s.str(); + } +}; - template<> - struct ToNumStr +template<> +struct ToNumStr +{ + inline std::string operator()(cdouble val) { - inline std::string operator()(cdouble val) - { - ToNumStr realStr; - static const std::string INF = "INFINITY"; - std::stringstream s; - s << "{"; - s << realStr(val.s[0]); - s << ","; - s << realStr(val.s[1]); - s << "}"; - return s.str(); - } - }; - + ToNumStr realStr; + static const std::string INF = "INFINITY"; + std::stringstream s; + s << "{"; + s << realStr(val.s[0]); + s << ","; + s << realStr(val.s[1]); + s << "}"; + return s.str(); + } +}; } diff --git a/src/backend/opencl/vector_field.cpp b/src/backend/opencl/vector_field.cpp index 5f35437e95..c53279aa0c 100644 --- a/src/backend/opencl/vector_field.cpp +++ b/src/backend/opencl/vector_field.cpp @@ -9,11 +9,11 @@ #if defined (WITH_GRAPHICS) -#include #include -#include -#include #include +#include +#include +#include using af::dim4; @@ -32,13 +32,11 @@ void copy_vector_field(const Array &points, const Array &directions, size_t pBytes = vector_field->verticesSize(); size_t dBytes = vector_field->directionsSize(); - InteropManager& intrpMngr = InteropManager::getInstance(); - - cl::Buffer **resources = intrpMngr.getBufferResource(vector_field); + ShrdResVector res = interopManager().getBufferResource(vector_field); std::vector shared_objects; - shared_objects.push_back(*resources[0]); - shared_objects.push_back(*resources[1]); + shared_objects.push_back(*(res[0].get())); + shared_objects.push_back(*(res[1].get())); glFinish(); @@ -48,8 +46,8 @@ void copy_vector_field(const Array &points, const Array &directions, getQueue().enqueueAcquireGLObjects(&shared_objects, NULL, &event); event.wait(); - getQueue().enqueueCopyBuffer(*d_points , *resources[0], 0, 0, pBytes, NULL, &event); - getQueue().enqueueCopyBuffer(*d_directions, *resources[1], 0, 0, dBytes, NULL, &event); + getQueue().enqueueCopyBuffer(*d_points , *(res[0].get()), 0, 0, pBytes, NULL, &event); + getQueue().enqueueCopyBuffer(*d_directions, *(res[1].get()), 0, 0, dBytes, NULL, &event); getQueue().enqueueReleaseGLObjects(&shared_objects, NULL, &event); event.wait(); diff --git a/src/backend/template/.gitignore b/src/backend/template/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 65b91855e4..ed123c5781 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,6 +8,8 @@ FIND_PACKAGE(OpenCL QUIET) OPTION(BUILD_SINGLE_TEST_FILE "Build tests in a single file" OFF) +REMOVE_DEFINITIONS(-std=c++11) + # If the tests are not being built at the same time as ArrayFire, # we need to first find the ArrayFire library IF(TARGET afcpu OR TARGET afcuda OR TARGET afopencl OR TARGET af) @@ -52,8 +54,6 @@ ELSE() ENABLE_TESTING() ENDIF() -REMOVE_DEFINITIONS(-std=c++11) - MACRO(CREATE_TESTS BACKEND AFLIBNAME GTEST_LIBS OTHER_LIBS) STRING(TOUPPER ${BACKEND} DEF_NAME) @@ -75,6 +75,7 @@ MACRO(CREATE_TESTS BACKEND AFLIBNAME GTEST_LIBS OTHER_LIBS) SET(TEST_NAME_BASIC test_basic_${BACKEND}) ADD_EXECUTABLE(${TEST_NAME} ${CPP_FILES}) ADD_EXECUTABLE(${TEST_NAME_BASIC} basic_c.c) + SET_TARGET_PROPERTIES(${TEST_NAME} PROPERTIES COMPILE_OPTIONS -std=c++11) TARGET_LINK_LIBRARIES(${TEST_NAME} PRIVATE ${AFLIBNAME} PRIVATE ${THREAD_LIB_FLAG} @@ -120,6 +121,10 @@ MACRO(CREATE_TESTS BACKEND AFLIBNAME GTEST_LIBS OTHER_LIBS) COMPILE_FLAGS -DAF_${DEF_NAME} FOLDER "Tests/${BACKEND}") + IF (${FNAME} MATCHES "threading|solve") + SET_TARGET_PROPERTIES(${TEST_NAME} PROPERTIES COMPILE_OPTIONS -std=c++11) + ENDIF() + IF(TEST_LINK_FLAGS) SET_TARGET_PROPERTIES(${TEST_NAME} PROPERTIES LINK_FLAGS ${TEST_LINK_FLAGS}) ENDIF(TEST_LINK_FLAGS) diff --git a/test/bilateral.cpp b/test/bilateral.cpp index cde330dca4..2362e1c134 100644 --- a/test/bilateral.cpp +++ b/test/bilateral.cpp @@ -52,13 +52,13 @@ void bilateralTest(string pTestFile) ASSERT_EQ(AF_SUCCESS, af_bilateral(&outArray, inArray, 2.25f, 25.56f, isColor)); - T * outData = new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + std::vector outData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); - T * goldData= new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData, goldArray)); + std::vector goldData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData.data(), goldArray)); - ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 0.02f)); + ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(), outData.data(), 0.02f)); ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); @@ -103,25 +103,23 @@ void bilateralDataTest(string pTestFile) af::dim4 dims = numDims[0]; af_array outArray = 0; af_array inArray = 0; - outType *outData; ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &(in[0].front()), dims.ndims(), dims.get(), (af_dtype)af::dtype_traits::af_type)); ASSERT_EQ(AF_SUCCESS, af_bilateral(&outArray, inArray, 2.25f, 25.56f, false)); - outData = new outType[dims.elements()]; + std::vector outData(dims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t testIter=0; testIter currGoldBar = tests[testIter]; size_t nElems = currGoldBar.size(); - ASSERT_EQ(true, compareArraysRMSD(nElems, &currGoldBar.front(), outData, 0.02f)); + ASSERT_EQ(true, compareArraysRMSD(nElems, &currGoldBar.front(), outData.data(), 0.02f)); } // cleanup - delete[] outData; ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); } @@ -171,17 +169,14 @@ TEST(Bilateral, CPP) array a(dims, &(in[0].front())); array b = af::bilateral(a, 2.25f, 25.56f, false); - float *outData = new float[dims.elements()]; - b.host(outData); + std::vector outData(dims.elements()); + b.host(outData.data()); for (size_t testIter=0; testIter currGoldBar = tests[testIter]; size_t nElems = currGoldBar.size(); - ASSERT_EQ(true, compareArraysRMSD(nElems, &currGoldBar.front(), outData, 0.02f)); + ASSERT_EQ(true, compareArraysRMSD(nElems, currGoldBar.data(), outData.data(), 0.02f)); } - - // cleanup - delete[] outData; } diff --git a/test/canny.cpp b/test/canny.cpp index 16dd5d5171..19e159edaa 100644 --- a/test/canny.cpp +++ b/test/canny.cpp @@ -51,9 +51,9 @@ void cannyTest(string pTestFile) ASSERT_EQ(AF_SUCCESS, af_canny(&outArray, sArray, AF_MANUAL_THRESHOLD, 0.4147f, 0.8454f, 3, true)); - char *outData = new char[sDims.elements()]; + std::vector outData(sDims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); @@ -62,7 +62,6 @@ void cannyTest(string pTestFile) } // cleanup - delete[] outData; ASSERT_EQ(AF_SUCCESS, af_release_array(sArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); } @@ -110,13 +109,13 @@ void cannyImageOtsuTest(string pTestFile, bool isColor) ASSERT_EQ(AF_SUCCESS, af_canny(&outArray, inArray, AF_AUTO_OTSU_THRESHOLD, 0.08, 0.32, 3, false)); - char * outData = new char[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + std::vector outData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); - char * goldData= new char[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData, goldArray)); + std::vector goldData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData.data(), goldArray)); - ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 1.0e-3)); + ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(), outData.data(), 1.0e-3)); ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); diff --git a/test/covariance.cpp b/test/covariance.cpp index 933f617612..224003dfb7 100644 --- a/test/covariance.cpp +++ b/test/covariance.cpp @@ -96,17 +96,14 @@ void covTest(string pFileName, bool isbiased=false) vector currGoldBar(tests[0].begin(), tests[0].end()); size_t nElems = currGoldBar.size(); - outType *outData = new outType[nElems]; + std::vector outData(nElems); - c.host((void*)outData); + c.host((void*)outData.data()); for (size_t elIter=0; elIter currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); - float *outData = new float[nElems]; + std::vector outData(nElems); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t elIter=0; elIter currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); - float *outData = new float[nElems]; + std::vector outData(nElems); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t elIter=0; elIter currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); - float *outData = new float[nElems]; + std::vector outData(nElems); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t elIter=0; elIter currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); - float *outData = new float[nElems]; + std::vector outData(nElems); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t elIter=0; elIter currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); - float *outData = new float[nElems]; + std::vector outData(nElems); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t elIter=0; elIter::af_type)); ASSERT_EQ(AF_SUCCESS,af_histogram(&outArray,inArray,nbins,minval,maxval)); - outData = new outType[dims.elements()]; + std::vector outData(dims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t testIter=0; testIter currGoldBar = tests[testIter]; @@ -66,7 +66,6 @@ void histTest(string pTestFile, unsigned nbins, double minval, double maxval) } // cleanup - delete[] outData; ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); } @@ -118,8 +117,8 @@ TEST(Histogram, CPP) af::array output = histogram(input, nbins, minval, maxval); //! [hist_nominmax] - uint *outData = new uint[output.elements()]; - output.host((void*)outData); + std::vector outData(output.elements()); + output.host((void*)outData.data()); for (size_t testIter=0; testIter currGoldBar = tests[testIter]; @@ -128,9 +127,6 @@ TEST(Histogram, CPP) ASSERT_EQ(currGoldBar[elIter],outData[elIter])<< "at: " << elIter<< std::endl; } } - - // cleanup - delete[] outData; } /////////////////////////////////// Documentation Snippets ////////////////////////////////// diff --git a/test/hsv_rgb.cpp b/test/hsv_rgb.cpp index 5e221c2e6d..cd057b0152 100644 --- a/test/hsv_rgb.cpp +++ b/test/hsv_rgb.cpp @@ -45,17 +45,14 @@ TEST(hsv2rgb, CPP) af::array input(dims, &(in[0].front())); af::array output = af::hsv2rgb(input); - float *outData = new float[dims.elements()]; - output.host((void*)outData); + std::vector outData(dims.elements()); + output.host((void*)outData.data()); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); for (size_t elIter=0; elIter outData(dims.elements()); + output.host((void*)outData.data()); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); for (size_t elIter=0; elIter outData(sDims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); @@ -68,7 +68,6 @@ void matchTemplateTest(string pTestFile, af_match_type pMatchType) } // cleanup - delete[] outData; ASSERT_EQ(AF_SUCCESS, af_release_array(sArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(tArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); diff --git a/test/mean.cpp b/test/mean.cpp index e3f7031747..dd370f4e93 100644 --- a/test/mean.cpp +++ b/test/mean.cpp @@ -92,9 +92,9 @@ void meanDimTest(string pFileName, dim_t dim, bool isWeighted=false) af::array outArray = af::mean(inArray, dim); - outType *outData = new outType[dims.elements()]; + std::vector outData(dims.elements()); - outArray.host((void*)outData); + outArray.host((void*)outData.data()); vector currGoldBar(tests[0].begin(), tests[0].end()); size_t nElems = currGoldBar.size(); @@ -102,9 +102,6 @@ void meanDimTest(string pFileName, dim_t dim, bool isWeighted=false) ASSERT_NEAR(::real(currGoldBar[elIter]), ::real(outData[elIter]), 1.0e-3)<< "at: " << elIter<< std::endl; ASSERT_NEAR(::imag(currGoldBar[elIter]), ::imag(outData[elIter]), 1.0e-3)<< "at: " << elIter<< std::endl; } - - // cleanup - delete[] outData; } else { af::dim4 dims = numDims[0]; af::dim4 wdims = numDims[1]; @@ -116,9 +113,9 @@ void meanDimTest(string pFileName, dim_t dim, bool isWeighted=false) af::array outArray = af::mean(inArray, wtsArray, dim); - outType *outData = new outType[dims.elements()]; + std::vector outData(dims.elements()); - outArray.host((void*)outData); + outArray.host((void*)outData.data()); vector currGoldBar(tests[0].begin(), tests[0].end()); size_t nElems = currGoldBar.size(); @@ -126,9 +123,6 @@ void meanDimTest(string pFileName, dim_t dim, bool isWeighted=false) ASSERT_NEAR(::real(currGoldBar[elIter]), ::real(outData[elIter]), 1.0e-3)<< "at: " << elIter<< std::endl; ASSERT_NEAR(::imag(currGoldBar[elIter]), ::imag(outData[elIter]), 1.0e-3)<< "at: " << elIter<< std::endl; } - - // cleanup - delete[] outData; } } diff --git a/test/meanshift.cpp b/test/meanshift.cpp index a35ca288d9..c9fe41af29 100644 --- a/test/meanshift.cpp +++ b/test/meanshift.cpp @@ -84,13 +84,13 @@ void meanshiftTest(string pTestFile) ASSERT_EQ(AF_SUCCESS, af_mean_shift(&outArray, inArray, 2.25f, 25.56f, 5, isColor)); - T * outData = new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + std::vector outData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); - T * goldData= new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData, goldArray)); + std::vector goldData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData.data(), goldArray)); - ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 0.07f)); + ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(), outData.data(), 0.07f)); ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(inArray_f32)); @@ -145,16 +145,13 @@ TEST(Meanshift, Color_CPP) dim_t nElems = gold.elements(); af::array output= af::meanShift(img, 2.25f, 25.56f, 5, true); - float * outData = new float[nElems]; - output.host((void*)outData); + std::vector outData(nElems); + output.host((void*)outData.data()); - float * goldData= new float[nElems]; - gold.host((void*)goldData); + std::vector goldData(nElems); + gold.host((void*)goldData.data()); - ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 0.07f)); - // cleanup - delete[] outData; - delete[] goldData; + ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(), outData.data(), 0.07f)); } } diff --git a/test/medfilt.cpp b/test/medfilt.cpp index 1296e22b0c..e41e7a45cb 100644 --- a/test/medfilt.cpp +++ b/test/medfilt.cpp @@ -60,9 +60,9 @@ void medfiltTest(string pTestFile, dim_t w_len, dim_t w_wid, af_border_type pad) ASSERT_EQ(AF_SUCCESS, af_medfilt2(&outArray, inArray, w_len, w_wid, pad)); - T *outData = new T[dims.elements()]; + std::vector outData(dims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); @@ -71,7 +71,6 @@ void medfiltTest(string pTestFile, dim_t w_len, dim_t w_wid, af_border_type pad) } // cleanup - delete[] outData; ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); } @@ -117,9 +116,9 @@ void medfilt1_Test(string pTestFile, dim_t w_wid, af_border_type pad) ASSERT_EQ(AF_SUCCESS, af_medfilt1(&outArray, inArray, w_wid, pad)); - T *outData = new T[dims.elements()]; + std::vector outData(dims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); @@ -128,7 +127,6 @@ void medfilt1_Test(string pTestFile, dim_t w_wid, af_border_type pad) } // cleanup - delete[] outData; ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); } @@ -186,13 +184,13 @@ void medfiltImageTest(string pTestFile, dim_t w_len, dim_t w_wid) ASSERT_EQ(AF_SUCCESS, af_medfilt2(&outArray, inArray, w_len, w_wid, AF_PAD_ZERO)); - T * outData = new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + std::vector outData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); - T * goldData= new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData, goldArray)); + std::vector goldData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData.data(), goldArray)); - ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 0.018f)); + ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(), outData.data(), 0.018f)); ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); @@ -224,6 +222,7 @@ void medfiltInputTest(void) ASSERT_EQ(true, medfilt1); ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); + ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); } TYPED_TEST(MedianFilter, InvalidArray) @@ -360,17 +359,14 @@ TEST(MedianFilter, CPP) af::array input(dims, &(in[0].front())); af::array output = af::medfilt(input, w_len, w_wid, AF_PAD_SYM); - float *outData = new float[dims.elements()]; - output.host((void*)outData); + std::vector outData(dims.elements()); + output.host((void*)outData.data()); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); for (size_t elIter=0; elIter outData(dims.elements()); + output.host((void*)outData.data()); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); for (size_t elIter=0; elIter::af_type)); @@ -69,9 +68,9 @@ void morphTest(string pTestFile) ASSERT_EQ(AF_SUCCESS, af_erode(&outArray, inArray, maskArray)); } - outData = new inType[dims.elements()]; + std::vector outData(dims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); for (size_t testIter=0; testIter currGoldBar = tests[testIter]; @@ -82,7 +81,6 @@ void morphTest(string pTestFile) } // cleanup - delete[] outData; ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(maskArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(outArray)); @@ -159,13 +157,13 @@ void morphImageTest(string pTestFile) else ASSERT_EQ(AF_SUCCESS, af_erode(&outArray, inArray, maskArray)); - T * outData = new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + std::vector outData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData.data(), outArray)); - T * goldData= new T[nElems]; - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData, goldArray)); + std::vector goldData(nElems); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData.data(), goldArray)); - ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 0.018f)); + ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(), outData.data(), 0.018f)); ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(maskArray)); @@ -371,16 +369,13 @@ void cppMorphImageTest(string pTestFile) else output = erode(img, mask); - T * outData = new T[nElems]; - output.host((void*)outData); + std::vector outData(nElems); + output.host((void*)outData.data()); - T * goldData= new T[nElems]; - gold.host((void*)goldData); + std::vector goldData(nElems); + gold.host((void*)goldData.data()); - ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 0.018f)); - //cleanup - delete[] outData; - delete[] goldData; + ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(), outData.data(), 0.018f)); } } @@ -452,8 +447,8 @@ TEST(Morph, EdgeIssue1564) array dilated = dilate(input.as(b8), mask.as(b8)); size_t nElems = dilated.elements(); - char * outData = new char[nElems]; - dilated.host((void*)outData); + std::vector outData(nElems); + dilated.host((void*)outData.data()); for (size_t i=0; i dxData(dims.elements()); + std::vector dyData(dims.elements()); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)dxData, dxArray)); - ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)dyData, dyArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)dxData.data(), dxArray)); + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)dyData.data(), dyArray)); vector currDXGoldBar = tests[0]; vector currDYGoldBar = tests[1]; @@ -79,8 +79,6 @@ void testSobelDerivatives(string pTestFile) } // cleanup - delete[] dxData; - delete[] dyData; ASSERT_EQ(AF_SUCCESS, af_release_array(inArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(dxArray)); ASSERT_EQ(AF_SUCCESS, af_release_array(dyArray)); diff --git a/test/solve_common.hpp b/test/solve_common.hpp new file mode 100644 index 0000000000..c532cad42c --- /dev/null +++ b/test/solve_common.hpp @@ -0,0 +1,154 @@ +/******************************************************* + * Copyright (c) 2014, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +using std::vector; +using std::string; +using std::cout; +using std::endl; +using std::abs; +using af::cfloat; +using af::cdouble; + +///////////////////////////////// CPP //////////////////////////////////// +// + +template +void solveTester(const int m, const int n, const int k, double eps, int targetDevice=-1) +{ + if (targetDevice>=0) + af::setDevice(targetDevice); + + af::deviceGC(); + + if (noDoubleTests()) return; + if (noLAPACKTests()) return; + +#if 1 + af::array A = cpu_randu(af::dim4(m, n)); + af::array X0 = cpu_randu(af::dim4(n, k)); +#else + af::array A = af::randu(m, n, (af::dtype)af::dtype_traits::af_type); + af::array X0 = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); +#endif + af::array B0 = af::matmul(A, X0); + + //! [ex_solve] + af::array X1 = af::solve(A, B0); + //! [ex_solve] + + //! [ex_solve_recon] + af::array B1 = af::matmul(A, X1); + //! [ex_solve_recon] + + if(noDoubleTests()) { + ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (m * k), eps); + ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (m * k), eps); + } else { + ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (m * k), eps); + ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (m * k), eps); + } +} + +template +void solveLUTester(const int n, const int k, double eps, int targetDevice=-1) +{ + if (targetDevice>=0) + af::setDevice(targetDevice); + + af::deviceGC(); + + if (noDoubleTests()) return; + if (noLAPACKTests()) return; + +#if 1 + af::array A = cpu_randu(af::dim4(n, n)); + af::array X0 = cpu_randu(af::dim4(n, k)); +#else + af::array A = af::randu(n, n, (af::dtype)af::dtype_traits::af_type); + af::array X0 = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); +#endif + af::array B0 = af::matmul(A, X0); + + //! [ex_solve_lu] + af::array A_lu, pivot; + af::lu(A_lu, pivot, A); + af::array X1 = af::solveLU(A_lu, pivot, B0); + //! [ex_solve_lu] + + af::array B1 = af::matmul(A, X1); + + if(noDoubleTests()) { + ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (n * k), eps); + ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (n * k), eps); + } else { + ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (n * k), eps); + ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (n * k), eps); + } +} + +template +void solveTriangleTester(const int n, const int k, bool is_upper, double eps, int targetDevice=-1) +{ + if (targetDevice>=0) + af::setDevice(targetDevice); + + af::deviceGC(); + + if (noDoubleTests()) return; + if (noLAPACKTests()) return; + +#if 1 + af::array A = cpu_randu(af::dim4(n, n)); + af::array X0 = cpu_randu(af::dim4(n, k)); +#else + af::array A = af::randu(n, n, (af::dtype)af::dtype_traits::af_type); + af::array X0 = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); +#endif + + af::array L, U, pivot; + af::lu(L, U, pivot, A); + + af::array AT = is_upper ? U : L; + af::array B0 = af::matmul(AT, X0); + af::array X1; + + if (is_upper) { + //! [ex_solve_upper] + af::array X = af::solve(AT, B0, AF_MAT_UPPER); + //! [ex_solve_upper] + + X1 = X; + } else { + //! [ex_solve_lower] + af::array X = af::solve(AT, B0, AF_MAT_LOWER); + //! [ex_solve_lower] + + X1 = X; + } + + af::array B1 = af::matmul(AT, X1); + + if(noDoubleTests()) { + ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (n * k), eps); + ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (n * k), eps); + } else { + ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (n * k), eps); + ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (n * k), eps); + } +} diff --git a/test/solve_dense.cpp b/test/solve_dense.cpp index 171551e8cf..3caa656336 100644 --- a/test/solve_dense.cpp +++ b/test/solve_dense.cpp @@ -8,127 +8,9 @@ ********************************************************/ #include -#include -#include -#include -#include -#include -#include -#include -#include #include - -using std::vector; -using std::string; -using std::cout; -using std::endl; -using std::abs; -using af::cfloat; -using af::cdouble; - -///////////////////////////////// CPP //////////////////////////////////// -// - -template -void solveTester(const int m, const int n, const int k, double eps) -{ - af::deviceGC(); - - if (noDoubleTests()) return; - if (noLAPACKTests()) return; - -#if 1 - af::array A = cpu_randu(af::dim4(m, n)); - af::array X0 = cpu_randu(af::dim4(n, k)); -#else - af::array A = af::randu(m, n, (af::dtype)af::dtype_traits::af_type); - af::array X0 = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); -#endif - af::array B0 = af::matmul(A, X0); - - //! [ex_solve] - af::array X1 = af::solve(A, B0); - //! [ex_solve] - - //! [ex_solve_recon] - af::array B1 = af::matmul(A, X1); - //! [ex_solve_recon] - - ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (m * k), eps); - ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (m * k), eps); -} - -template -void solveLUTester(const int n, const int k, double eps) -{ - af::deviceGC(); - - if (noDoubleTests()) return; - if (noLAPACKTests()) return; - -#if 1 - af::array A = cpu_randu(af::dim4(n, n)); - af::array X0 = cpu_randu(af::dim4(n, k)); -#else - af::array A = af::randu(n, n, (af::dtype)af::dtype_traits::af_type); - af::array X0 = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); -#endif - af::array B0 = af::matmul(A, X0); - - //! [ex_solve_lu] - af::array A_lu, pivot; - af::lu(A_lu, pivot, A); - af::array X1 = af::solveLU(A_lu, pivot, B0); - //! [ex_solve_lu] - - af::array B1 = af::matmul(A, X1); - - ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (n * k), eps); - ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (n * k), eps); -} - -template -void solveTriangleTester(const int n, const int k, bool is_upper, double eps) -{ - af::deviceGC(); - - if (noDoubleTests()) return; - if (noLAPACKTests()) return; - -#if 1 - af::array A = cpu_randu(af::dim4(n, n)); - af::array X0 = cpu_randu(af::dim4(n, k)); -#else - af::array A = af::randu(n, n, (af::dtype)af::dtype_traits::af_type); - af::array X0 = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); -#endif - - af::array L, U, pivot; - af::lu(L, U, pivot, A); - - af::array AT = is_upper ? U : L; - af::array B0 = af::matmul(AT, X0); - af::array X1; - - if (is_upper) { - //! [ex_solve_upper] - af::array X = af::solve(AT, B0, AF_MAT_UPPER); - //! [ex_solve_upper] - - X1 = X; - } else { - //! [ex_solve_lower] - af::array X = af::solve(AT, B0, AF_MAT_LOWER); - //! [ex_solve_lower] - - X1 = X; - } - - af::array B1 = af::matmul(AT, X1); - - ASSERT_NEAR(0, af::sum(af::abs(real(B0 - B1))) / (n * k), eps); - ASSERT_NEAR(0, af::sum(af::abs(imag(B0 - B1))) / (n * k), eps); -} +#include "solve_common.hpp" +#include #define SOLVE_LU_TESTS(T, eps) \ TEST(SOLVE_LU, T##Reg) \ @@ -199,4 +81,43 @@ SOLVE_TESTS(double, 1E-5) SOLVE_TESTS(cfloat, 0.01) SOLVE_TESTS(cdouble, 1E-5) + +#if !defined(AF_OPENCL) +int nextTargetDeviceId() +{ + static int nextId = 0; + return nextId++; +} + +#define SOLVE_LU_TESTS_THREADING(T, eps) \ + tests.emplace_back(solveLUTester, 1000, 100, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(solveTriangleTester, 1000, 100, true, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(solveTriangleTester, 1000, 100, false, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(solveTester, 1000, 1000, 100, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(solveTester, 800, 1000, 200, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(solveTester, 800, 600, 64, eps, nextTargetDeviceId()%numDevices); \ + +TEST(SOLVE, Threading) +{ + cleanSlate(); // Clean up everything done so far + + vector tests; + + int numDevices = 1; + ASSERT_EQ(AF_SUCCESS, af_get_device_count(&numDevices)); + + SOLVE_LU_TESTS_THREADING(float, 0.01); + SOLVE_LU_TESTS_THREADING(cfloat, 0.01); + if (noDoubleTests()) { + SOLVE_LU_TESTS_THREADING(double, 1E-5); + SOLVE_LU_TESTS_THREADING(cdouble, 1E-5); + } + + for (size_t testId=0; testId -#include -#include -#include -#include -#include -#include -#include -#include #include - -using std::vector; -using std::string; -using std::cout; -using std::endl; -using std::abs; -using af::cfloat; -using af::cdouble; - -///////////////////////////////// CPP //////////////////////////////////// -// - -template -af::array makeSparse(af::array A, int factor) -{ - A = floor(A * 1000); - A = A * ((A % factor) == 0) / 1000; - return A; -} - -template<> -af::array makeSparse(af::array A, int factor) -{ - af::array r = real(A); - r = floor(r * 1000); - r = r * ((r % factor) == 0) / 1000; - - af::array i = r / 2; - - A = af::complex(r, i); - return A; -} - -template<> -af::array makeSparse(af::array A, int factor) -{ - af::array r = real(A); - r = floor(r * 1000); - r = r * ((r % factor) == 0) / 1000; - - af::array i = r / 2; - - A = af::complex(r, i); - return A; -} - -double calc_norm(af::array lhs, af::array rhs) -{ - return af::max(af::abs(lhs - rhs) / (af::abs(lhs) + af::abs(rhs) + 1E-5)); -} - -template -void sparseTester(const int m, const int n, const int k, int factor, double eps) -{ - af::deviceGC(); - - if (noDoubleTests()) return; - -#if 1 - af::array A = cpu_randu(af::dim4(m, n)); - af::array B = cpu_randu(af::dim4(n, k)); -#else - af::array A = af::randu(m, n, (af::dtype)af::dtype_traits::af_type); - af::array B = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); -#endif - - A = makeSparse(A, factor); - - // Result of GEMM - af::array dRes1 = matmul(A, B); - - // Create Sparse Array From Dense - af::array sA = af::sparse(A, AF_STORAGE_CSR); - - // Sparse Matmul - af::array sRes1 = matmul(sA, B); - - // Verify Results - ASSERT_NEAR(0, calc_norm(real(dRes1), real(sRes1)), eps); - ASSERT_NEAR(0, calc_norm(imag(dRes1), imag(sRes1)), eps); -} - -template -void sparseTransposeTester(const int m, const int n, const int k, int factor, double eps) -{ - af::deviceGC(); - - if (noDoubleTests()) return; - -#if 1 - af::array A = cpu_randu(af::dim4(m, n)); - af::array B = cpu_randu(af::dim4(m, k)); -#else - af::array A = af::randu(m, n, (af::dtype)af::dtype_traits::af_type); - af::array B = af::randu(m, k, (af::dtype)af::dtype_traits::af_type); -#endif - - A = makeSparse(A, factor); - - // Result of GEMM - af::array dRes2 = matmul(A, B, AF_MAT_TRANS, AF_MAT_NONE); - af::array dRes3 = matmul(A, B, AF_MAT_CTRANS, AF_MAT_NONE); - - // Create Sparse Array From Dense - af::array sA = af::sparse(A, AF_STORAGE_CSR); - - // Sparse Matmul - af::array sRes2 = matmul(sA, B, AF_MAT_TRANS, AF_MAT_NONE); - af::array sRes3 = matmul(sA, B, AF_MAT_CTRANS, AF_MAT_NONE); - - // Verify Results - ASSERT_NEAR(0, calc_norm(real(dRes2), real(sRes2)), eps); - ASSERT_NEAR(0, calc_norm(imag(dRes2), imag(sRes2)), eps); - - ASSERT_NEAR(0, calc_norm(real(dRes3), real(sRes3)), eps); - ASSERT_NEAR(0, calc_norm(imag(dRes3), imag(sRes3)), eps); -} - -template -void convertCSR(const int M, const int N, const float ratio) -{ - if (noDoubleTests()) return; -#if 1 - af::array a = cpu_randu(af::dim4(M, N)); -#else - af::array a = af::randu(M, N); -#endif - a = a * (a > ratio); - - af::array s = af::sparse(a, AF_STORAGE_CSR); - af::array aa = af::dense(s); - - ASSERT_EQ(0, af::max(af::abs(a - aa))); -} +#include #define SPARSE_TESTS(T, eps) \ TEST(SPARSE, T##Square) \ @@ -197,25 +56,6 @@ SPARSE_TESTS(cdouble, 1E-5) #undef SPARSE_TESTS -// This test essentially verifies that the sparse structures have the correct -// dimensions and indices using a very basic test -template -void createFunction() -{ - af::array in = af::sparse(af::identity(3, 3), stype); - - af::array values = sparseGetValues(in); - af::array rowIdx = sparseGetRowIdx(in); - af::array colIdx = sparseGetColIdx(in); - dim_t nNZ = sparseGetNNZ(in); - - ASSERT_EQ(nNZ, values.elements()); - - ASSERT_EQ(0, af::max(values - af::constant(1, nNZ))); - ASSERT_EQ(0, af::max(rowIdx - af::range(af::dim4(rowIdx.elements()), 0, s32))); - ASSERT_EQ(0, af::max(colIdx - af::range(af::dim4(colIdx.elements()), 0, s32))); -} - #define CREATE_TESTS(STYPE) \ TEST(SPARSE_CREATE, STYPE) \ { \ @@ -237,56 +77,6 @@ TEST(SPARSE_CREATE, AF_STORAGE_CSC) if(out != 0) af_release_array(out); } -template -void sparseCastTester(const int m, const int n, int factor) -{ - if (noDoubleTests()) return; - if (noDoubleTests()) return; - - af::array A = cpu_randu(af::dim4(m, n)); - - A = makeSparse(A, factor); - - af::array sTi = af::sparse(A, AF_STORAGE_CSR); - - // Cast - af::array sTo = sTi.as((af::dtype)af::dtype_traits::af_type); - - // Verify nnZ - dim_t iNNZ = sparseGetNNZ(sTi); - dim_t oNNZ = sparseGetNNZ(sTo); - - ASSERT_EQ(iNNZ, oNNZ); - - // Verify Types - dim_t iSType = sparseGetStorage(sTi); - dim_t oSType = sparseGetStorage(sTo); - - ASSERT_EQ(iSType, oSType); - - // Get the individual arrays and verify equality - af::array iValues = sparseGetValues(sTi); - af::array iRowIdx = sparseGetRowIdx(sTi); - af::array iColIdx = sparseGetColIdx(sTi); - - af::array oValues = sparseGetValues(sTo); - af::array oRowIdx = sparseGetRowIdx(sTo); - af::array oColIdx = sparseGetColIdx(sTo); - - // Verify values - ASSERT_EQ(0, af::max(af::abs(iRowIdx - oRowIdx))); - ASSERT_EQ(0, af::max(af::abs(iColIdx - oColIdx))); - - static const double eps = 1e-6; - if(iValues.iscomplex() && !oValues.iscomplex()) { - ASSERT_NEAR(0, af::max(af::abs(af::abs(iValues) - oValues)), eps); - } else if(!iValues.iscomplex() && oValues.iscomplex()) { - ASSERT_NEAR(0, af::max(af::abs(iValues - af::abs(oValues))), eps); - } else { - ASSERT_NEAR(0, af::max(af::abs(iValues - oValues)), eps); - } -} - #define CAST_TESTS_TYPES(Ti, To, SUFFIX, M, N, F) \ TEST(SPARSE_CAST, Ti##_##To##_##SUFFIX) \ { \ diff --git a/test/sparse_common.hpp b/test/sparse_common.hpp new file mode 100644 index 0000000000..34cfd12ae3 --- /dev/null +++ b/test/sparse_common.hpp @@ -0,0 +1,232 @@ +/******************************************************* + * Copyright (c) 2017, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +using std::vector; +using std::string; +using std::cout; +using std::endl; +using std::abs; +using af::cfloat; +using af::cdouble; + +///////////////////////////////// CPP //////////////////////////////////// +// + +template static +af::array makeSparse(af::array A, int factor) +{ + A = floor(A * 1000); + A = A * ((A % factor) == 0) / 1000; + return A; +} + +template<> +af::array makeSparse(af::array A, int factor) +{ + af::array r = real(A); + r = floor(r * 1000); + r = r * ((r % factor) == 0) / 1000; + + af::array i = r / 2; + + A = af::complex(r, i); + return A; +} + +template<> +af::array makeSparse(af::array A, int factor) +{ + af::array r = real(A); + r = floor(r * 1000); + r = r * ((r % factor) == 0) / 1000; + + af::array i = r / 2; + + A = af::complex(r, i); + return A; +} + +static double calc_norm(af::array lhs, af::array rhs) +{ + return af::max(af::abs(lhs - rhs) / (af::abs(lhs) + af::abs(rhs) + 1E-5)); +} + +template static +void sparseTester(const int m, const int n, const int k, int factor, double eps, + int targetDevice=-1) +{ + if (targetDevice>=0) + af::setDevice(targetDevice); + + af::deviceGC(); + + if (noDoubleTests()) return; + +#if 1 + af::array A = cpu_randu(af::dim4(m, n)); + af::array B = cpu_randu(af::dim4(n, k)); +#else + af::array A = af::randu(m, n, (af::dtype)af::dtype_traits::af_type); + af::array B = af::randu(n, k, (af::dtype)af::dtype_traits::af_type); +#endif + + A = makeSparse(A, factor); + + // Result of GEMM + af::array dRes1 = matmul(A, B); + + // Create Sparse Array From Dense + af::array sA = af::sparse(A, AF_STORAGE_CSR); + + // Sparse Matmul + af::array sRes1 = matmul(sA, B); + + // Verify Results + ASSERT_NEAR(0, calc_norm(real(dRes1), real(sRes1)), eps); + ASSERT_NEAR(0, calc_norm(imag(dRes1), imag(sRes1)), eps); +} + +template static +void sparseTransposeTester(const int m, const int n, const int k, int factor, double eps, + int targetDevice=-1) +{ + if (targetDevice>=0) + af::setDevice(targetDevice); + + af::deviceGC(); + + if (noDoubleTests()) return; + +#if 1 + af::array A = cpu_randu(af::dim4(m, n)); + af::array B = cpu_randu(af::dim4(m, k)); +#else + af::array A = af::randu(m, n, (af::dtype)af::dtype_traits::af_type); + af::array B = af::randu(m, k, (af::dtype)af::dtype_traits::af_type); +#endif + + A = makeSparse(A, factor); + + // Result of GEMM + af::array dRes2 = matmul(A, B, AF_MAT_TRANS, AF_MAT_NONE); + af::array dRes3 = matmul(A, B, AF_MAT_CTRANS, AF_MAT_NONE); + + // Create Sparse Array From Dense + af::array sA = af::sparse(A, AF_STORAGE_CSR); + + // Sparse Matmul + af::array sRes2 = matmul(sA, B, AF_MAT_TRANS, AF_MAT_NONE); + af::array sRes3 = matmul(sA, B, AF_MAT_CTRANS, AF_MAT_NONE); + + // Verify Results + ASSERT_NEAR(0, calc_norm(real(dRes2), real(sRes2)), eps); + ASSERT_NEAR(0, calc_norm(imag(dRes2), imag(sRes2)), eps); + + ASSERT_NEAR(0, calc_norm(real(dRes3), real(sRes3)), eps); + ASSERT_NEAR(0, calc_norm(imag(dRes3), imag(sRes3)), eps); +} + +template static +void convertCSR(const int M, const int N, const float ratio, int targetDevice=-1) +{ + if (targetDevice>=0) + af::setDevice(targetDevice); + + if (noDoubleTests()) return; +#if 1 + af::array a = cpu_randu(af::dim4(M, N)); +#else + af::array a = af::randu(M, N); +#endif + a = a * (a > ratio); + + af::array s = af::sparse(a, AF_STORAGE_CSR); + af::array aa = af::dense(s); + + ASSERT_EQ(0, af::max(af::abs(a - aa))); +} + +// This test essentially verifies that the sparse structures have the correct +// dimensions and indices using a very basic test +template static +void createFunction() +{ + af::array in = af::sparse(af::identity(3, 3), stype); + + af::array values = sparseGetValues(in); + af::array rowIdx = sparseGetRowIdx(in); + af::array colIdx = sparseGetColIdx(in); + dim_t nNZ = sparseGetNNZ(in); + + ASSERT_EQ(nNZ, values.elements()); + + ASSERT_EQ(0, af::max(values - af::constant(1, nNZ))); + ASSERT_EQ(0, af::max(rowIdx - af::range(af::dim4(rowIdx.elements()), 0, s32))); + ASSERT_EQ(0, af::max(colIdx - af::range(af::dim4(colIdx.elements()), 0, s32))); +} + +template static +void sparseCastTester(const int m, const int n, int factor) +{ + if (noDoubleTests()) return; + if (noDoubleTests()) return; + + af::array A = cpu_randu(af::dim4(m, n)); + + A = makeSparse(A, factor); + + af::array sTi = af::sparse(A, AF_STORAGE_CSR); + + // Cast + af::array sTo = sTi.as((af::dtype)af::dtype_traits::af_type); + + // Verify nnZ + dim_t iNNZ = sparseGetNNZ(sTi); + dim_t oNNZ = sparseGetNNZ(sTo); + + ASSERT_EQ(iNNZ, oNNZ); + + // Verify Types + dim_t iSType = sparseGetStorage(sTi); + dim_t oSType = sparseGetStorage(sTo); + + ASSERT_EQ(iSType, oSType); + + // Get the individual arrays and verify equality + af::array iValues = sparseGetValues(sTi); + af::array iRowIdx = sparseGetRowIdx(sTi); + af::array iColIdx = sparseGetColIdx(sTi); + + af::array oValues = sparseGetValues(sTo); + af::array oRowIdx = sparseGetRowIdx(sTo); + af::array oColIdx = sparseGetColIdx(sTo); + + // Verify values + ASSERT_EQ(0, af::max(af::abs(iRowIdx - oRowIdx))); + ASSERT_EQ(0, af::max(af::abs(iColIdx - oColIdx))); + + static const double eps = 1e-6; + if(iValues.iscomplex() && !oValues.iscomplex()) { + ASSERT_NEAR(0, af::max(af::abs(af::abs(iValues) - oValues)), eps); + } else if(!iValues.iscomplex() && oValues.iscomplex()) { + ASSERT_NEAR(0, af::max(af::abs(iValues - af::abs(oValues))), eps); + } else { + ASSERT_NEAR(0, af::max(af::abs(iValues - oValues)), eps); + } +} diff --git a/test/stdev.cpp b/test/stdev.cpp index ff70948752..c8f61e4364 100644 --- a/test/stdev.cpp +++ b/test/stdev.cpp @@ -93,17 +93,14 @@ void stdevDimTest(string pFileName, dim_t dim=-1) vector currGoldBar(tests[0].begin(), tests[0].end()); size_t nElems = currGoldBar.size(); - outType *outData = new outType[nElems]; + std::vector outData(nElems); - b.host((void*)outData); + b.host((void*)outData.data()); for (size_t elIter=0; elIter currGoldBar(tests[0].begin(), tests[0].end()); size_t nElems = currGoldBar.size(); - outType *outData = new outType[nElems]; + std::vector outData(nElems); - c.host((void*)outData); + c.host((void*)outData.data()); for (size_t elIter=0; elIter outX (gold[0].size()); + std::vector outY (gold[1].size()); + std::vector outScore (gold[2].size()); + std::vector outOrientation(gold[3].size()); + std::vector outSize (gold[4].size()); + out.getX().host(outX.data()); + out.getY().host(outY.data()); + out.getScore().host(outScore.data()); + out.getOrientation().host(outOrientation.data()); + out.getSize().host(outSize.data()); vector out_feat; - array_to_feat(out_feat, outX, outY, outScore, outOrientation, outSize, out.getNumFeatures()); + array_to_feat(out_feat, outX.data(), outY.data(), outScore.data(), + outOrientation.data(), outSize.data(), out.getNumFeatures()); vector gold_feat; array_to_feat(gold_feat, &gold[0].front(), &gold[1].front(), &gold[2].front(), &gold[3].front(), &gold[4].front(), gold[0].size()); @@ -108,12 +109,6 @@ void susanTest(string pTestFile, float t, float g) ASSERT_EQ(out_feat[elIter].f[3], gold_feat[elIter].f[3]) << "at: " << elIter << std::endl; ASSERT_EQ(out_feat[elIter].f[4], gold_feat[elIter].f[4]) << "at: " << elIter << std::endl; } - - delete [] outX; - delete [] outY; - delete [] outScore; - delete [] outOrientation; - delete [] outSize; } } diff --git a/test/testHelpers.hpp b/test/testHelpers.hpp index 999a71b97d..58b820eabd 100644 --- a/test/testHelpers.hpp +++ b/test/testHelpers.hpp @@ -6,6 +6,7 @@ * The complete license agreement can be obtained at: * http://arrayfire.com/licenses/BSD-3-Clause ********************************************************/ +#pragma once #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" @@ -453,4 +454,26 @@ af::array cpu_randu(const af::dim4 dims) return af::array(dims, (T *)&out[0]); } +void cleanSlate() +{ + const size_t step_bytes = 1024; + + size_t alloc_bytes, alloc_buffers; + size_t lock_bytes, lock_buffers; + + af::deviceGC(); + + af::deviceMemInfo(&alloc_bytes, &alloc_buffers, + &lock_bytes, &lock_buffers); + + ASSERT_EQ(alloc_buffers, 0u); + ASSERT_EQ(lock_buffers, 0u); + ASSERT_EQ(alloc_bytes, 0u); + ASSERT_EQ(lock_bytes, 0u); + + af::setMemStepSize(step_bytes); + + ASSERT_EQ(af::getMemStepSize(), step_bytes); +} + #pragma GCC diagnostic pop diff --git a/test/threading.cpp b/test/threading.cpp new file mode 100644 index 0000000000..01a130fb99 --- /dev/null +++ b/test/threading.cpp @@ -0,0 +1,655 @@ +/******************************************************* + * Copyright (c) 2017, ArrayFire + * All rights reserved. + * + * This file is distributed under 3-clause BSD license. + * The complete license agreement can be obtained at: + * http://arrayfire.com/licenses/BSD-3-Clause + ********************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace af; + +using std::vector; +using std::string; + +static const int THREAD_COUNT = 32; + +#if defined(AF_CPU) +static const unsigned ITERATION_COUNT = 10; +#else +static const unsigned ITERATION_COUNT = 1000; +#endif + +int nextTargetDeviceId() +{ + static int nextId = 0; + return nextId++; +} + +void morphTest(const array input, const array mask, const bool isDilation, + const array gold, int targetDevice) +{ + af::setDevice(targetDevice); + + vector goldData(gold.elements()); + vector outData(gold.elements()); + + gold.host((void*)goldData.data()); + + af::array out; + + for (unsigned i=0; i isDilationFlags; + vector isColorFlags; + vector files; + + files.push_back( string(TEST_DIR "/morph/gray.test") ); + isDilationFlags.push_back(true); + isColorFlags.push_back(false); + + files.push_back( string(TEST_DIR "/morph/color.test") ); + isDilationFlags.push_back(false); + isColorFlags.push_back(true); + + vector tests; + unsigned totalTestCount = 0; + + for(size_t pos = 0; pos inDims; + vector inFiles; + vector outSizes; + vector outFiles; + + readImageTests(files[pos], inDims, inFiles, outSizes, outFiles); + + const unsigned testCount = inDims.size(); + + const dim4 maskdims(3,3,1,1); + + for (size_t testId=0; testId out(res.elements()); + res.host((void*)out.data()); + + for (unsigned i=0; i tests; + + for (int t=0; t lock(cvMutex); + //Check for current thread launch counter value + //if reached zero, notify others to continue + //otherwise block current thread + if (--counter==0) + cv.notify_all(); + else + cv.wait(lock, [] {return counter==0;}); + lock.unlock(); + + af::array a = randu(5, 5); + + // Wait for for other threads to hit randu call + // while this thread's variable a is still in scope. + std::this_thread::sleep_for(std::chrono::seconds(2)); +} + +TEST(Threading, MemoryManagementScope) +{ + cleanSlate(); // Clean up everything done so far + + vector tests; + + for (int t=0; t tests; + + for (int t=0; t +void fftTest(int targetDevice, string pTestFile, dim_t pad0=0, dim_t pad1=0, dim_t pad2=0) +{ + if (noDoubleTests()) return; + if (noDoubleTests()) return; + + vector numDims; + vector > in; + vector > tests; + + readTestsFromFile(pTestFile, numDims, in, tests); + + af::dim4 dims = numDims[0]; + af_array outArray = 0; + af_array inArray = 0; + + ASSERT_EQ(AF_SUCCESS, af_set_device(targetDevice)); + + ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &(in[0].front()), + dims.ndims(), dims.get(), (af_dtype)af::dtype_traits::af_type)); + + if (isInverse){ + switch (dims.ndims()) { + case 1 : ASSERT_EQ(AF_SUCCESS, af_ifft (&outArray, inArray, 1.0, pad0)); break; + case 2 : ASSERT_EQ(AF_SUCCESS, af_ifft2(&outArray, inArray, 1.0, pad0, pad1)); break; + case 3 : ASSERT_EQ(AF_SUCCESS, af_ifft3(&outArray, inArray, 1.0, pad0, pad1, pad2)); break; + default: throw std::runtime_error("This error shouldn't happen, pls check"); + } + } else { + switch(dims.ndims()) { + case 1 : ASSERT_EQ(AF_SUCCESS, af_fft (&outArray, inArray, 1.0, pad0)); break; + case 2 : ASSERT_EQ(AF_SUCCESS, af_fft2(&outArray, inArray, 1.0, pad0, pad1)); break; + case 3 : ASSERT_EQ(AF_SUCCESS, af_fft3(&outArray, inArray, 1.0, pad0, pad1, pad2)); break; + default: throw std::runtime_error("This error shouldn't happen, pls check"); + } + } + + size_t out_size = tests[0].size(); + outType *outData= new outType[out_size]; + ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray)); + + vector goldBar(tests[0].begin(), tests[0].end()); + + size_t test_size = 0; + switch(dims.ndims()) { + case 1 : test_size = dims[0]/2+1; break; + case 2 : test_size = dims[1] * (dims[0]/2+1); break; + case 3 : test_size = dims[2] * dims[1] * (dims[0]/2+1); break; + default : test_size = dims[0]/2+1; break; + } + outType output_scale = (outType)(isInverse ? test_size : 1); + for (size_t elIter=0; elIter, targetDevice, file, 0, 0, 0); \ + } + +#define INSTANTIATE_TEST_TP(func, name, is_inverse, in_t, out_t, file, p0, p1) \ + { \ + int targetDevice = nextTargetDeviceId() % numDevices; \ + tests.emplace_back(fftTest, targetDevice, file, p0, p1, 0);\ + } + +TEST(Threading, FFT_R2C) +{ + cleanSlate(); // Clean up everything done so far + + vector tests; + + int numDevices = 1; + ASSERT_EQ(AF_SUCCESS, af_get_device_count(&numDevices)); + + // Real to complex transforms + INSTANTIATE_TEST(fft , R2C_Float, false, float, cfloat, string(TEST_DIR"/signal/fft_r2c.test") ); + INSTANTIATE_TEST(fft2, R2C_Float, false, float, cfloat, string(TEST_DIR"/signal/fft2_r2c.test")); + INSTANTIATE_TEST(fft3, R2C_Float, false, float, cfloat, string(TEST_DIR"/signal/fft3_r2c.test")); + + // Factors 7, 11, 13 + INSTANTIATE_TEST(fft , R2C_Float_7_11_13 , false, float , cfloat , string(TEST_DIR"/signal/fft_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, R2C_Float_7_11_13 , false, float , cfloat , string(TEST_DIR"/signal/fft2_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, R2C_Float_7_11_13 , false, float , cfloat , string(TEST_DIR"/signal/fft3_r2c_7_11_13.test") ); + + // transforms on padded and truncated arrays + INSTANTIATE_TEST_TP(fft2, R2C_Float_Trunc, false, float, cfloat, string(TEST_DIR"/signal/fft2_r2c_trunc.test"), 16, 16); + + + if (noDoubleTests()) { + // Real to complex transforms + INSTANTIATE_TEST(fft , R2C_Double, false, double, cdouble, string(TEST_DIR"/signal/fft_r2c.test") ); + INSTANTIATE_TEST(fft2, R2C_Double, false, double, cdouble, string(TEST_DIR"/signal/fft2_r2c.test")); + INSTANTIATE_TEST(fft3, R2C_Double, false, double, cdouble, string(TEST_DIR"/signal/fft3_r2c.test")); + + // Factors 7, 11, 13 + INSTANTIATE_TEST(fft , R2C_Double_7_11_13, false, double , cdouble, string(TEST_DIR"/signal/fft_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, R2C_Double_7_11_13, false, double , cdouble, string(TEST_DIR"/signal/fft2_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, R2C_Double_7_11_13, false, double , cdouble, string(TEST_DIR"/signal/fft3_r2c_7_11_13.test") ); + + // transforms on padded and truncated arrays + INSTANTIATE_TEST_TP(fft2, R2C_Double_Trunc, false, double, cdouble, string(TEST_DIR"/signal/fft2_r2c_trunc.test"), 16, 16); + } + + + for (size_t testId=0; testId tests; + + int numDevices = 1; + ASSERT_EQ(AF_SUCCESS, af_get_device_count(&numDevices)); + + // complex to complex transforms + INSTANTIATE_TEST(fft , C2C_Float, false, cfloat, cfloat, string(TEST_DIR"/signal/fft_c2c.test") ); + INSTANTIATE_TEST(fft2, C2C_Float, false, cfloat, cfloat, string(TEST_DIR"/signal/fft2_c2c.test")); + INSTANTIATE_TEST(fft3, C2C_Float, false, cfloat, cfloat, string(TEST_DIR"/signal/fft3_c2c.test")); + + INSTANTIATE_TEST(fft , C2C_Float_7_11_13 , false, cfloat , cfloat , string(TEST_DIR"/signal/fft_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, C2C_Float_7_11_13 , false, cfloat , cfloat , string(TEST_DIR"/signal/fft2_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, C2C_Float_7_11_13 , false, cfloat , cfloat , string(TEST_DIR"/signal/fft3_c2c_7_11_13.test") ); + + // transforms on padded and truncated arrays + INSTANTIATE_TEST_TP(fft2, C2C_Float_Pad, false, cfloat, cfloat, string(TEST_DIR"/signal/fft2_c2c_pad.test"), 16, 16); + + // inverse transforms + // complex to complex transforms + INSTANTIATE_TEST(ifft , C2C_Float, true, cfloat, cfloat, string(TEST_DIR"/signal/ifft_c2c.test") ); + INSTANTIATE_TEST(ifft2, C2C_Float, true, cfloat, cfloat, string(TEST_DIR"/signal/ifft2_c2c.test")); + INSTANTIATE_TEST(ifft3, C2C_Float, true, cfloat, cfloat, string(TEST_DIR"/signal/ifft3_c2c.test")); + + + if (noDoubleTests()) { + INSTANTIATE_TEST(fft , C2C_Double, false, cdouble, cdouble, string(TEST_DIR"/signal/fft_c2c.test") ); + INSTANTIATE_TEST(fft2, C2C_Double, false, cdouble, cdouble, string(TEST_DIR"/signal/fft2_c2c.test")); + INSTANTIATE_TEST(fft3, C2C_Double, false, cdouble, cdouble, string(TEST_DIR"/signal/fft3_c2c.test")); + + INSTANTIATE_TEST(fft , C2C_Double_7_11_13, false, cdouble , cdouble, string(TEST_DIR"/signal/fft_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, C2C_Double_7_11_13, false, cdouble , cdouble, string(TEST_DIR"/signal/fft2_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, C2C_Double_7_11_13, false, cdouble , cdouble, string(TEST_DIR"/signal/fft3_c2c_7_11_13.test") ); + + INSTANTIATE_TEST_TP(fft2, C2C_Double_Pad, false, cdouble, cdouble, string(TEST_DIR"/signal/fft2_c2c_pad.test"), 16, 16); + + INSTANTIATE_TEST(ifft , C2C_Double, true, cdouble, cdouble, string(TEST_DIR"/signal/ifft_c2c.test") ); + INSTANTIATE_TEST(ifft2, C2C_Double, true, cdouble, cdouble, string(TEST_DIR"/signal/ifft2_c2c.test")); + INSTANTIATE_TEST(ifft3, C2C_Double, true, cdouble, cdouble, string(TEST_DIR"/signal/ifft3_c2c.test")); + } + + for (size_t testId=0; testId tests; + + int numDevices = 1; + ASSERT_EQ(AF_SUCCESS, af_get_device_count(&numDevices)); + + // Real to complex transforms + INSTANTIATE_TEST(fft , R2C_Float, false, float, cfloat, string(TEST_DIR"/signal/fft_r2c.test") ); + INSTANTIATE_TEST(fft2, R2C_Float, false, float, cfloat, string(TEST_DIR"/signal/fft2_r2c.test")); + INSTANTIATE_TEST(fft3, R2C_Float, false, float, cfloat, string(TEST_DIR"/signal/fft3_r2c.test")); + + // Factors 7, 11, 13 + INSTANTIATE_TEST(fft , R2C_Float_7_11_13 , false, float , cfloat , string(TEST_DIR"/signal/fft_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, R2C_Float_7_11_13 , false, float , cfloat , string(TEST_DIR"/signal/fft2_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, R2C_Float_7_11_13 , false, float , cfloat , string(TEST_DIR"/signal/fft3_r2c_7_11_13.test") ); + + // transforms on padded and truncated arrays + INSTANTIATE_TEST_TP(fft2, R2C_Float_Trunc, false, float, cfloat, string(TEST_DIR"/signal/fft2_r2c_trunc.test"), 16, 16); + + // complex to complex transforms + INSTANTIATE_TEST(fft , C2C_Float, false, cfloat, cfloat, string(TEST_DIR"/signal/fft_c2c.test") ); + INSTANTIATE_TEST(fft2, C2C_Float, false, cfloat, cfloat, string(TEST_DIR"/signal/fft2_c2c.test")); + INSTANTIATE_TEST(fft3, C2C_Float, false, cfloat, cfloat, string(TEST_DIR"/signal/fft3_c2c.test")); + + INSTANTIATE_TEST(fft , C2C_Float_7_11_13 , false, cfloat , cfloat , string(TEST_DIR"/signal/fft_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, C2C_Float_7_11_13 , false, cfloat , cfloat , string(TEST_DIR"/signal/fft2_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, C2C_Float_7_11_13 , false, cfloat , cfloat , string(TEST_DIR"/signal/fft3_c2c_7_11_13.test") ); + + // transforms on padded and truncated arrays + INSTANTIATE_TEST_TP(fft2, C2C_Float_Pad, false, cfloat, cfloat, string(TEST_DIR"/signal/fft2_c2c_pad.test"), 16, 16); + + // inverse transforms + // complex to complex transforms + INSTANTIATE_TEST(ifft , C2C_Float, true, cfloat, cfloat, string(TEST_DIR"/signal/ifft_c2c.test") ); + INSTANTIATE_TEST(ifft2, C2C_Float, true, cfloat, cfloat, string(TEST_DIR"/signal/ifft2_c2c.test")); + INSTANTIATE_TEST(ifft3, C2C_Float, true, cfloat, cfloat, string(TEST_DIR"/signal/ifft3_c2c.test")); + + if (noDoubleTests()) { + INSTANTIATE_TEST(fft , R2C_Double, false, double, cdouble, string(TEST_DIR"/signal/fft_r2c.test") ); + INSTANTIATE_TEST(fft2, R2C_Double, false, double, cdouble, string(TEST_DIR"/signal/fft2_r2c.test")); + INSTANTIATE_TEST(fft3, R2C_Double, false, double, cdouble, string(TEST_DIR"/signal/fft3_r2c.test")); + INSTANTIATE_TEST(fft , R2C_Double_7_11_13, false, double , cdouble, string(TEST_DIR"/signal/fft_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, R2C_Double_7_11_13, false, double , cdouble, string(TEST_DIR"/signal/fft2_r2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, R2C_Double_7_11_13, false, double , cdouble, string(TEST_DIR"/signal/fft3_r2c_7_11_13.test") ); + INSTANTIATE_TEST_TP(fft2, R2C_Double_Trunc, false, double, cdouble, string(TEST_DIR"/signal/fft2_r2c_trunc.test"), 16, 16); + INSTANTIATE_TEST(fft , C2C_Double, false, cdouble, cdouble, string(TEST_DIR"/signal/fft_c2c.test") ); + INSTANTIATE_TEST(fft2, C2C_Double, false, cdouble, cdouble, string(TEST_DIR"/signal/fft2_c2c.test")); + INSTANTIATE_TEST(fft3, C2C_Double, false, cdouble, cdouble, string(TEST_DIR"/signal/fft3_c2c.test")); + INSTANTIATE_TEST(fft , C2C_Double_7_11_13, false, cdouble , cdouble, string(TEST_DIR"/signal/fft_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft2, C2C_Double_7_11_13, false, cdouble , cdouble, string(TEST_DIR"/signal/fft2_c2c_7_11_13.test") ); + INSTANTIATE_TEST(fft3, C2C_Double_7_11_13, false, cdouble , cdouble, string(TEST_DIR"/signal/fft3_c2c_7_11_13.test") ); + INSTANTIATE_TEST_TP(fft2, C2C_Double_Pad, false, cdouble, cdouble, string(TEST_DIR"/signal/fft2_c2c_pad.test"), 16, 16); + INSTANTIATE_TEST(ifft , C2C_Double, true, cdouble, cdouble, string(TEST_DIR"/signal/ifft_c2c.test") ); + INSTANTIATE_TEST(ifft2, C2C_Double, true, cdouble, cdouble, string(TEST_DIR"/signal/ifft2_c2c.test")); + INSTANTIATE_TEST(ifft3, C2C_Double, true, cdouble, cdouble, string(TEST_DIR"/signal/ifft3_c2c.test")); + } + + for (size_t testId=0; testId +void cppMatMulCheck(int targetDevice, string TestFile) +{ + if (noDoubleTests()) return; + + using std::vector; + vector numDims; + + vector > hData; + vector > tests; + readTests(TestFile, numDims, hData, tests); + + af::setDevice(targetDevice); + + af::array a(numDims[0], &hData[0].front()); + af::array b(numDims[1], &hData[1].front()); + + af::dim4 atdims = numDims[0]; + { + dim_t f = atdims[0]; + atdims[0] = atdims[1]; + atdims[1] = f; + } + af::dim4 btdims = numDims[1]; + { + dim_t f = btdims[0]; + btdims[0] = btdims[1]; + btdims[1] = f; + } + + af::array aT = moddims(a, atdims.ndims(), atdims.get()); + af::array bT = moddims(b, btdims.ndims(), btdims.get()); + + vector out(tests.size()); + if(isBVector) { + out[0] = af::matmul(aT, b, AF_MAT_NONE, AF_MAT_NONE); + out[1] = af::matmul(bT, a, AF_MAT_NONE, AF_MAT_NONE); + out[2] = af::matmul(b, a, AF_MAT_TRANS, AF_MAT_NONE); + out[3] = af::matmul(bT, aT, AF_MAT_NONE, AF_MAT_TRANS); + out[4] = af::matmul(b, aT, AF_MAT_TRANS, AF_MAT_TRANS); + } + else { + out[0] = af::matmul(a, b, AF_MAT_NONE, AF_MAT_NONE); + out[1] = af::matmul(a, bT, AF_MAT_NONE, AF_MAT_TRANS); + out[2] = af::matmul(a, bT, AF_MAT_TRANS, AF_MAT_NONE); + out[3] = af::matmul(aT, bT, AF_MAT_TRANS, AF_MAT_TRANS); + } + + for(size_t i = 0; i < tests.size(); i++) { + dim_t elems = out[i].elements(); + vector h_out(elems); + out[i].host((void*)&h_out.front()); + + if (false == equal(h_out.begin(), h_out.end(), tests[i].begin())) { + + std::cout << "Failed test " << i << "\nCalculated: " << std::endl; + std::copy(h_out.begin(), h_out.end(), std::ostream_iterator(std::cout, ", ")); + std::cout << "Expected: " << std::endl; + std::copy(tests[i].begin(), tests[i].end(), std::ostream_iterator(std::cout, ", ")); + FAIL(); + } + } +} + +#define TEST_FOR_TYPE(TypeName) \ + tests.emplace_back(cppMatMulCheck, \ + nextTargetDeviceId()%numDevices, TEST_DIR "/blas/Basic.test"); \ + tests.emplace_back(cppMatMulCheck, \ + nextTargetDeviceId()%numDevices, TEST_DIR "/blas/NonSquare.test"); \ + tests.emplace_back(cppMatMulCheck, \ + nextTargetDeviceId()%numDevices, TEST_DIR "/blas/SquareVector.test"); \ + tests.emplace_back(cppMatMulCheck, \ + nextTargetDeviceId()%numDevices, TEST_DIR "/blas/RectangleVector.test"); + +TEST(Threading, BLAS) +{ + cleanSlate(); // Clean up everything done so far + + vector tests; + + int numDevices = 1; + ASSERT_EQ(AF_SUCCESS, af_get_device_count(&numDevices)); + + TEST_FOR_TYPE( float); + TEST_FOR_TYPE( af::cfloat); + + if (noDoubleTests()) { + TEST_FOR_TYPE( double); + TEST_FOR_TYPE(af::cdouble); + } + + for (size_t testId=0; testId, 1000, 1000, 100, 5, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(sparseTester, 500, 1000, 250, 1, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(sparseTester, 625, 1331, 1, 2, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(sparseTransposeTester, 625, 1331, 1, 2, eps, nextTargetDeviceId()%numDevices); \ + tests.emplace_back(sparseTransposeTester, 453, 751, 397, 1, eps, nextTargetDeviceId()%numDevices);\ + tests.emplace_back(convertCSR, 2345, 5678, 0.5, nextTargetDeviceId()%numDevices); + +TEST(Threading, Sparse) +{ + cleanSlate(); // Clean up everything done so far + + vector tests; + + int numDevices = 1; + ASSERT_EQ(AF_SUCCESS, af_get_device_count(&numDevices)); + + SPARSE_TESTS( float, 1E-3); + SPARSE_TESTS( cfloat, 1E-3); + if (noDoubleTests()) { + SPARSE_TESTS( double, 1E-5); + SPARSE_TESTS(cdouble, 1E-5); + } + + for (size_t testId=0; testId < tests.size(); ++testId) + if (tests[testId].joinable()) + tests[testId].join(); +} + +TEST(Threading, DISABLED_MemoryManagerStressTest) +{ + vector threads; + for (int i = 0; i < THREAD_COUNT; i++) { + threads.emplace_back([] { + vector arrg; + int size = 100; + int ex_count = 0; + + // Continue until the memory runs out multiple times + while (true) { + try { + // constantly change size of the array allocated + size+=10; + arrg.push_back(af::randu(size)); + + // delete some values intermittently + if (!(size%200)) { + arrg.erase(std::begin(arrg), std::begin(arrg)+5); + } + } catch( const af::exception &ex ) { + if (ex_count++ > 3) { + break; + } + } + } + }); + } + for (auto& t : threads) { + t.join(); + } +} diff --git a/test/ycbcr_rgb.cpp b/test/ycbcr_rgb.cpp index f2c7aacf79..4f4d7cec73 100644 --- a/test/ycbcr_rgb.cpp +++ b/test/ycbcr_rgb.cpp @@ -45,17 +45,14 @@ TEST(ycbcr2rgb, CPP) af::array input(dims, &(in[0].front())); af::array output = af::ycbcr2rgb(input); - float *outData = new float[dims.elements()]; - output.host((void*)outData); + std::vector outData(dims.elements()); + output.host(outData.data()); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); for (size_t elIter=0; elIter outData(dims.elements()); + output.host(outData.data()); vector currGoldBar = tests[0]; size_t nElems = currGoldBar.size(); for (size_t elIter=0; elIter