chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:47:05 +08:00
commit 4f3b7da785
7394 changed files with 2005594 additions and 0 deletions
@@ -0,0 +1,48 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef LIBND4J_AFFINITYMANAGER_H
#define LIBND4J_AFFINITYMANAGER_H
#include <system/common.h>
#include <atomic>
#include <mutex>
namespace sd {
class SD_LIB_EXPORT AffinityManager {
private:
static std::atomic<int> _lastDevice;
static int _numberOfDevices;
static std::mutex _currentMutex;
static std::mutex _numberMutex;
public:
static int currentNativeDeviceId();
static int currentDeviceId();
static int numberOfDevices();
static void setCurrentDevice(int deviceId);
static void setCurrentNativeDevice(int deviceId);
};
} // namespace sd
#endif // DEV_TESTS_AFFINITYMANAGER_H
+54
View File
@@ -0,0 +1,54 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SAMEDIFF_BLOCKINGQUEUE_H
#define SAMEDIFF_BLOCKINGQUEUE_H
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
namespace samediff {
template <typename T>
class BlockingQueue {
private:
std::queue<T> _queue;
std::mutex _lock;
std::atomic<int> _size;
std::atomic<bool> _available;
std::condition_variable _condition;
public:
BlockingQueue(int queueSize);
~BlockingQueue() = default;
T poll();
void put(const T &t);
bool available();
void markAvailable();
void markUnavailable();
};
} // namespace samediff
#endif // DEV_TESTS_BLOCKINGQUEUE_H
@@ -0,0 +1,99 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SAMEDIFF_CALLABLEINTERFACE_H
#define SAMEDIFF_CALLABLEINTERFACE_H
#include <system/common.h>
#include <array>
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <mutex>
namespace samediff {
/**
* This class is suited for passing functions to execution threads without queues
*/
class CallableInterface {
private:
// parallel_for functions
FUNC_1D _function_1d;
FUNC_2D _function_2d;
FUNC_3D _function_3d;
// parallel function
FUNC_DO _function_do;
// reduction functions
FUNC_RL _function_rl;
FUNC_RD _function_rd;
std::array<int64_t, 9> _arguments;
volatile int _branch = 0;
volatile uint32_t _thread_id = 0;
volatile uint32_t _num_threads = 0;
std::atomic<bool> _finished;
std::atomic<bool> _filled;
std::atomic<bool> _available;
std::condition_variable _starter;
std::condition_variable _finisher;
int64_t *_lptr = nullptr;
double *_dptr = nullptr;
std::mutex _ms;
std::mutex _mf;
public:
CallableInterface();
~CallableInterface() = default;
void waitForTask();
void waitForCompletion();
void fill(int thread_id, int num_threads, int64_t *lpt, FUNC_RL func, int64_t start_x, int64_t stop_x, int64_t inc_x);
void fill(int thread_id, int num_threads, double *dpt, FUNC_RD func, int64_t start_x, int64_t stop_x, int64_t inc_x);
void fill(int thread_id, int num_threads, FUNC_DO func);
void fill(int thread_id, int num_threads, FUNC_1D func, int64_t start_x, int64_t stop_x, int64_t inc_x);
void fill(int thread_id, int num_threads, FUNC_2D func, int64_t start_x, int64_t stop_x, int64_t inc_x,
int64_t start_y, int64_t stop_y, int64_t inc_y);
void fill(int thread_id, int num_threads, FUNC_3D func, int64_t start_x, int64_t stop_x, int64_t inc_x,
int64_t start_y, int64_t stop_y, int64_t inc_y, int64_t start_z, int64_t stop_z, int64_t inc_z);
bool available();
void markAvailable();
void markUnavailable();
void finish();
void execute();
};
} // namespace samediff
#endif // DEV_TESTS_CALLABLEINTERFACE_H
@@ -0,0 +1,95 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef DEV_TESTS_CALLABLEWITHARGUMENTS_H
#define DEV_TESTS_CALLABLEWITHARGUMENTS_H
#include <system/op_boilerplate.h>
#include <atomic>
#include <condition_variable>
#include <functional>
#include <vector>
namespace samediff {
class CallableWithArguments {
FUNC_DO _function_do;
FUNC_1D _function_1d;
FUNC_2D _function_2d;
FUNC_3D _function_3d;
std::vector<int64_t> _arguments;
std::atomic<bool> _finished;
std::condition_variable _condition;
std::mutex _lock;
int _dimensions = 0;
uint64_t _threadId;
uint64_t _numThreads;
public:
CallableWithArguments(FUNC_DO func, uint64_t thread_id, uint64_t numThreads);
CallableWithArguments(FUNC_1D func, uint64_t thread_id, int64_t start_x, int64_t stop_x, int64_t increment_x);
CallableWithArguments(FUNC_2D func, uint64_t thread_id, int64_t start_x, int64_t stop_x, int64_t increment_x,
int64_t start_y, int64_t stop_y, int64_t increment_y);
CallableWithArguments(FUNC_3D func, uint64_t thread_id, int64_t start_x, int64_t stop_x, int64_t increment_x,
int64_t start_y, int64_t stop_y, int64_t increment_y, int64_t start_z, int64_t stop_z,
int64_t increment_z);
/**
* This method returns number of dimensions
* @return
*/
int dimensions();
/**
* This method checks if this callable is finished
* @return
*/
bool finished();
/**
* this method marks this Callable as finished
*/
void finish();
/**
* This method blocks until callable is finished
*/
void waitUntilFinished();
std::vector<int64_t> &arguments();
FUNC_DO function_do();
FUNC_1D function_1d();
FUNC_2D function_2d();
FUNC_3D function_3d();
uint64_t threadId();
uint64_t numThreads();
};
} // namespace samediff
#endif // DEV_TESTS_CALLABLEWITHARGUMENTS_H
@@ -0,0 +1,77 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef LIBND4J_CONTEXTBUFFERS_H
#define LIBND4J_CONTEXTBUFFERS_H
#include <execution/ErrorReference.h>
#include <system/common.h>
namespace sd {
class SD_LIB_EXPORT ContextBuffers {
private:
void *_reductionPointer = nullptr;
void *_scalarPointer = nullptr;
void *_allocationPointer = nullptr;
void *_execStream = nullptr;
void *_specialStream = nullptr;
ErrorReference _errorReference;
bool _allocated = false;
bool _initialized = false;
int _deviceId = -1;
void initialize();
public:
ContextBuffers();
ContextBuffers(const ContextBuffers &other);
ContextBuffers(void *rPointer, void *sPointer, void *aPointer, bool isOwner = false);
~ContextBuffers();
ContextBuffers &operator=(const ContextBuffers &other);
ContextBuffers &operator=(ContextBuffers &&other);
void release();
void *reductionBuffer();
void *scalarBuffer();
void *allocationBuffer();
void *execStream();
void *specialStream();
void setReductionBuffer(void *pointer);
void setScalarBuffer(void *pointer);
void setAllocationBuffer(void *pointer);
ErrorReference *errorReference();
void triggerOwnership(bool isOwner);
int deviceId();
bool isInitialized();
};
} // namespace sd
#endif // DEV_TESTS_CONTEXTBUFFERS_H
+33
View File
@@ -0,0 +1,33 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SD_ENGINE_H
#define SD_ENGINE_H
namespace samediff {
enum Engine {
ENGINE_CPU = 0,
ENGINE_CUDA = 1
};
}
#endif // SD_ENGINE_H
@@ -0,0 +1,48 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef DEV_TESTS_ERRORREFERENCE_H
#define DEV_TESTS_ERRORREFERENCE_H
#include <system/common.h>
#include <string>
namespace sd {
class SD_LIB_EXPORT ErrorReference {
private:
int _errorCode = 0;
std::string *_errorMessage;
public:
ErrorReference() = default;
~ErrorReference() = default;
int errorCode();
const char *errorMessage();
void setErrorCode(int errorCode);
void setErrorMessage(std::string message);
void setErrorMessage(const char *message);
};
} // namespace sd
#endif // DEV_TESTS_ERRORREFERENCE_H
+34
View File
@@ -0,0 +1,34 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SD_EXECUTIONMODE_H
#define SD_EXECUTIONMODE_H
namespace samediff {
enum ExecutionMode {
MODE_UNDEFINED = 0,
MODE_TRAINING = 1,
MODE_INFERENCE = 2,
};
}
#endif // SD_EXECUTIONMODE_H
+35
View File
@@ -0,0 +1,35 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SD_EXECUTOR_H
#define SD_EXECUTOR_H
namespace sd {
class Executor {
public:
static void execute() {
//
}
};
} // namespace sd
#endif // SD_EXECUTOR_H
+140
View File
@@ -0,0 +1,140 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// Created by raver119 on 30.11.17.
//
#ifndef LIBND4J_CUDACONTEXT_H
#define LIBND4J_CUDACONTEXT_H
#ifdef SD_CUDA
#include <cuda.h>
#include <cuda_device_runtime_api.h>
#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include "config.h"
#endif
// used for MKLDNN etc
#if !defined(__STANDALONE_BUILD__)
#include "config.h"
#endif
#include <execution/ContextBuffers.h>
#include <execution/ErrorReference.h>
#include <memory/Workspace.h>
#include <system/common.h>
#include <system/op_boilerplate.h>
#include <memory>
#include <mutex>
#include <vector>
namespace sd {
class SD_LIB_EXPORT LaunchContext {
private:
// Previous implementation used heap-allocated pointer which caused crashes during JVM shutdown:
// - The vector* was allocated with new and "intentionally leaked"
// - But C++ static destruction still tried to destruct it, causing SIGSEGV
// - This SIGSEGV triggered signal handler which called abort(), resulting in SIGABRT
// Function-local static has well-defined destruction order and avoids this crash
static std::vector<LaunchContext*>& contexts();
static std::mutex _mutex;
static SD_MAP_IMPL<int, std::mutex*> _deviceMutexes;
// used for MKLDNN
void* _engine = nullptr;
#ifdef SD_CUDA
#ifndef __JAVACPP_HACK__
void* _cublasHandle = nullptr;
void* _cusolverHandle = nullptr;
#endif // JCPP
bool _isAllocated = false;
#endif // CUDA
memory::Workspace* _workspace = nullptr;
int _deviceID = 0;
public:
#ifdef SD_CUDA
#ifndef __JAVACPP_HACK__
LaunchContext(cudaStream_t* cudaStream, cudaStream_t& specialCudaStream, void* reductionPointer = nullptr,
void* scalarPointer = nullptr, int* allocationPointer = nullptr);
void* getReductionPointer() const;
void* getScalarPointer() const;
LongType* getAllocationPointer() const;
void* getCublasHandle() const;
void* getCusolverHandle() const;
void* getCuDnnHandle() const;
cudaStream_t* getCudaStream() const;
cudaStream_t* getCudaSpecialStream() const;
void setReductionPointer(void* reductionPointer);
void setScalarPointer(void* scalarPointer);
void setAllocationPointer(int* allocationPointer);
void setCudaStream(cudaStream_t* cudaStream);
void setCudaSpecialStream(cudaStream_t* cudaStream);
void setCublasHandle(void* handle);
#endif // JCPP
#endif // CUDA
LaunchContext(Pointer cudaStream, Pointer reductionPointer = nullptr, Pointer scalarPointer = nullptr,
Pointer allocationPointer = nullptr);
LaunchContext();
~LaunchContext();
memory::Workspace* getWorkspace() const {
return _workspace;
}
void setWorkspace(memory::Workspace* theWorkspace) { _workspace = theWorkspace; }
void* engine();
int getDeviceID() const { return _deviceID; }
void setDeviceID(int deviceID) { _deviceID = deviceID; }
ErrorReference* errorReference();
#ifndef __JAVACPP_HACK__
// this method returns mutex shared between all threads that use the same device
static std::mutex* deviceMutex();
#endif
static bool isInitialized();
static void releaseBuffers();
static LaunchContext* defaultContext();
static void swapContextBuffers(ContextBuffers& buffers);
};
} // namespace sd
#endif // LIBND4J_CUDACONTEXT_H
+74
View File
@@ -0,0 +1,74 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SAMEDIFF_THREADPOOL_H
#define SAMEDIFF_THREADPOOL_H
#include <execution/BlockingQueue.h>
#include <execution/CallableInterface.h>
#include <execution/CallableWithArguments.h>
#include <execution/Ticket.h>
#include <system/common.h>
#include <atomic>
#include <list>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
namespace samediff {
class SD_LIB_EXPORT ThreadPool {
private:
std::vector<std::thread> _threads;
std::vector<BlockingQueue<CallableWithArguments*>*> _queues;
std::vector<CallableInterface*> _interfaces;
std::mutex _lock;
std::atomic<int> _available;
std::queue<Ticket*> _tickets;
protected:
ThreadPool();
~ThreadPool();
public:
static ThreadPool& getInstance();
/**
* This method returns list of pointers to threads ONLY if num_threads of threads were available upon request,
* returning empty list otherwise
* @param num_threads
* @return
*/
Ticket* tryAcquire(int num_threads);
/**
* This method marks specified number of threads as released, and available for use
* @param num_threads
*/
void release(int num_threads = 1);
void release(Ticket* ticket);
};
} // namespace samediff
#endif // DEV_TESTS_THREADPOOL_H
+206
View File
@@ -0,0 +1,206 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SAMEDIFF_THREADS_H
#define SAMEDIFF_THREADS_H
#include <system/Environment.h>
#include <system/common.h>
#include <system/op_boilerplate.h>
#include <system/op_enums.h>
#include <functional>
namespace samediff {
class SD_LIB_EXPORT ThreadsHelper {
public:
static int numberOfThreads(int maxThreads, uint64_t numberOfElements);
static int numberOfThreads2d(int maxThreads, uint64_t iters_x, uint64_t iters_y);
static int numberOfThreads3d(int maxThreads, uint64_t iters_x, uint64_t iters_y, uint64_t iters_z);
static int pickLoop2d(int numThreads, uint64_t iters_x, uint64_t iters_y);
static int pickLoop3d(int numThreads, uint64_t iters_x, uint64_t iters_y, uint64_t iters_z);
};
class SD_LIB_EXPORT Span {
private:
int64_t _startX, _stopX, _incX;
public:
Span(int64_t start_x, int64_t stop_x, int64_t inc_x);
~Span() = default;
int64_t startX() const;
int64_t stopX() const;
int64_t incX() const;
static Span build(uint64_t thread_id, uint64_t num_threads, int64_t start_x, int64_t stop_x, int64_t inc_x);
};
class SD_LIB_EXPORT Span2 {
private:
int64_t _startX, _stopX, _incX;
int64_t _startY, _stopY, _incY;
public:
Span2(int64_t start_x, int64_t stop_x, int64_t inc_x, int64_t start_y, int64_t stop_y, int64_t inc_y);
~Span2() = default;
int64_t startX() const;
int64_t startY() const;
int64_t stopX() const;
int64_t stopY() const;
int64_t incX() const;
int64_t incY() const;
static Span2 build(int loop, uint64_t thread_id, uint64_t num_threads, int64_t start_x, int64_t stop_x, int64_t inc_x,
int64_t start_y, int64_t stop_y, int64_t inc_y);
};
class SD_LIB_EXPORT Span3 {
private:
int64_t _startX, _stopX, _incX;
int64_t _startY, _stopY, _incY;
int64_t _startZ, _stopZ, _incZ;
public:
Span3(int64_t start_x, int64_t stop_x, int64_t inc_x, int64_t start_y, int64_t stop_y, int64_t inc_y, int64_t start_z,
int64_t stop_z, int64_t inc_z);
~Span3() = default;
int64_t startX() const;
int64_t startY() const;
int64_t startZ() const;
int64_t stopX() const;
int64_t stopY() const;
int64_t stopZ() const;
int64_t incX() const;
int64_t incY() const;
int64_t incZ() const;
static Span3 build(int loop, uint64_t thread_id, uint64_t num_threads, int64_t start_x, int64_t stop_x, int64_t inc_x,
int64_t start_y, int64_t stop_y, int64_t inc_y, int64_t start_z, int64_t stop_z, int64_t inc_z);
};
class SD_LIB_EXPORT Threads {
#ifdef _OPENMP
public:
static std::mutex gThreadmutex;
static uint64_t _nFreeThreads;
static bool tryAcquire(int numThreads);
static bool freeThreads(int numThreads);
#endif
public:
/**
* This function executes 1 dimensional loop for a given number of threads
* PLEASE NOTE: this function can use smaller number of threads than requested.
*
* @param function
* @param numThreads
* @param start
* @param stop
* @param increment
* @return
*/
static int parallel_for(FUNC_1D function, long long int start, long long int stop, long long int increment = 1,
long long int numThreads = sd::Environment::getInstance().maxMasterThreads());
/**
* This function executes 1 dimensional loop for a given number of threads
*
* @param function
* @param start
* @param stop
* @param increment
* @param numThreads
* @return
*/
static int parallel_tad(FUNC_1D function, long long int start, long long int stop, long long int increment = 1,
long long int numThreads = sd::Environment::getInstance().maxMasterThreads());
/**
* This method will execute function splitting 2 nested loops space with multiple threads
*
* @param function
* @param numThreads
* @param start_x
* @param stop_x
* @param inc_x
* @param start_y
* @param stop_y
* @param inc_y
* @return
*/
static int parallel_for(FUNC_2D function, int64_t start_x, int64_t stop_x, int64_t inc_x, int64_t start_y,
int64_t stop_y, int64_t inc_y,
uint64_t numThreads = sd::Environment::getInstance().maxMasterThreads(), bool debug = false);
/**
* This method will execute function splitting 3 nested loops space with multiple threads
*
* @param function
* @param numThreads
* @param start_x
* @param stop_x
* @param inc_x
* @param start_y
* @param stop_y
* @param inc_y
* @param start_z
* @param stop_z
* @param inc_z
* @return
*/
static int parallel_for(FUNC_3D function, int64_t start_x, int64_t stop_x, int64_t inc_x, int64_t start_y,
int64_t stop_y, int64_t inc_y, int64_t start_z, int64_t stop_z, int64_t inc_z,
uint64_t numThreads = sd::Environment::getInstance().maxMasterThreads());
/**
*
* @param function
* @param numThreads
* @return
*/
static int parallel_do(FUNC_DO function,
long long int numThreads = sd::Environment::getInstance().maxMasterThreads());
static int64_t parallel_long(FUNC_RL function, FUNC_AL aggregator, long long int start, long long int stop,
long long int increment = 1,
long long int numThreads = sd::Environment::getInstance().maxMasterThreads());
static double parallel_double(FUNC_RD function, FUNC_AD aggregator, int64_t start, int64_t stop,
int64_t increment = 1,
uint64_t numThreads = sd::Environment::getInstance().maxMasterThreads());
/**
* This method will execute function in parallel preserving the parts to be aligned increment size
* PLEASE NOTE: this function can use smaller number of threads than requested.
*
*/
static int parallel_aligned_increment(FUNC_1D function, int64_t start, int64_t stop, int64_t increment,
size_t type_size = sizeof(float),
uint32_t req_numThreads = sd::Environment::getInstance().maxMasterThreads());
};
} // namespace samediff
#endif // SAMEDIFF_THREADS_H
+74
View File
@@ -0,0 +1,74 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#ifndef SAMEDIFF_TICKET_H
#define SAMEDIFF_TICKET_H
#include <execution/BlockingQueue.h>
#include <execution/CallableInterface.h>
#include <execution/CallableWithArguments.h>
#include <system/common.h>
#include <atomic>
#include <mutex>
#include <vector>
namespace samediff {
class SD_LIB_EXPORT Ticket {
private:
bool _acquired = false;
std::vector<BlockingQueue<CallableWithArguments *> *> _queues;
std::vector<CallableWithArguments *> _callables;
std::vector<CallableInterface *> _interfaces;
uint32_t _acquiredThreads = 0;
public:
explicit Ticket(const std::vector<BlockingQueue<CallableWithArguments *> *> &queues);
Ticket();
~Ticket() = default;
bool acquired();
void acquiredThreads(uint32_t threads);
void attach(uint32_t thread_id, CallableInterface *call_interface);
// deprecated one
void enqueue(int thread_id, CallableWithArguments *callable);
void enqueue(uint32_t thread_id, uint32_t num_threads, int64_t *lpt, FUNC_RL func, int64_t start_x, int64_t stop_x,
int64_t inc_x);
void enqueue(uint32_t thread_id, uint32_t num_threads, double *lpt, FUNC_RD func, int64_t start_x, int64_t stop_x,
int64_t inc_x);
void enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_DO func);
void enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_1D func, int64_t start_x, int64_t stop_x, int64_t inc_x);
void enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_2D func, int64_t start_x, int64_t stop_x, int64_t inc_x,
int64_t start_y, int64_t stop_y, int64_t inc_y);
void enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_3D func, int64_t start_x, int64_t stop_x, int64_t inc_x,
int64_t start_y, int64_t stop_y, int64_t inc_y, int64_t start_, int64_t stop_z, int64_t inc_z);
void waitAndRelease();
};
} // namespace samediff
#endif // DEV_TESTS_TICKET_H
@@ -0,0 +1,38 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/AffinityManager.h>
namespace sd {
int AffinityManager::currentDeviceId() { return 0; }
int AffinityManager::currentNativeDeviceId() { return 0; }
int AffinityManager::numberOfDevices() { return 1; }
void AffinityManager::setCurrentDevice(int deviceId) {
// no-op
}
void AffinityManager::setCurrentNativeDevice(int deviceId) {
// no-op
}
} // namespace sd
@@ -0,0 +1,78 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/AffinityManager.h>
#include <execution/ContextBuffers.h>
namespace sd {
ContextBuffers::ContextBuffers() { _deviceId = AffinityManager::currentDeviceId(); }
ContextBuffers::~ContextBuffers() {
// no-op
}
ContextBuffers::ContextBuffers(void* rPointer, void* sPointer, void* aPointer, bool isOwner) {
_reductionPointer = rPointer;
_scalarPointer = sPointer;
_allocationPointer = aPointer;
_allocated = isOwner;
}
ContextBuffers::ContextBuffers(const ContextBuffers& other) {
//
}
void ContextBuffers::initialize() {
// no-op
}
void* ContextBuffers::reductionBuffer() { return _reductionPointer; }
void* ContextBuffers::scalarBuffer() { return _scalarPointer; }
void* ContextBuffers::allocationBuffer() { return _allocationPointer; }
void ContextBuffers::setReductionBuffer(void* pointer) { _reductionPointer = pointer; }
void ContextBuffers::setScalarBuffer(void* pointer) { _scalarPointer = pointer; }
void ContextBuffers::setAllocationBuffer(void* pointer) { _allocationPointer = pointer; }
void ContextBuffers::triggerOwnership(bool isOwner) { _allocated = isOwner; }
int ContextBuffers::deviceId() { return _deviceId; }
void* ContextBuffers::execStream() { return _execStream; }
void* ContextBuffers::specialStream() { return _specialStream; }
bool ContextBuffers::isInitialized() { return true; }
void ContextBuffers::release() {
//
}
ContextBuffers& ContextBuffers::operator=(const ContextBuffers& other) { return *this; }
ContextBuffers& ContextBuffers::operator=(ContextBuffers&& other) { return *this; }
sd::ErrorReference* ContextBuffers::errorReference() { return &_errorReference; }
} // namespace sd
@@ -0,0 +1,119 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// Created by raver119 on 30.11.17.
//
#include <exceptions/cuda_exception.h>
#include <execution/AffinityManager.h>
#include <execution/LaunchContext.h>
#include <helpers/logger.h>
#include <thread>
// NOTE: Removed thread_local to fix "cannot allocate memory in static TLS block" error
// when JavaCPP loads library via dlopen(). This error occurs because:
// 1. dlopen() loads the library at runtime (not at program startup)
// 2. thread_local variables require space in the static TLS block
// 3. The static TLS block has limited size and cannot be extended after program start
// 4. When loaded via dlopen(), the library's TLS requirements must fit in remaining space
// 5. If sanitizers or lifecycle tracking are enabled, TLS space may already be exhausted
//
// iOS/Apple/Android builds already avoid thread_local for similar reasons.
// For CPU builds, making this a non-thread-local global is acceptable because:
// - Each LaunchContext instance maintains its own thread-safe state
// - The global contextBuffers is used as a default/fallback buffer pool
// - Proper synchronization is handled at the LaunchContext level
sd::ContextBuffers contextBuffers = sd::ContextBuffers();
#if HAVE_ONEDNN
#include <dnnl.hpp>
#endif
namespace sd {
LaunchContext::~LaunchContext() {
#if HAVE_ONEDNN
// Intentionally NOT deleting _engine to avoid use-after-free during shutdown
// The LaunchContext objects are kept alive in a function-local static vector (contexts())
// to survive JVM shutdown. Deleting _engine here causes crashes because
// OneDNN's static cleanup may have already run, making the engine invalid.
// This is safe because LaunchContext instances are never destroyed during
// normal operation - only during process exit when memory cleanup doesn't matter.
// delete reinterpret_cast<dnnl::engine*>(_engine);
#endif
}
// Use pointer to prevent static destructor from running.
// The vector is intentionally leaked to avoid crashes during shutdown when:
// 1. An exception is thrown that references LaunchContext
// 2. JVM shuts down and runs static destructors
// 3. The vector's destructor tries to destroy stored contexts while they're still in use
// This is safe because LaunchContexts are only created during initialization and
// process exit cleans up all memory anyway.
std::vector<LaunchContext*>& LaunchContext::contexts() {
static std::vector<LaunchContext*>* _contexts = new std::vector<LaunchContext*>();
return *_contexts;
}
SD_MAP_IMPL<int, std::mutex*> LaunchContext::_deviceMutexes;
std::mutex LaunchContext::_mutex;
////////////////////////////////////////////////////////////////////////
LaunchContext::LaunchContext() {
// default constructor, just to make clang/ranlib happy
_workspace = nullptr;
_deviceID = 0;
#if HAVE_ONEDNN
_engine = new dnnl::engine(dnnl::engine::kind::cpu, 0);
#endif
}
LaunchContext::LaunchContext(sd::Pointer cudaStream, sd::Pointer reductionPointer, sd::Pointer scalarPointer,
sd::Pointer allocationPointer) {}
static std::mutex _lock;
LaunchContext* LaunchContext::defaultContext() {
{
// synchronous block goes here
std::lock_guard<std::mutex> lock(_lock);
// TODO: we need it to be device-aware, but only once we add NUMA support for cpu
if (LaunchContext::contexts().empty())
LaunchContext::contexts().emplace_back(new LaunchContext());
}
// return context for current device
return LaunchContext::contexts().at(0);
}
std::mutex* LaunchContext::deviceMutex() { return &_mutex; }
void LaunchContext::swapContextBuffers(ContextBuffers& buffers) {
//
}
bool LaunchContext::isInitialized() { return true; }
void LaunchContext::releaseBuffers() {
//
}
sd::ErrorReference* LaunchContext::errorReference() { return contextBuffers.errorReference(); }
void* LaunchContext::engine() { return _engine; }
} // namespace sd
@@ -0,0 +1,119 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <exceptions/cuda_exception.h>
#include <execution/AffinityManager.h>
#include <execution/LaunchContext.h>
#include <helpers/logger.h>
thread_local int globalThreadToDevice = -1;
namespace sd {
std::mutex AffinityManager::_currentMutex;
std::mutex AffinityManager::_numberMutex;
int AffinityManager::_numberOfDevices = -1;
int AffinityManager::currentDeviceId() {
// if there's no affinity set - set it now
if (globalThreadToDevice < 0) {
// this block must be thread-local
_currentMutex.lock();
globalThreadToDevice = _lastDevice++;
// we need to check if we've got deviceId >= number of actual devices, and reset to zero otherwise
if (globalThreadToDevice >= numberOfDevices()) {
globalThreadToDevice = 0;
_lastDevice = numberOfDevices() > 1 ? 1 : 0;
}
_currentMutex.unlock();
setCurrentNativeDevice(globalThreadToDevice);
}
// if we already know affinity - just return it
if (globalThreadToDevice >= 0) return globalThreadToDevice;
int dev = 0;
auto res = cudaGetDevice(&dev);
if (res != 0) throw cuda_exception::build("cudaGetDevice failed", res);
return dev;
}
int AffinityManager::currentNativeDeviceId() {
int dev = 0;
auto res = cudaGetDevice(&dev);
if (res != 0) throw cuda_exception::build("cudaGetDevice failed", res);
return dev;
}
int AffinityManager::numberOfDevices() {
_numberMutex.lock();
// we want to cache number of devices
if (_numberOfDevices <= 0) {
int dev = 0;
auto res = cudaGetDeviceCount(&dev);
if (res != 0) throw cuda_exception::build("cudaGetDeviceCount failed", res);
_numberOfDevices = dev;
}
_numberMutex.unlock();
return _numberOfDevices;
}
void AffinityManager::setCurrentNativeDevice(int deviceId) {
auto res = cudaSetDevice(deviceId);
if (res != 0) throw cuda_exception::build("setCurrentDevice failed", res);
}
void AffinityManager::setCurrentDevice(int deviceId) {
auto previousDeviceId = globalThreadToDevice;
if (previousDeviceId >= 0 && LaunchContext::isInitialized()) {
auto res = cudaStreamSynchronize(*LaunchContext::defaultContext()->getCudaStream());
if (res != 0) throw cuda_exception::build("setCurrentDevice -> sync failed", res);
res = cudaStreamSynchronize(*LaunchContext::defaultContext()->getCudaSpecialStream());
if (res != 0) throw cuda_exception::build("setCurrentDevice -> specialSync failed", res);
if (deviceId != previousDeviceId) {
// discard existing stuff
LaunchContext::releaseBuffers();
}
}
if (deviceId != previousDeviceId) {
auto res = cudaSetDevice(deviceId);
if (res != 0) throw cuda_exception::build("cudaSetDevice failed", res);
}
// update thread-device affinity
globalThreadToDevice = deviceId;
}
std::atomic<int> AffinityManager::_lastDevice; // = std::atomic<int>(initialV);
} // namespace sd
@@ -0,0 +1,202 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <cuda.h>
#include <cuda_device_runtime_api.h>
#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include <exceptions/cuda_exception.h>
#include <execution/AffinityManager.h>
#include <execution/ContextBuffers.h>
#include <helpers/logger.h>
namespace sd {
ContextBuffers::ContextBuffers() {
_deviceId = AffinityManager::currentDeviceId();
}
ContextBuffers::ContextBuffers(const ContextBuffers& other) {
release();
this->_initialized = other._initialized;
this->_allocated = other._allocated;
this->_deviceId = other._deviceId;
this->_specialStream = other._specialStream;
this->_execStream = other._execStream;
this->_allocationPointer = other._allocationPointer;
this->_reductionPointer = other._reductionPointer;
this->_scalarPointer = other._scalarPointer;
}
ContextBuffers& ContextBuffers::operator=(const ContextBuffers& other) {
release();
this->_initialized = other._initialized;
this->_allocated = other._allocated;
this->_deviceId = other._deviceId;
this->_specialStream = other._specialStream;
this->_execStream = other._execStream;
this->_allocationPointer = other._allocationPointer;
this->_reductionPointer = other._reductionPointer;
this->_scalarPointer = other._scalarPointer;
return *this;
}
ContextBuffers& ContextBuffers::operator=(ContextBuffers&& other) {
release();
this->_initialized = other._initialized;
this->_allocated = other._allocated;
this->_deviceId = other._deviceId;
this->_specialStream = other._specialStream;
this->_execStream = other._execStream;
this->_allocationPointer = other._allocationPointer;
this->_reductionPointer = other._reductionPointer;
this->_scalarPointer = other._scalarPointer;
return *this;
}
void ContextBuffers::release() {
if (_allocated) {
if (_allocationPointer != nullptr) cudaFree(_allocationPointer);
if (_scalarPointer != nullptr) cudaFreeHost(_scalarPointer);
if (_allocationPointer != nullptr) cudaFree(_reductionPointer);
auto _cudaStream = reinterpret_cast<cudaStream_t*>(_execStream);
auto _cudaSpecialStream = reinterpret_cast<cudaStream_t*>(_specialStream);
cudaStreamSynchronize(*_cudaStream);
cudaStreamSynchronize(*_cudaSpecialStream);
cudaStreamDestroy(*_cudaStream);
cudaStreamDestroy(*_cudaSpecialStream);
delete _cudaStream;
delete _cudaSpecialStream;
//////
_allocated = false;
_deviceId = -1;
this->_specialStream = nullptr;
this->_execStream = nullptr;
this->_allocationPointer = nullptr;
this->_reductionPointer = nullptr;
this->_scalarPointer = nullptr;
}
_initialized = false;
}
ContextBuffers::~ContextBuffers() { release(); }
ContextBuffers::ContextBuffers(void* rPointer, void* sPointer, void* aPointer, bool isOwner) {
_reductionPointer = rPointer;
_scalarPointer = sPointer;
_allocationPointer = aPointer;
_allocated = isOwner;
}
void ContextBuffers::initialize() {
_deviceId = AffinityManager::currentNativeDeviceId();
auto res = cudaMalloc(reinterpret_cast<void**>(&_reductionPointer), 1024 * 1024 * 8);
if (res != 0) throw cuda_exception::build("_reductionPointer allocation failed", res);
res = cudaHostAlloc(reinterpret_cast<void**>(&_scalarPointer), 16, cudaHostAllocDefault);
if (res != 0) throw cuda_exception::build("_scalarPointer allocation failed", res);
res = cudaMalloc(reinterpret_cast<void**>(&_allocationPointer), 1024 * 1024 * 8);
if (res != 0) throw cuda_exception::build("_allocationPointer allocation failed", res);
_execStream = new cudaStream_t();
_specialStream = new cudaStream_t();
if (nullptr == _execStream || nullptr == _specialStream)
THROW_EXCEPTION("Failed to allocate memory for new CUDA stream");
res = cudaStreamCreate(reinterpret_cast<cudaStream_t*>(_execStream));
if (res != 0) throw cuda_exception::build("Failed to create default CUDA stream with launch context", res);
res = cudaStreamCreate(reinterpret_cast<cudaStream_t*>(_specialStream));
if (res != 0) throw cuda_exception::build("Failed to create special CUDA stream with launch context", res);
_allocated = true;
_initialized = true;
}
void* ContextBuffers::reductionBuffer() {
if (!_initialized) initialize();
return _reductionPointer;
}
void* ContextBuffers::scalarBuffer() {
if (!_initialized) initialize();
return _scalarPointer;
}
void* ContextBuffers::allocationBuffer() {
if (!_initialized) initialize();
return _allocationPointer;
}
void ContextBuffers::setReductionBuffer(void* pointer) { _reductionPointer = pointer; }
void ContextBuffers::setScalarBuffer(void* pointer) { _scalarPointer = pointer; }
void ContextBuffers::setAllocationBuffer(void* pointer) { _allocationPointer = pointer; }
void ContextBuffers::triggerOwnership(bool isOwner) { _allocated = isOwner; }
int ContextBuffers::deviceId() { return _deviceId; }
void* ContextBuffers::execStream() {
if (!_initialized) {
initialize();
} else {
}
return _execStream;
}
void* ContextBuffers::specialStream() {
if (!_initialized) {
initialize();
} else {
}
return _specialStream;
}
bool ContextBuffers::isInitialized() { return _initialized; }
ErrorReference* ContextBuffers::errorReference() { return &_errorReference; }
} // namespace sd
@@ -0,0 +1,473 @@
#include "DeviceValidator.h"
ValidationResult::ValidationResult()
: isComputeCapabilitySufficient(true),
isManagedMemorySupported(true),
isComputePreemptionSupported(true),
isThreadsPerBlockWithinLimit(true),
isBlocksWithinGridSizeLimit(true),
isSharedMemoryUsageWithinLimit(true),
isRegisterUsageWithinLimit(true),
isTotalThreadsWithinLimit(true),
isGlobalMemoryUsageWithinLimit(true),
isMemoryUsageWithinLimit(true),
isLocalMemoryUsageWithinLimit(true),
isConcurrentKernelsSupported(true),
isL2CacheSizeSufficient(true) {}
DeviceValidator* DeviceValidator::instance = nullptr;
std::mutex DeviceValidator::mtx;
DeviceValidator* DeviceValidator::getInstance(const std::string& directory, int device) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new DeviceValidator(directory, device);
}
return instance;
}
DeviceValidator::DeviceValidator(const std::string& directoryPath, int device)
: directoryPath(directoryPath) {
cudaGetDeviceProperties(&prop, device);
init();
}
DeviceValidator::~DeviceValidator() {
for (auto& pair : moduleMap) {
cuModuleUnload(pair.second);
}
moduleMap.clear();
functionMap.clear();
}
std::vector<std::string> DeviceValidator::parsePTXFile(const std::string& filePath) {
std::ifstream file(filePath);
std::string line;
std::vector<std::string> functionNames;
std::regex functionPattern("\\.entry\\s+([a-zA-Z_][a-zA-Z0-9_]*)");
while (std::getline(file, line)) {
std::smatch match;
if (std::regex_search(line, match, functionPattern) && match.size() > 1) {
functionNames.push_back(match.str(1));
}
}
return functionNames;
}
std::vector<std::string> DeviceValidator::parseCUBINFile(const std::string& filePath) {
std::string command = "cuobjdump -sass " + filePath + " | grep -oP '(?<=FUNC ).*(?=\\()'";
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
std::stringstream ss(result);
std::string functionName;
std::vector<std::string> functionNames;
while (std::getline(ss, functionName, '\n')) {
functionNames.push_back(functionName);
}
return functionNames;
}
void DeviceValidator::init() {
// Check if cuobjdump is available
#ifdef _WIN32
// Windows
if (system("cuobjdump --version > nul 2>&1") != 0) {
throw std::runtime_error("cuobjdump is not available on the system's PATH. Please install it and try again.");
}
#else
// Linux
if (system("cuobjdump --version > /dev/null 2>&1") != 0) {
throw std::runtime_error("cuobjdump is not available on the system's PATH. Please install it and try again.");
}
#endif
printf("Initializing DeviceValidator at path %s\n", directoryPath.c_str());
for (const auto &entry : std::filesystem::directory_iterator(directoryPath)) {
if (entry.path().extension() == ".ptx" || entry.path().extension() == ".cubin") {
CUmodule cuModule;
printf("Adding path %s\n",entry.path().filename().c_str());
if (cuModuleLoad(&cuModule, entry.path().c_str()) == CUDA_SUCCESS) {
moduleMap[entry.path().filename().string()] = cuModule;
if (entry.path().extension() == ".ptx") {
std::vector<std::string> functionNames = parsePTXFile(entry.path().c_str());
for (const auto& functionName : functionNames) {
CUfunction cuFunction;
if (cuModuleGetFunction(&cuFunction, cuModule, functionName.c_str()) == CUDA_SUCCESS) {
functionMap[functionName] = cuFunction;
}
}
} else if (entry.path().extension() == ".cubin") {
std::vector<std::string> functionNames = parseCUBINFile(entry.path().c_str());
for (const auto& functionName : functionNames) {
CUfunction cuFunction;
if (cuModuleGetFunction(&cuFunction, cuModule, functionName.c_str()) == CUDA_SUCCESS) {
functionMap[functionName] = cuFunction;
}
}
}
}
}
}
}
void DeviceValidator::setKernelAttribute(const std::string& functionName, CUfunction_attribute attribute, int value) {
if (functionMap.find(functionName) != functionMap.end()) {
cuFuncSetAttribute(functionMap[functionName], attribute, value);
}
}
void DeviceValidator::setKernelMaxDynamicSharedSizeBytes(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, value);
}
void DeviceValidator::setKernelPreferredSharedMemoryCarveout(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_PREFERRED_SHARED_MEMORY_CARVEOUT, value);
}
void DeviceValidator::setKernelMaxRegisters(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_NUM_REGS, value);
}
void DeviceValidator::setKernelMaxThreadsPerBlock(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, value);
}
void DeviceValidator::setKernelNumRegs(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_NUM_REGS, value);
}
void DeviceValidator::setKernelSharedSizeBytes(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, value);
}
void DeviceValidator::setKernelBinaryVersion(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_BINARY_VERSION, value);
}
void DeviceValidator::setKernelCacheModeCA(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_CACHE_MODE_CA, value);
}
void DeviceValidator::setKernelMaxThreadsPerBlockOptIn(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, value);
}
void DeviceValidator::setKernelReservedSharedSizeBytes(const std::string& functionName, int value) {
setKernelAttribute(functionName, CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, value);
}
void DeviceValidator::setAllKernelsAttribute(CUfunction_attribute attribute, int value) {
for (auto& pair : functionMap) {
cuFuncSetAttribute(pair.second, attribute, value);
}
}
void DeviceValidator::setAllKernelsMaxDynamicSharedSizeBytes(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, value);
}
void DeviceValidator::setAllKernelsPreferredSharedMemoryCarveout(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_PREFERRED_SHARED_MEMORY_CARVEOUT, value);
}
void DeviceValidator::setAllKernelsMaxRegisters(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_NUM_REGS, value);
}
void DeviceValidator::setAllKernelsMaxThreadsPerBlock(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, value);
}
void DeviceValidator::setAllKernelsNumRegs(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_NUM_REGS, value);
}
void DeviceValidator::setAllKernelsSharedSizeBytes(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, value);
}
void DeviceValidator::setAllKernelsBinaryVersion(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_BINARY_VERSION, value);
}
void DeviceValidator::setAllKernelsCacheModeCA(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_CACHE_MODE_CA, value);
}
void DeviceValidator::setAllKernelsMaxThreadsPerBlockOptIn(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, value);
}
void DeviceValidator::setAllKernelsReservedSharedSizeBytes(int value) {
setAllKernelsAttribute(CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, value);
}
void DeviceValidator::printKernelAttribute(const char* name, CUfunction_attribute attribute) {
CUfunction kernel = functionMap[name];
int value;
cuFuncGetAttribute(&value, attribute, kernel);
std::cout << "Attribute " << attribute << " for function " << name << " is " << value << std::endl;
}
ValidationResult DeviceValidator::validateKernelLaunch(const char* name, dim3 threadsPerBlock, dim3 numBlocks,
size_t globalMemoryUsage) {
ValidationResult result;
CUfunction kernel;
// Look up the function from the function map
auto it = functionMap.find(name);
if (it != functionMap.end()) {
kernel = it->second;
} else {
// If the function is not found in the map, return an invalid ValidationResult
return result;
}
int sharedSizeBytes, numRegs, maxThreadsPerBlock;
cuFuncGetAttribute(&sharedSizeBytes, CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, kernel);
cuFuncGetAttribute(&numRegs, CU_FUNC_ATTRIBUTE_NUM_REGS, kernel);
cuFuncGetAttribute(&maxThreadsPerBlock, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, kernel);
result.sharedSizeBytes = sharedSizeBytes;
result.numRegs = numRegs;
result.maxThreadsPerBlock = maxThreadsPerBlock;
result.isComputeCapabilitySufficient = true;
result.isECCMemorySupported = prop.ECCEnabled;
result.isManagedMemorySupported = prop.managedMemory;
result.isComputePreemptionSupported = prop.computePreemptionSupported;
result.isConcurrentKernelsSupported = prop.concurrentKernels;
result.isL2CacheSizeSufficient = prop.l2CacheSize;
result.isThreadsPerBlockWithinLimit = threadsPerBlock.x * threadsPerBlock.y * threadsPerBlock.z <= prop.maxThreadsPerBlock;
result.isThreadsPerBlockWithinLimit &= threadsPerBlock.x * threadsPerBlock.y * threadsPerBlock.z <= maxThreadsPerBlock;
result.numBlocks = numBlocks.x;
result.isBlocksWithinGridSizeLimit = numBlocks.x <= prop.maxGridSize[0] && numBlocks.y <= prop.maxGridSize[1] && numBlocks.z <= prop.maxGridSize[2];
result.isSharedMemoryUsageWithinLimit = sharedSizeBytes <= prop.sharedMemPerBlock;
result.isRegisterUsageWithinLimit = numRegs * threadsPerBlock.x * threadsPerBlock.y * threadsPerBlock.z <= prop.regsPerBlock;
result.numThreads = threadsPerBlock.x * threadsPerBlock.y * threadsPerBlock.z * numBlocks.x * numBlocks.y * numBlocks.z;
result.isTotalThreadsWithinLimit = threadsPerBlock.x * threadsPerBlock.y * threadsPerBlock.z * numBlocks.x * numBlocks.y * numBlocks.z <= prop.maxThreadsPerMultiProcessor * prop.multiProcessorCount;
result.isGlobalMemoryUsageWithinLimit = globalMemoryUsage <= prop.totalGlobalMem;
result.globalMemory = globalMemoryUsage;
size_t freeMemory, totalMemory;
cudaMemGetInfo(&freeMemory, &totalMemory);
size_t usedMemory = totalMemory - freeMemory;
result.memoryUsage = usedMemory + globalMemoryUsage;
result.isMemoryUsageWithinLimit = result.memoryUsage <= totalMemory;
result.isLocalMemoryUsageWithinLimit = usedMemory + globalMemoryUsage <= prop.localL1CacheSupported ? totalMemory : prop.localL1CacheSupported;
result.freeMemory = freeMemory;
result.totalMemory = totalMemory;
return result;
}
void DeviceValidator::printProblematicFunctions(dim3 threadsPerBlock, dim3 numBlocks, size_t globalMemory) {
printf("Attempting to print functions with size of map %d\n",functionMap.size());
for (const auto& pair : functionMap) {
const std::string functionName = pair.first;
CUfunction function = pair.second;
ValidationResult validationResult = validateKernelLaunch(functionName.c_str(),threadsPerBlock, numBlocks, globalMemory);
if (!validationResult.isValid()) {
std::cout << "Function " << functionName << " has the following problems:\n";
printValidationResult(functionName.c_str(),validationResult);
std::cout << std::endl;
}
}
}
bool ValidationResult::isValid() {
return isComputeCapabilitySufficient &&
isManagedMemorySupported &&
isComputePreemptionSupported &&
isThreadsPerBlockWithinLimit &&
isBlocksWithinGridSizeLimit &&
isSharedMemoryUsageWithinLimit &&
isRegisterUsageWithinLimit &&
isTotalThreadsWithinLimit &&
isGlobalMemoryUsageWithinLimit &&
isMemoryUsageWithinLimit &&
isLocalMemoryUsageWithinLimit &&
isConcurrentKernelsSupported &&
isL2CacheSizeSufficient;
}
void DeviceValidator::printValidationResult(const char* name, ValidationResult& result) {
printf("Validating: %s\n",name);
if (!result.isValid()) {
std::cout << "Function " << name << " has an issue:\n";
if (!result.isComputeCapabilitySufficient) {
std::cout << " - Compute capability is not sufficient. Required: " << result.isComputeCapabilitySufficient << ", Actual: " << prop.major * 10 + prop.minor << "\n";
}
if (!result.isManagedMemorySupported) {
std::cout << " - Managed memory is not supported.\n";
}
if (!result.isComputePreemptionSupported) {
std::cout << " - Compute preemption is not supported.\n";
}
if (!result.isThreadsPerBlockWithinLimit) {
std::cout << " - Threads per block is not within limit. Max: " << prop.maxThreadsPerBlock << "\n";
std::cout << " - Value is: " << result.maxThreadsPerBlock << "\n";
}
if (!result.isBlocksWithinGridSizeLimit) {
// result.isRegisterUsageWithinLimit = numRegs * threadsPerBlock.x * threadsPerBlock.y * threadsPerBlock.z <= prop.regsPerBlock;
std::cout << " - Blocks within grid size is not within limit. Max: (" << prop.maxGridSize[0] << ", " << prop.maxGridSize[1] << ", " << prop.maxGridSize[2] << ")\n";
std::cout << " - Value is: (" << result.numBlocks << ", " << result.numBlocks << ", " << result.numBlocks << ")\n";
}
if (!result.isSharedMemoryUsageWithinLimit) {
std::cout << " - Shared memory usage is not within limit. Max: " << prop.sharedMemPerBlock << "\n";
}
if (!result.isRegisterUsageWithinLimit) {
std::cout << " - Register usage is not within limit. Max: " << prop.regsPerBlock << "\n";
std::cout << " - Value is: (" << result.numRegs << ", " << result.numBlocks << "\n";
}
if (!result.isTotalThreadsWithinLimit) {
std::cout << " - Total threads is not within limit. Max: " << prop.maxThreadsPerMultiProcessor * prop.multiProcessorCount << "\n";
std::cout << " - Value is: " << result.numThreads << "\n";
}
if (!result.isGlobalMemoryUsageWithinLimit) {
std::cout << " - Global memory usage is not within limit. Max: " << prop.totalGlobalMem << "\n";
std::cout << " - Value is: " << result.globalMemory << "\n";
}
if (!result.isMemoryUsageWithinLimit) {
std::cout << " - Memory usage is not within limit.\n";
std::cout << " - Value is: " << result.memoryUsage << "\n";
}
if (!result.isLocalMemoryUsageWithinLimit) {
std::cout << " - Local memory usage is not within limit. Max: " << (prop.localL1CacheSupported ? prop.localL1CacheSupported : prop.totalGlobalMem) << "\n";
}
if (!result.isConcurrentKernelsSupported) {
std::cout << " - Concurrent kernels is not supported.\n";
}
if (!result.isL2CacheSizeSufficient) {
std::cout << " - L2 cache size is not sufficient. Max: " << prop.l2CacheSize << "\n";
}
}
}
void DeviceValidator::printMaxKernelAttributes() {
std::cout << "Device name: " << prop.name << std::endl;
std::cout << "Total global memory: " << prop.totalGlobalMem << std::endl;
std::cout << "Shared memory per block: " << prop.sharedMemPerBlock << std::endl;
std::cout << "Registers per block: " << prop.regsPerBlock << std::endl;
std::cout << "Warp size: " << prop.warpSize << std::endl;
std::cout << "Max pitch: " << prop.memPitch << std::endl;
std::cout << "Max threads per block: " << prop.maxThreadsPerBlock << std::endl;
std::cout << "Max thread dimensions: (" << prop.maxThreadsDim[0] << ", " << prop.maxThreadsDim[1] << ", " << prop.maxThreadsDim[2] << ")" << std::endl;
std::cout << "Max grid dimensions: (" << prop.maxGridSize[0] << ", " << prop.maxGridSize[1] << ", " << prop.maxGridSize[2] << ")" << std::endl;
std::cout << "Clock rate: " << prop.clockRate << std::endl;
std::cout << "Total constant memory: " << prop.totalConstMem << std::endl;
std::cout << "Compute capability: " << prop.major << "." << prop.minor << std::endl;
std::cout << "Texture alignment: " << prop.textureAlignment << std::endl;
std::cout << "Concurrent copy and execution: " << (prop.deviceOverlap ? "Yes" : "No") << std::endl;
std::cout << "Number of multiprocessors: " << prop.multiProcessorCount << std::endl;
std::cout << "Kernel execution timeout: " << (prop.kernelExecTimeoutEnabled ? "Yes" : "No") << std::endl;
std::cout << "Integrated: " << (prop.integrated ? "Yes" : "No") << std::endl;
std::cout << "Can map host memory: " << (prop.canMapHostMemory ? "Yes" : "No") << std::endl;
std::cout << "Compute mode: " << (prop.computeMode == cudaComputeModeDefault ? "Default" : (prop.computeMode == cudaComputeModeExclusive ? "Exclusive" : (prop.computeMode == cudaComputeModeProhibited ? "Prohibited" : "Exclusive Process"))) << std::endl;
std::cout << "Max texture 1D: " << prop.maxTexture1D << std::endl;
std::cout << "Max texture 2D: (" << prop.maxTexture2D[0] << ", " << prop.maxTexture2D[1] << ")" << std::endl;
std::cout << "Max texture 3D: (" << prop.maxTexture3D[0] << ", " << prop.maxTexture3D[1] << ", " << prop.maxTexture3D[2] << ")" << std::endl;
std::cout << "Concurrent kernels: " << (prop.concurrentKernels ? "Yes" : "No") << std::endl;
std::cout << "ECC enabled: " << (prop.ECCEnabled ? "Yes" : "No") << std::endl;
std::cout << "PCI bus ID: " << prop.pciBusID << std::endl;
std::cout << "PCI device ID: " << prop.pciDeviceID << std::endl;
std::cout << "TCC driver: " << (prop.tccDriver ? "Yes" : "No") << std::endl;
std::cout << "Memory clock rate: " << prop.memoryClockRate << std::endl;
std::cout << "Global memory bus width: " << prop.memoryBusWidth << std::endl;
std::cout << "L2 cache size: " << prop.l2CacheSize << std::endl;
std::cout << "Max threads per multiprocessor: " << prop.maxThreadsPerMultiProcessor << std::endl;
std::cout << "Stream priorities supported: " << (prop.streamPrioritiesSupported ? "Yes" : "No") << std::endl;
std::cout << "Global L1 cache supported: " << (prop.globalL1CacheSupported ? "Yes" : "No") << std::endl;
std::cout << "Local L1 cache supported: " << (prop.localL1CacheSupported ? "Yes" : "No") << std::endl;
std::cout << "Shared memory per multiprocessor: " << prop.sharedMemPerMultiprocessor << std::endl;
std::cout << "Registers per multiprocessor: " << prop.regsPerMultiprocessor << std::endl;
std::cout << "Managed memory concurrent: " << (prop.concurrentManagedAccess ? "Yes" : "No") << std::endl;
std::cout << "Is multi GPU board: " << (prop.isMultiGpuBoard ? "Yes" : "No") << std::endl;
std::cout << "Multi GPU board group ID: " << prop.multiGpuBoardGroupID << std::endl;
std::cout << "Host native atomic supported: " << (prop.hostNativeAtomicSupported ? "Yes" : "No") << std::endl;
std::cout << "Single to double precision perf ratio: " << prop.singleToDoublePrecisionPerfRatio << std::endl;
std::cout << "Pageable memory access: " << (prop.pageableMemoryAccess ? "Yes" : "No") << std::endl;
std::cout << "Concurrent managed access: " << (prop.concurrentManagedAccess ? "Yes" : "No") << std::endl;
}
void DeviceValidator::printKernelAttributes(const char* name) {
auto it = functionMap.find(name);
if (it == functionMap.end()) {
std::cerr << "Error: Function " << name << " not found." << std::endl;
return;
}
CUfunction kernel = it->second;
int sharedSizeBytes, numRegs, maxThreadsPerBlock, binaryVersion, cacheModeCA, maxDynamicSharedSizeBytes, preferredSharedMemoryCarveout;
cuFuncGetAttribute(&sharedSizeBytes, CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, kernel);
cuFuncGetAttribute(&numRegs, CU_FUNC_ATTRIBUTE_NUM_REGS, kernel);
cuFuncGetAttribute(&maxThreadsPerBlock, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, kernel);
cuFuncGetAttribute(&binaryVersion, CU_FUNC_ATTRIBUTE_BINARY_VERSION, kernel);
cuFuncGetAttribute(&cacheModeCA, CU_FUNC_ATTRIBUTE_CACHE_MODE_CA, kernel);
cuFuncGetAttribute(&maxDynamicSharedSizeBytes, CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, kernel);
cuFuncGetAttribute(&preferredSharedMemoryCarveout, CU_FUNC_ATTRIBUTE_PREFERRED_SHARED_MEMORY_CARVEOUT, kernel);
std::cout << "Kernel name: " << name << std::endl;
std::cout << "Shared memory per block: " << sharedSizeBytes << std::endl;
std::cout << "Registers per block: " << numRegs << std::endl;
std::cout << "Max threads per block: " << maxThreadsPerBlock << std::endl;
std::cout << "Binary version: " << binaryVersion << std::endl;
std::cout << "Cache mode CA: " << cacheModeCA << std::endl;
std::cout << "Max dynamic shared size bytes: " << maxDynamicSharedSizeBytes << std::endl;
std::cout << "Preferred shared memory carveout: " << preferredSharedMemoryCarveout << std::endl;
if (sharedSizeBytes > prop.sharedMemPerBlock) {
std::cout << "WARNING: The kernel uses more shared memory per block than the device supports." << std::endl;
}
if (numRegs > prop.regsPerBlock) {
std::cout << "WARNING: The kernel uses more registers per block than the device supports." << std::endl;
}
if (maxThreadsPerBlock > prop.maxThreadsPerBlock) {
std::cout << "WARNING: The kernel uses more threads per block than the device supports." << std::endl;
}
// Note: There are no device properties to compare with binaryVersion, cacheModeCA, maxDynamicSharedSizeBytes, and preferredSharedMemoryCarveout.
}
std::map<std::string, ValidationResult> DeviceValidator::collectResourceProblems() {
std::map<std::string, ValidationResult> problematicFunctions;
for (const auto& pair : functionMap) {
const char* name = pair.first.c_str();
ValidationResult result = validateKernelLaunch(name, dim3(1, 1, 1), dim3(1, 1, 1), 0);
if (!result.isComputeCapabilitySufficient ||
!result.isECCMemorySupported ||
!result.isManagedMemorySupported ||
!result.isComputePreemptionSupported ||
!result.isThreadsPerBlockWithinLimit ||
!result.isBlocksWithinGridSizeLimit ||
!result.isSharedMemoryUsageWithinLimit ||
!result.isRegisterUsageWithinLimit ||
!result.isTotalThreadsWithinLimit ||
!result.isGlobalMemoryUsageWithinLimit ||
!result.isMemoryUsageWithinLimit ||
!result.isLocalMemoryUsageWithinLimit ||
!result.isConcurrentKernelsSupported ||
!result.isL2CacheSizeSufficient) {
problematicFunctions[name] = result;
}
}
return problematicFunctions;
}
@@ -0,0 +1,107 @@
#include <cuda.h>
#include <algorithm>
#include <filesystem>
#include <iostream>
#include <map>
#include <mutex>
#include <unordered_map>
#include <fstream>
#include <regex>
#include <string>
#include <vector>
class ValidationResult {
public:
bool isComputeCapabilitySufficient;
bool isECCMemorySupported;
bool isManagedMemorySupported;
bool isComputePreemptionSupported;
bool isThreadsPerBlockWithinLimit;
bool isBlocksWithinGridSizeLimit;
bool isSharedMemoryUsageWithinLimit;
bool isRegisterUsageWithinLimit;
bool isTotalThreadsWithinLimit;
bool isGlobalMemoryUsageWithinLimit;
bool isMemoryUsageWithinLimit;
bool isLocalMemoryUsageWithinLimit;
bool isConcurrentKernelsSupported;
bool isL2CacheSizeSufficient;
int sharedSizeBytes, numRegs, maxThreadsPerBlock;
size_t freeMemory, totalMemory;
int numBlocks;
int globalMemory;
int numThreads;
int memoryUsage;
bool isValid();
ValidationResult();
};
class DeviceValidator {
private:
cudaDeviceProp prop;
static std::mutex mtx;
std::unordered_map<std::string, CUmodule> moduleMap;
std::unordered_map<std::string, CUfunction> functionMap;
std::string directoryPath;
static DeviceValidator* instance;
void init();
public:
DeviceValidator(const std::string& directoryPath, int device = 0);
~DeviceValidator();
static DeviceValidator* getInstance(const std::string& directory, int device = 0);
// Set kernel attribute
void setKernelAttribute(const std::string& functionName, CUfunction_attribute attribute, int value);
void setKernelMaxDynamicSharedSizeBytes(const std::string& functionName, int value);
void setKernelPreferredSharedMemoryCarveout(const std::string& functionName, int value);
void setKernelMaxRegisters(const std::string& functionName, int value);
void setKernelMaxThreadsPerBlock(const std::string& functionName, int value);
void setKernelNumRegs(const std::string& functionName, int value);
void setKernelSharedSizeBytes(const std::string& functionName, int value);
void setKernelBinaryVersion(const std::string& functionName, int value);
void setKernelCacheModeCA(const std::string& functionName, int value);
void setKernelMaxThreadsPerBlockOptIn(const std::string& functionName, int value);
void setKernelReservedSharedSizeBytes(const std::string& functionName, int value);
void setAllKernelsAttribute(CUfunction_attribute attribute, int value);
void setAllKernelsMaxDynamicSharedSizeBytes(int value);
void setAllKernelsPreferredSharedMemoryCarveout(int value);
void setAllKernelsMaxRegisters(int value);
void setAllKernelsMaxThreadsPerBlock(int value);
void setAllKernelsNumRegs(int value);
void setAllKernelsSharedSizeBytes(int value);
void setAllKernelsBinaryVersion(int value);
void setAllKernelsCacheModeCA(int value);
void setAllKernelsMaxThreadsPerBlockOptIn(int value);
void setAllKernelsReservedSharedSizeBytes(int value);
void printKernelAttribute(const char* name, CUfunction_attribute attribute);
void printMaxKernelAttributes();
void printKernelAttributes(const char* name);
std::map<std::string, ValidationResult> collectResourceProblems();
void printValidationResult(const char* name, ValidationResult& result);
ValidationResult validateKernelLaunch(const char* name, dim3 threadsPerBlock, dim3 numBlocks,
size_t globalMemoryUsage);
void printProblematicFunctions(dim3 threadsPerBlock, dim3 numBlocks, size_t globalMemory);
std::vector<std::string> parseCUBINFile(const std::string& filePath);
std::vector<std::string> parsePTXFile(const std::string& filePath);
};
@@ -0,0 +1,160 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <exceptions/cuda_exception.h>
#include <execution/AffinityManager.h>
#include <execution/LaunchContext.h>
#include <helpers/cublasHelper.h>
#include <helpers/logger.h>
#include <thread>
thread_local sd::ContextBuffers contextBuffers = sd::ContextBuffers();
namespace sd {
// This avoids static destruction order crashes during JVM shutdown
std::vector<LaunchContext*>& LaunchContext::contexts() {
static std::vector<LaunchContext*>* _contexts = new std::vector<LaunchContext*>();
return *_contexts;
}
std::mutex LaunchContext::_mutex;
SD_MAP_IMPL<int, std::mutex*> LaunchContext::_deviceMutexes;
////////////////////////////////////////////////////////////////////////
LaunchContext::LaunchContext(cudaStream_t* cudaStream, cudaStream_t& specialCudaStream, void* reductionPointer,
void* scalarPointer, int* allocationPointer) {
_workspace = nullptr;
_isAllocated = false;
}
std::mutex* LaunchContext::deviceMutex() {
auto deviceId = AffinityManager::currentDeviceId();
return _deviceMutexes[deviceId];
}
LaunchContext::~LaunchContext() {
if (_isAllocated) {
}
}
////////////////////////////////////////////////////////////////////////
LaunchContext::LaunchContext() {
// default constructor, just to make clang/ranlib happy
_workspace = nullptr;
_deviceID = 0;
_isAllocated = true;
}
LaunchContext::LaunchContext(Pointer cudaStream, Pointer reductionPointer, Pointer scalarPointer,
Pointer allocationPointer) {
_isAllocated = false;
}
LaunchContext* LaunchContext::defaultContext() {
/**
* This method returns LaunchContext, that has multiple entities within:
* 1) temporary buffers. they must be per-thread
* 2) CUDA stream. it must be either per-thread or per-device
* 3) cuBLAS handle. it must be per-device
*/
auto deviceId = AffinityManager::currentDeviceId();
{
// we need this block synchronous, to avoid double initialization etc
std::lock_guard<std::mutex> lock(_mutex);
if (contexts().empty()) {
// create one context per device
auto numDevices = AffinityManager::numberOfDevices();
contexts().resize(numDevices);
for (int e = 0; e < numDevices; e++) {
_deviceMutexes[e] = new std::mutex();
AffinityManager::setCurrentNativeDevice(e);
contexts().at(e) = new LaunchContext();
}
// don't forget to restore device back again
AffinityManager::setCurrentNativeDevice(deviceId);
}
}
// return context for current device
return contexts().at(deviceId);
}
void* LaunchContext::getReductionPointer() const { return contextBuffers.reductionBuffer(); };
void* LaunchContext::getScalarPointer() const { return contextBuffers.scalarBuffer(); };
LongType* LaunchContext::getAllocationPointer() const { return reinterpret_cast<LongType*>(contextBuffers.allocationBuffer()); };
void* LaunchContext::getCublasHandle() const { return CublasHelper::getInstance().handle(); };
void* LaunchContext::getCusolverHandle() const { return CublasHelper::getInstance().solver(); };
cudaStream_t* LaunchContext::getCudaStream() const {
return reinterpret_cast<cudaStream_t*>(contextBuffers.execStream());
};
cudaStream_t* LaunchContext::getCudaSpecialStream() const {
return reinterpret_cast<cudaStream_t*>(contextBuffers.specialStream());
;
};
void LaunchContext::setReductionPointer(void* reductionPointer) {
contextBuffers.setReductionBuffer(reductionPointer);
};
void LaunchContext::setScalarPointer(void* scalarPointer) { contextBuffers.setScalarBuffer(scalarPointer); };
void LaunchContext::setAllocationPointer(int* allocationPointer) {
contextBuffers.setAllocationBuffer(allocationPointer);
};
void LaunchContext::setCudaStream(cudaStream_t* cudaStream){
};
void LaunchContext::setCudaSpecialStream(cudaStream_t* cudaStream){
};
void LaunchContext::setCublasHandle(void* handle) { _cublasHandle = handle; };
void LaunchContext::swapContextBuffers(ContextBuffers& buffers) { contextBuffers = buffers; };
void LaunchContext::releaseBuffers() {
contextBuffers.release();
}
bool LaunchContext::isInitialized() { return contextBuffers.isInitialized(); }
void* LaunchContext::getCuDnnHandle() const { return CublasHelper::getInstance().cudnn(); }
ErrorReference* LaunchContext::errorReference() { return contextBuffers.errorReference(); }
void* LaunchContext::engine() { return _engine; }
} // namespace sd
File diff suppressed because it is too large Load Diff
+867
View File
@@ -0,0 +1,867 @@
#if !defined(LAUNCH_DIMS_H)
#pragma once
#include <cstdlib>
#include <unordered_map>
#include <string>
#include <cuda_runtime.h>
#include <map>
#include <helpers/CudaLaunchHelper.h>
#include <array/NDArray.h>
// Retrieve the environment variable value for the given variable name
int getEnvVariable(const std::string& varName, int defaultValue);
// Grid, block, and shared memory size definitions using environment variables
#define GRID_SIZE_ADJUST_WEIGHTS getEnvVariable("GRID_SIZE_ADJUST_WEIGHTS", 256)
#define BLOCK_SIZE_ADJUST_WEIGHTS getEnvVariable("BLOCK_SIZE_ADJUST_WEIGHTS", 512)
#define SHARED_MEM_SIZE_ADJUST_WEIGHTS getEnvVariable("SHARED_MEM_SIZE_ADJUST_WEIGHTS", 8192)
//note we only use the shared memory for this op the rest are array specific
#define GRID_SIZE_SEQUENCE_MASK getEnvVariable("GRID_SIZE_SEQUENCE_MASK", 256)
#define BLOCK_SIZE_SEQUENCE_MASK getEnvVariable("BLOCK_SIZE_SEQUENCE_MASK", 256)
#define SHARED_MEM_SIZE_SEQUENCE_MASK getEnvVariable("SHARED_MEM_SIZE_SEQUENCE_MASK", 128)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_SEGMENT_SUM getEnvVariable("GRID_SIZE_SEGMENT_SUM", 256)
#define BLOCK_SIZE_SEGMENT_SUM getEnvVariable("BLOCK_SIZE_SEGMENT_SUM", 256)
#define SHARED_MEM_SIZE_SEGMENT_SUM getEnvVariable("SHARED_MEM_SIZE_SEGMENT_SUM", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_UNSORTED_SEGMENT_SUM getEnvVariable("GRID_SIZE_UNSORTED_SEGMENT_SUM", 256)
#define BLOCK_SIZE_UNSORTED_SEGMENT_SUM getEnvVariable("BLOCK_SIZE_UNSORTED_SEGMENT_SUM", 256)
#define SHARED_MEM_SIZE_UNSORTED_SEGMENT_SUM getEnvVariable("SHARED_MEM_SIZE_UNSORTED_SEGMENT_SUM", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_SEGMENT_SQRTN getEnvVariable("GRID_SIZE_SEGMENT_SQRTN", 128)
#define BLOCK_SIZE_SEGMENT_SQRTN getEnvVariable("BLOCK_SIZE_SEGMENT_SQRTN", 256)
#define SHARED_MEM_SIZE_SEGMENT_SQRTN getEnvVariable("SHARED_MEM_SIZE_SEGMENT_SQRTN", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_SEGMENT_PROD getEnvVariable("GRID_SIZE_SEGMENT_PROD", 256)
#define BLOCK_SIZE_SEGMENT_PROD getEnvVariable("BLOCK_SIZE_SEGMENT_PROD", 256)
#define SHARED_MEM_SIZE_SEGMENT_PROD getEnvVariable("SHARED_MEM_SIZE_SEGMENT_PROD", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_UNSORTED_SEGMENT_PROD getEnvVariable("GRID_SIZE_UNSORTED_SEGMENT_PROD", 256)
#define BLOCK_SIZE_UNSORTED_SEGMENT_PROD getEnvVariable("BLOCK_SIZE_UNSORTED_SEGMENT_PROD", 256)
#define SHARED_MEM_SIZE_UNSORTED_SEGMENT_PROD getEnvVariable("SHARED_MEM_SIZE_UNSORTED_SEGMENT_PROD", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_SEGMENT_MIN getEnvVariable("GRID_SIZE_SEGMENT_MIN", 256)
#define BLOCK_SIZE_SEGMENT_MIN getEnvVariable("BLOCK_SIZE_SEGMENT_MIN", 256)
#define SHARED_MEM_SIZE_SEGMENT_MIN getEnvVariable("SHARED_MEM_SIZE_SEGMENT_MIN", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_UNSORTED_SEGMENT_MIN getEnvVariable("GRID_SIZE_UNSORTED_SEGMENT_MIN", 256)
#define BLOCK_SIZE_UNSORTED_SEGMENT_MIN getEnvVariable("BLOCK_SIZE_UNSORTED_SEGMENT_MIN", 256)
#define SHARED_MEM_SIZE_UNSORTED_SEGMENT_MIN getEnvVariable("SHARED_MEM_SIZE_UNSORTED_SEGMENT_MIN", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_SEGMENT_MEAN getEnvVariable("GRID_SIZE_SEGMENT_MEAN", 256)
#define BLOCK_SIZE_SEGMENT_MEAN getEnvVariable("BLOCK_SIZE_SEGMENT_MEAN", 256)
#define SHARED_MEM_SIZE_SEGMENT_MEAN getEnvVariable("SHARED_MEM_SIZE_SEGMENT_MEAN", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_UNSORTED_SEGMENT_MEAN getEnvVariable("GRID_SIZE_UNSORTED_SEGMENT_MEAN", 256)
#define BLOCK_SIZE_UNSORTED_SEGMENT_MEAN getEnvVariable("BLOCK_SIZE_UNSORTED_SEGMENT_MEAN", 256)
#define SHARED_MEM_SIZE_UNSORTED_SEGMENT_MEAN getEnvVariable("SHARED_MEM_SIZE_UNSORTED_SEGMENT_MEAN", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_SEGMENT_MAX getEnvVariable("GRID_SIZE_SEGMENT_MAX", 256)
#define BLOCK_SIZE_SEGMENT_MAX getEnvVariable("BLOCK_SIZE_SEGMENT_MAX", 512)
#define SHARED_MEM_SIZE_SEGMENT_MAX getEnvVariable("SHARED_MEM_SIZE_SEGMENT_MAX", 256)
//not we don't really use this due to defaults being array specific for this op
#define GRID_SIZE_UNSORTED_SEGMENT_MAX getEnvVariable("GRID_SIZE_UNSORTED_SEGMENT_MAX", 256)
#define BLOCK_SIZE_UNSORTED_SEGMENT_MAX getEnvVariable("BLOCK_SIZE_UNSORTED_SEGMENT_MAX", 256)
#define SHARED_MEM_SIZE_UNSORTED_SEGMENT_MAX getEnvVariable("SHARED_MEM_SIZE_UNSORTED_SEGMENT_MAX", 256)
#define GRID_SIZE_MATRIX_DIAG getEnvVariable("GRID_SIZE_MATRIX_DIAG", 256)
#define BLOCK_SIZE_MATRIX_DIAG getEnvVariable("BLOCK_SIZE_MATRIX_DIAG", 512)
#define SHARED_MEM_SIZE_MATRIX_DIAG getEnvVariable("SHARED_MEM_SIZE_MATRIX_DIAG", 8192)
//not we don't really use this due to defaults being array specific for this op (see fillUpSegments_)
#define GRID_SIZE_SEGMENT_FILL_UP_SEGMENTS getEnvVariable("GRID_SIZE_SEGMENT_FILL_UP_SEGMENTS", 256)
#define BLOCK_SIZE_SEGMENT_FILL_UP_SEGMENTS getEnvVariable("BLOCK_SIZE_SEGMENT_FILL_UP_SEGMENTS", 256)
#define SHARED_MEM_SIZE_SEGMENT_FILL_UP_SEGMENTS getEnvVariable("SHARED_MEM_SIZE_SEGMENT_FILL_UP_SEGMENTS", 256)
#define GRID_SIZE_MATRIX_BAND getEnvVariable("GRID_SIZE_MATRIX_BAND", 256)
#define BLOCK_SIZE_MATRIX_BAND getEnvVariable("BLOCK_SIZE_MATRIX_BAND", 512)
#define SHARED_MEM_SIZE_MATRIX_BAND getEnvVariable("SHARED_MEM_SIZE_MATRIX_BAND", 8192)
#define GRID_SIZE_LUP getEnvVariable("GRID_SIZE_LUP", 256)
#define BLOCK_SIZE_LUP getEnvVariable("BLOCK_SIZE_LUP", 256)
#define SHARED_MEM_SIZE_LUP getEnvVariable("SHARED_MEM_SIZE_LUP", 256)
#define GRID_SIZE_ISMAX getEnvVariable("GRID_SIZE_ISMAX", 256)
#define BLOCK_SIZE_ISMAX getEnvVariable("BLOCK_SIZE_ISMAX", 256)
#define SHARED_MEM_SIZE_ISMAX getEnvVariable("SHARED_MEM_SIZE_ISMAX", 16384)
#define GRID_SIZE_ISMAX_FILL getEnvVariable("GRID_SIZE_ISMAXGRID_SIZE_ISMAX_FILL", 128)
#define BLOCK_SIZE_ISMAX_FILL getEnvVariable("BLOCK_SIZE_ISMAXBLOCK_SIZE_ISMAX_FILL", 512)
#define SHARED_MEM_SIZE_ISMAX_FILL getEnvVariable("SHARED_MEM_SIZE_ISMAXSHARED_MEM_SIZE_ISMAX_FILL", 1024)
#define GRID_SIZE_IMAGE_RESIZE getEnvVariable("GRID_SIZE_IMAGE_RESIZE", 256)
#define BLOCK_SIZE_IMAGE_RESIZE getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE", 256)
#define SHARED_MEM_SIZE_IMAGE_RESIZE getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE", 256)
#define GRID_SIZE_DIAG getEnvVariable("GRID_SIZE_DIAG", 256)
#define BLOCK_SIZE_DIAG getEnvVariable("BLOCK_SIZE_DIAG", 256)
#define SHARED_MEM_SIZE_DIAG getEnvVariable("SHARED_MEM_SIZE_DIAG", 256)
#define GRID_SIZE_CONFUSION_MATRIX getEnvVariable("GRID_SIZE_CONFUSION_MATRIX", 32)
#define BLOCK_SIZE_CONFUSION_MATRIX getEnvVariable("BLOCK_SIZE_CONFUSION_MATRIX", 32)
#define SHARED_MEM_SIZE_CONFUSION_MATRIX getEnvVariable("SHARED_MEM_SIZE_CONFUSION_MATRIX", 1024)
#define GRID_SIZE_TILE getEnvVariable("GRID_SIZE_TILE", 256)
#define BLOCK_SIZE_TILE getEnvVariable("BLOCK_SIZE_TILE", 512)
#define SHARED_MEM_SIZE_TILE getEnvVariable("SHARED_MEM_SIZE_TILE", 8192)
#define GRID_SIZE_DIAGONAL getEnvVariable("GRID_SIZE_DIAGONAL", 256)
#define BLOCK_SIZE_DIAGONAL getEnvVariable("BLOCK_SIZE_DIAGONAL", 512)
#define SHARED_MEM_SIZE_DIAGONAL getEnvVariable("SHARED_MEM_SIZE_DIAGONAL", 8192)
#define GRID_SIZE_TEAR getEnvVariable("GRID_SIZE_TEAR", 512)
#define BLOCK_SIZE_TEAR getEnvVariable("BLOCK_SIZE_TEAR", 512)
#define SHARED_MEM_SIZE_TEAR getEnvVariable("SHARED_MEM_SIZE_TEAR", 512)
#define GRID_SIZE_SORT_TENSOR_BY_DIM_KEY getEnvVariable("GRID_SIZE_SORT_TENSOR_BY_DIM_KEY", 256)
#define BLOCK_SIZE_SORT_TENSOR_BY_DIM_KEY getEnvVariable("BLOCK_SIZE_SORT_TENSOR_BY_DIM_KEY", 256)
#define SHARED_MEM_SIZE_SORT_TENSOR_BY_DIM_KEY getEnvVariable("SHARED_MEM_SIZE_SORT_TENSOR_BY_DIM_KEY", 256)
#define GRID_SIZE_SORT_TENSOR_ALONG_DIM_KEY getEnvVariable("GRID_SIZE_SORT_TENSOR_ALONG_DIM_KEY", 256)
#define BLOCK_SIZE_SORT_TENSOR_ALONG_DIM_KEY getEnvVariable("BLOCK_SIZE_SORT_TENSOR_ALONG_DIM_KEY", 256)
#define SHARED_MEM_SIZE_SORT_TENSOR_ALONG_DIM_KEY getEnvVariable("SHARED_MEM_SIZE_SORT_TENSOR_ALONG_DIM_KEY", 256)
#define GRID_SIZE_SORT_TENSOR_ALONG_DIM_VALUE getEnvVariable("GRID_SIZE_SORT_TENSOR_ALONG_DIM_VALUE", 256)
#define BLOCK_SIZE_SORT_TENSOR_ALONG_DIM_VALUE getEnvVariable("BLOCK_SIZE_SORT_TENSOR_ALONG_DIM_VALUE", 256)
#define SHARED_MEM_SIZE_SORT_TENSOR_ALONG_DIM_VALUE getEnvVariable("SHARED_MEM_SIZE_SORT_TENSOR_ALONG_DIM_VALUE", 256)
#define GRID_SIZE_SHUFFLE getEnvVariable("GRID_SIZE_SHUFFLE", 256)
#define BLOCK_SIZE_SHUFFLE getEnvVariable("BLOCK_SIZE_SHUFFLE", 512)
#define SHARED_MEM_SIZE_SHUFFLE getEnvVariable("SHARED_MEM_SIZE_SHUFFLE", 8192)
#define GRID_SIZE_PULLROWS getEnvVariable("GRID_SIZE_PULLROWS", 64)
#define BLOCK_SIZE_PULLROWS getEnvVariable("BLOCK_SIZE_PULLROWS", 256)
#define SHARED_MEM_SIZE_PULLROWS getEnvVariable("SHARED_MEM_SIZE_PULLROWS", 1024)
#define GRID_SIZE_PRESCAN_ARRAY_RECURSIVE getEnvVariable("GRID_SIZE_PRESCAN_ARRAY_RECURSIVE", 256)
#define BLOCK_SIZE_PRESCAN_ARRAY_RECURSIVE getEnvVariable("BLOCK_SIZE_PRESCAN_ARRAY_RECURSIVE", 256)
#define SHARED_MEM_SIZE_PRESCAN_ARRAY_RECURSIVE getEnvVariable("SHARED_MEM_SIZE_PRESCAN_ARRAY_RECURSIVE", 256)
#define GRID_SIZE_SCALAR_SCAN getEnvVariable("GRID_SIZE_SCALAR_SCAN", 256)
#define BLOCK_SIZE_SCALAR_SCAN getEnvVariable("BLOCK_SIZE_SCALAR_SCAN", 512)
#define SHARED_MEM_SIZE_SCALAR_SCAN getEnvVariable("SHARED_MEM_SIZE_SCALAR_SCAN", 8192)
#define GRID_SIZE_SCALAR_TAD getEnvVariable("GRID_SIZE_SCALAR_TAD", 256)
#define BLOCK_SIZE_SCALAR_TAD getEnvVariable("BLOCK_SIZE_SCALAR_TAD", 256)
#define SHARED_MEM_SIZE_SCALAR_TAD getEnvVariable("SHARED_MEM_SIZE_SCALAR_TAD", 16384)
#define GRID_SIZE_REDUCE_LONG getEnvVariable("GRID_SIZE_REDUCE_LONG", 256)
#define BLOCK_SIZE_REDUCE_LONG getEnvVariable("BLOCK_SIZE_REDUCE_LONG", 256)
#define SHARED_MEM_SIZE_REDUCE_LONG getEnvVariable("SHARED_MEM_SIZE_REDUCE_LONG", 1024)
#define GRID_SIZE_REDUCE_BOOL getEnvVariable("GRID_SIZE_REDUCE_BOOL", 256)
#define BLOCK_SIZE_REDUCE_BOOL getEnvVariable("BLOCK_SIZE_REDUCE_BOOL", 256)
#define SHARED_MEM_SIZE_REDUCE_BOOL getEnvVariable("SHARED_MEM_SIZE_REDUCE_BOOL", 1024)
#define GRID_SIZE_AVERAGE getEnvVariable("GRID_SIZE_AVERAGE", 256)
#define BLOCK_SIZE_AVERAGE getEnvVariable("BLOCK_SIZE_AVERAGE", 256)
#define SHARED_MEM_SIZE_AVERAGE getEnvVariable("SHARED_MEM_SIZE_AVERAGE", 4096)
#define GRID_SIZE_ACCUMULATE getEnvVariable("GRID_SIZE_ACCUMULATE", 256)
#define BLOCK_SIZE_ACCUMULATE getEnvVariable("BLOCK_SIZE_ACCUMULATE", 256)
#define SHARED_MEM_SIZE_ACCUMULATE getEnvVariable("SHARED_MEM_SIZE_ACCUMULATE", 8192)
#define GRID_SIZE_TRANSFORM_SCAN getEnvVariable("GRID_SIZE_TRANSFORM_SCAN", 256)
#define BLOCK_SIZE_TRANSFORM_SCAN getEnvVariable("BLOCK_SIZE_TRANSFORM_SCAN", 256)
#define SHARED_MEM_SIZE_TRANSFORM_SCAN getEnvVariable("SHARED_MEM_SIZE_TRANSFORM_SCAN", 1024)
#define GRID_SIZE_SUMMARY_STATS getEnvVariable("GRID_SIZE_SUMMARY_STATS", 256)
#define BLOCK_SIZE_SUMMARY_STATS getEnvVariable("BLOCK_SIZE_SUMMARY_STATS", SD_CUDA_BLOCK_SIZE)
#define SHARED_MEM_SIZE_SUMMARY_STATS getEnvVariable("SHARED_MEM_SIZE_SUMMARY_STATS", 1024)
#define GRID_SIZE_REDUCE_FLOAT getEnvVariable("GRID_SIZE_REDUCE_FLOAT", 256)
#define BLOCK_SIZE_REDUCE_FLOAT getEnvVariable("BLOCK_SIZE_REDUCE_FLOAT", 256)
#define SHARED_MEM_SIZE_REDUCE_FLOAT getEnvVariable("SHARED_MEM_SIZE_REDUCE_FLOAT", 256)
#define GRID_SIZE_SCALAR_BOOL getEnvVariable("GRID_SIZE_SCALAR_BOOL", 256)
#define BLOCK_SIZE_SCALAR_BOOL getEnvVariable("BLOCK_SIZE_SCALAR_BOOL", 256)
#define SHARED_MEM_SIZE_SCALAR_BOOL getEnvVariable("SHARED_MEM_SIZE_SCALAR_BOOL", 256)
#define GRID_SIZE_SCALAR_SAME getEnvVariable("GRID_SIZE_SCALAR_SAME", 256)
#define BLOCK_SIZE_SCALAR_SAME getEnvVariable("BLOCK_SIZE_SCALAR_SAME", 256)
#define SHARED_MEM_SIZE_SCALAR_SAME getEnvVariable("SHARED_MEM_SIZE_SCALAR_SAME", 256)
#define GRID_SIZE_SCALAR_LONG getEnvVariable("GRID_SIZE_SCALAR_LONG", 256)
#define BLOCK_SIZE_SCALAR_LONG getEnvVariable("BLOCK_SIZE_SCALAR_LONG", 256)
#define SHARED_MEM_SIZE_SCALAR_LONG getEnvVariable("SHARED_MEM_SIZE_SCALAR_LONG", 256)
#define GRID_SIZE_REDUCE_3 getEnvVariable("GRID_SIZE_REDUCE_3", 256)
#define BLOCK_SIZE_REDUCE_3 getEnvVariable("BLOCK_SIZE_REDUCE_3", 256)
#define SHARED_MEM_SIZE_REDUCE_3 getEnvVariable("SHARED_MEM_SIZE_REDUCE_3", 256)
#define GRID_SIZE_PAIRWISE_TRANSFORMS getEnvVariable("GRID_SIZE_PAIRWISE_TRANSFORMS", 256)
#define BLOCK_SIZE_PAIRWISE_TRANSFORMS getEnvVariable("BLOCK_SIZE_PAIRWISE_TRANSFORMS", 256)
#define SHARED_MEM_SIZE_PAIRWISE_TRANSFORMS getEnvVariable("SHARED_MEM_SIZE_PAIRWISE_TRANSFORMS", 256)
#define GRID_SIZE_BROADCAST getEnvVariable("GRID_SIZE_BROADCAST", 256)
#define BLOCK_SIZE_BROADCAST getEnvVariable("BLOCK_SIZE_BROADCAST", 256)
#define SHARED_MEM_SIZE_BROADCAST getEnvVariable("SHARED_MEM_SIZE_BROADCAST", 1024)
#define GRID_SIZE_BROADCAST_INT getEnvVariable("GRID_SIZE_BROADCAST_INT", 256)
#define BLOCK_SIZE_BROADCAST_INT getEnvVariable("BLOCK_SIZE_BROADCAST_INT", 256)
#define SHARED_MEM_SIZE_BROADCAST_INT getEnvVariable("SHARED_MEM_SIZE_BROADCAST_INT", 1024)
#define GRID_SIZE_BROADCAST_BOOL getEnvVariable("GRID_SIZE_BROADCAST_BOOL", 256)
#define BLOCK_SIZE_BROADCAST_BOOL getEnvVariable("BLOCK_SIZE_BROADCAST_BOOL", 256)
#define SHARED_MEM_SIZE_BROADCAST_BOOL getEnvVariable("SHARED_MEM_SIZE_BROADCAST_BOOL", 1024)
#define GRID_SIZE_MATRIX_MULTIPLY getEnvVariable("GRID_SIZE_MATRIX_MULTIPLY", 256)
#define BLOCK_SIZE_MATRIX_MULTIPLY getEnvVariable("BLOCK_SIZE_MATRIX_MULTIPLY", 256)
#define SHARED_MEM_SIZE_MATRIX_MULTIPLY getEnvVariable("SHARED_MEM_SIZE_MATRIX_MULTIPLY", 256)
#define GRID_SIZE_LOG_ABS_DETERMINANT getEnvVariable("GRID_SIZE_LOG_ABS_DETERMINANT", 256)
#define BLOCK_SIZE_LOG_ABS_DETERMINANT getEnvVariable("BLOCK_SIZE_LOG_ABS_DETERMINANT", 256)
#define SHARED_MEM_SIZE_LOG_ABS_DETERMINANT getEnvVariable("SHARED_MEM_SIZE_LOG_ABS_DETERMINANT", 1024)
#define GRID_SIZE_DIAG_PART getEnvVariable("GRID_SIZE_DIAG_PART ", 256)
#define BLOCK_SIZE_DIAG_PART getEnvVariable("BLOCK_SIZE_DIAG_PART ", 512)
#define SHARED_MEM_SIZE_DIAG_PART getEnvVariable("SHARED_MEM_SIZE_DIAG_PART ", 8192)
#define GRID_SIZE_RANDOM getEnvVariable("GRID_SIZE_RANDOM ", 512)
#define BLOCK_SIZE_RANDOM getEnvVariable("BLOCK_SIZE_RANDOM ", 512)
#define SHARED_MEM_SIZE_RANDOM getEnvVariable("SHARED_MEM_SIZE_RANDOM ", 32768)
#define GRID_SIZE_LAMBDA getEnvVariable("GRID_SIZE_LAMBDA", 256)
#define BLOCK_SIZE_LAMBDA getEnvVariable("BLOCK_SIZE_LAMBDA", 512)
#define SHARED_MEM_SIZE_LAMBDA getEnvVariable("SHARED_MEM_SIZE_LAMBDA", 8192)
#define GRID_SIZE_IM2COL getEnvVariable("GRID_SIZE_IM2COL", 256)
#define BLOCK_SIZE_IM2COL getEnvVariable("BLOCK_SIZE_IM2COL", 512)
#define SHARED_MEM_SIZE_IM2COL getEnvVariable("SHARED_MEM_SIZE_IM2COL", 8192)
#define GRID_SIZE_COL2IM getEnvVariable("GRID_SIZE_COL2IM", 256)
#define BLOCK_SIZE_COL2IM getEnvVariable("BLOCK_SIZE_COL2IM", 512)
#define SHARED_MEM_SIZE_COL2IM getEnvVariable("SHARED_MEM_SIZE_COL2IM", 8192)
#define GRID_SIZE_GEMV getEnvVariable("GRID_SIZE_GEMV", 256)
#define BLOCK_SIZE_GEMV getEnvVariable("BLOCK_SIZE_GEMV", 512)
#define SHARED_MEM_SIZE_GEMV getEnvVariable("SHARED_MEM_SIZE_GEMV", 8192)
#define GRID_SIZE_ADDBIAS getEnvVariable("GRID_SIZE_ADDBIAS", 256)
#define BLOCK_SIZE_ADDBIAS getEnvVariable("BLOCK_SIZE_ADDBIAS", 512)
#define SHARED_MEM_SIZE_ADDBIAS getEnvVariable("SHARED_MEM_SIZE_ADDBIAS", 8192)
#define GRID_SIZE_POOLING getEnvVariable("GRID_SIZE_POOLING", 256)
#define BLOCK_SIZE_POOLING getEnvVariable("BLOCK_SIZE_POOLING", 512)
#define SHARED_MEM_SIZE_POOLING getEnvVariable("SHARED_MEM_SIZE_POOLING", 8192)
#define GRID_SIZE_COL2VOL getEnvVariable("GRID_SIZE_COL2VOL", 256)
#define BLOCK_SIZE_COL2VOL getEnvVariable("BLOCK_SIZE_COL2VOL", 512)
#define SHARED_MEM_SIZE_COL2VOL getEnvVariable("SHARED_MEM_SIZE_COL2VOL", 8192)
#define GRID_SIZE_VOL2COL getEnvVariable("GRID_SIZE_VOL2COL", 256)
#define BLOCK_SIZE_VOL2COL getEnvVariable("BLOCK_SIZE_VOL2COL", 512)
#define SHARED_MEM_SIZE_VOL2COL getEnvVariable("SHARED_MEM_SIZE_VOL2COL", 8192)
#define GRID_SIZE_UPSAMPLING getEnvVariable("GRID_SIZE_UPSAMPLING", 256)
#define BLOCK_SIZE_UPSAMPLING getEnvVariable("BLOCK_SIZE_UPSAMPLING", 512)
#define SHARED_MEM_SIZE_UPSAMPLING getEnvVariable("SHARED_MEM_SIZE_UPSAMPLING", 8192)
#define GRID_SIZE_UPSAMPLING getEnvVariable("GRID_SIZE_PRELU", 256)
#define BLOCK_SIZE_UPSAMPLING getEnvVariable("BLOCK_SIZE_PRELU", 512)
#define SHARED_MEM_SIZE_UPSAMPLING getEnvVariable("SHARED_MEM_SIZE_PRELU", 512)
#define GRID_SIZE_UPSAMPLING getEnvVariable("GRID_SIZE_ADJUST", 256)
#define BLOCK_SIZE_UPSAMPLING getEnvVariable("BLOCK_SIZE_ADJUST", 512)
#define SHARED_MEM_SIZE_UPSAMPLING getEnvVariable("SHARED_MEM_SIZE_ADJUST", 512)
#define GRID_SIZE_BATCHNORM getEnvVariable("GRID_SIZE_BATCHNORM", 256)
#define BLOCK_SIZE_BATCHNORM getEnvVariable("BLOCK_SIZE_BATCHNORM", 512)
#define SHARED_MEM_SIZE_BATCHNORM getEnvVariable("SHARED_MEM_SIZE_BATCHNORM", 512)
#define GRID_SIZE_COMPARE_AND_BITPACK getEnvVariable("GRID_SIZE_COMPARE_AND_BITPACK", 256)
#define BLOCK_SIZE_COMPARE_AND_BITPACK getEnvVariable("BLOCK_SIZE_COMPARE_AND_BITPACK", 512)
#define SHARED_MEM_SIZE_COMPARE_AND_BITPACK getEnvVariable("SHARED_MEM_SIZE_COMPARE_AND_BITPACK", 512)
#define GRID_SIZE_CONFUSION_MATRIX getEnvVariable("GRID_SIZE_CONFUSION_MATRIX", 256)
#define BLOCK_SIZE_CONFUSION_MATRIX getEnvVariable("BLOCK_SIZE_CONFUSION_MATRIX", 512)
#define SHARED_MEM_SIZE_CONFUSION_MATRIX getEnvVariable("SHARED_MEM_SIZE_CONFUSION_MATRIX", 1024)
#define GRID_SIZE_CLIP getEnvVariable("GRID_SIZE_CLIP", 256)
#define BLOCK_SIZE_CLIP getEnvVariable("BLOCK_SIZE_CLIP", 512)
#define SHARED_MEM_SIZE_CLIP getEnvVariable("SHARED_MEM_SIZE_CLIP", 8192)
#define GRID_SIZE_CROSS getEnvVariable("GRID_SIZE_CROSS", 256)
#define BLOCK_SIZE_CROSS getEnvVariable("BLOCK_SIZE_CROSS", 512)
#define SHARED_MEM_SIZE_CROSS getEnvVariable("SHARED_MEM_SIZE_CROSS", 8192)
#define GRID_SIZE_BETA_INC getEnvVariable("GRID_SIZE_BETA_INC", 256)
#define BLOCK_SIZE_BETA_INC getEnvVariable("BLOCK_SIZE_BETA_INC", 512)
#define SHARED_MEM_SIZE_BETA_INC getEnvVariable("SHARED_MEM_SIZE_BETA_INC", 8192)
#define GRID_SIZE_ADJUST getEnvVariable("GRID_SIZE_ADJUST", 256)
#define BLOCK_SIZE_ADJUST getEnvVariable("BLOCK_SIZE_ADJUST", 512)
#define SHARED_MEM_SIZE_ADJUST getEnvVariable("SHARED_MEM_SIZE_ADJUST", 8192)
#define GRID_SIZE_CONCAT getEnvVariable("GRID_SIZE_CONCAT", 256)
#define BLOCK_SIZE_CONCAT getEnvVariable("BLOCK_SIZE_CONCAT", 512)
#define SHARED_MEM_SIZE_CONCAT getEnvVariable("SHARED_MEM_SIZE_CONCAT", 8192)
#define GRID_SIZE_DEPTH_TO_SPACE getEnvVariable("GRID_SIZE_DEPTH_TO_SPACE", 512)
#define BLOCK_SIZE_DEPTH_TO_SPACE getEnvVariable("BLOCK_SIZE_DEPTH_TO_SPACE", 512)
#define SHARED_MEM_SIZE_DEPTH_TO_SPACE getEnvVariable("SHARED_MEM_SIZE_DEPTH_TO_SPACE", 1024)
#define GRID_SIZE_PRELU getEnvVariable("GRID_SIZE_PRELU", 512)
#define BLOCK_SIZE_PRELU getEnvVariable("BLOCK_SIZE_PRELU", 512)
#define SHARED_MEM_SIZE_PRELU getEnvVariable("SHARED_MEM_SIZE_PRELU", 1024)
#define GRID_SIZE_HISTOGRAM getEnvVariable("GRID_SIZE_HISTOGRAM", 512)
#define BLOCK_SIZE_HISTOGRAM getEnvVariable("BLOCK_SIZE_HISTOGRAM", 512)
#define SHARED_MEM_SIZE_HISTOGRAM getEnvVariable("SHARED_MEM_SIZE_HISTOGRAM", 1024)
#define GRID_SIZE_DILATION getEnvVariable("GRID_SIZE_DILATION", 512)
#define BLOCK_SIZE_DILATION getEnvVariable("BLOCK_SIZE_DILATION", 512)
#define SHARED_MEM_SIZE_DILATION getEnvVariable("SHARED_MEM_SIZE_DILATION", 1024)
#define GRID_SIZE_DROPOUT getEnvVariable("GRID_SIZE_DROPOUT", 256)
#define BLOCK_SIZE_DROPOUT getEnvVariable("BLOCK_SIZE_DROPOUT", 128)
#define SHARED_MEM_SIZE_DROPOUT getEnvVariable("SHARED_MEM_SIZE_DROPOUT", 1024)
#define GRID_SIZE_EXTRACT_PATCHES getEnvVariable("GRID_SIZE_EXTRACT_PATCHES", 128)
#define BLOCK_SIZE_EXTRACT_PATCHES getEnvVariable("BLOCK_SIZE_EXTRACT_PATCHES", 128)
#define SHARED_MEM_SIZE_EXTRACT_PATCHES getEnvVariable("SHARED_MEM_SIZE_EXTRACT_PATCHES", 1024)
#define GRID_SIZE_FAKE_QUANTIZATION getEnvVariable("GRID_SIZE_FAKE_QUANTIZATION", 128)
#define BLOCK_SIZE_FAKE_QUANTIZATION getEnvVariable("BLOCK_SIZE_FAKE_QUANTIZATION", 256)
#define SHARED_MEM_SIZE_FAKE_QUANTIZATION getEnvVariable("SHARED_MEM_SIZE_FAKE_QUANTIZATION", 256)
#define GRID_SIZE_FLATTEN getEnvVariable("GRID_SIZE_FLATTEN", 256)
#define BLOCK_SIZE_FLATTEN getEnvVariable("BLOCK_SIZE_FLATTEN", 512)
#define SHARED_MEM_SIZE_FLATTEN getEnvVariable("SHARED_MEM_SIZE_FLATTEN", 8192)
#define GRID_SIZE_GATHER_LINEAR getEnvVariable("GRID_SIZE_GATHER_LINEAR", 128)
#define BLOCK_SIZE_GATHER_LINEAR getEnvVariable("BLOCK_SIZE_GATHER_LINEAR", 256)
#define SHARED_MEM_SIZE_GATHER_LINEAR getEnvVariable("SHARED_MEM_SIZE_GATHER_LINEAR", 1024)
#define GRID_SIZE_GATHER getEnvVariable("GRID_SIZE_GATHER", 128)
#define BLOCK_SIZE_GATHER getEnvVariable("BLOCK_SIZE_GATHER", 256)
#define SHARED_MEM_SIZE_GATHER getEnvVariable("SHARED_MEM_SIZE_GATHER", 1024)
#define GRID_SIZE_GATHERND getEnvVariable("GRID_SIZE_GATHERND", 128)
#define BLOCK_SIZE_GATHERND getEnvVariable("BLOCK_SIZE_GATHERND", 256)
#define SHARED_MEM_SIZE_GATHERND getEnvVariable("SHARED_MEM_SIZE_GATHERND", 1024)
#define GRID_SIZE_HAMMING getEnvVariable("GRID_SIZE_HAMMING", 128)
#define BLOCK_SIZE_HAMMING getEnvVariable("BLOCK_SIZE_HAMMING",SD_CUDA_BLOCK_SIZE)
#define SHARED_MEM_SIZE_HAMMING getEnvVariable("SHARED_MEM_SIZE_HAMMING", 1024)
#define GRID_SIZE_HASHCODE_SPLIT getEnvVariable("GRID_SIZE_HASHCODE_SPLIT", 128)
#define BLOCK_SIZE_HASHCODE_SPLIT getEnvVariable("BLOCK_SIZE_HASHCODE_SPLIT",SD_CUDA_BLOCK_SIZE)
#define SHARED_MEM_SIZE_HASHCODE_SPLIT getEnvVariable("SHARED_MEM_SIZE_HASHCODE_SPLIT", 1024)
#define GRID_SIZE_HASHCODE_INTERNAL getEnvVariable("GRID_SIZE_HASHCODE_INTERNAL", 128)
#define BLOCK_SIZE_HASHCODE_INTERNAL getEnvVariable("BLOCK_SIZE_HASHCODE_INTERNAL",SD_CUDA_BLOCK_SIZE)
#define SHARED_MEM_SIZE_HASHCODE_INTERNAL getEnvVariable("SHARED_MEM_SIZE_HASHCODE_INTERNAL", 1024)
#define GRID_SIZE_HASHCODE_LAST getEnvVariable("GRID_SIZE_HASHCODE_LAST", 1)
#define BLOCK_SIZE_HASHCODE_LAST getEnvVariable("BLOCK_SIZE_HASHCODE_LAST",1)
#define SHARED_MEM_SIZE_HASHCODE_LAST getEnvVariable("SHARED_MEM_SIZE_HASHCODE_LAST", 128)
#define GRID_SIZE_HISTOGRAM_FIXED_WIDTH getEnvVariable("GRID_SIZE_HISTOGRAM_FIXED_WIDTH", 256)
#define BLOCK_SIZE_HISTOGRAM_FIXED_WIDTH getEnvVariable("BLOCK_SIZE_HISTOGRAM_FIXED_WIDTH",256)
#define SHARED_MEM_SIZE_HISTOGRAM_FIXED_WIDTH getEnvVariable("SHARED_MEM_SIZE_HISTOGRAM_FIXED_WIDTH", 1024)
#define GRID_SIZE_DRAW_BOUNDING_BOXES getEnvVariable("GRID_SIZE_DRAW_BOUNDING_BOXES", 128)
#define BLOCK_SIZE_DRAW_BOUNDING_BOXES getEnvVariable("BLOCK_SIZE_DRAW_BOUNDING_BOXES",128)
#define SHARED_MEM_SIZE_DRAW_BOUNDING_BOXES getEnvVariable("SHARED_MEM_SIZE_DRAW_BOUNDING_BOXES", 1024)
#define GRID_SIZE_IMAGE_RESIZE getEnvVariable("GRID_SIZE_IMAGE_RESIZE", 256)
#define BLOCK_SIZE_IMAGE_RESIZE getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE",256)
#define SHARED_MEM_SIZE_IMAGE_RESIZE getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE", 256)
#define GRID_SIZE_IMAGE_RESIZE_INTERP_WEIGHTS getEnvVariable("GRID_SIZE_IMAGE_RESIZE_INTERP_WEIGHTS", 256)
#define BLOCK_SIZE_IMAGE_RESIZE_INTERP_WEIGHTS getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_INTERP_WEIGHTS",512)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_INTERP_WEIGHTS getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_INTERP_WEIGHTS", 512)
#define GRID_SIZE_IMAGE_RESIZE_INIT_COEFFS getEnvVariable("GRID_SIZE_IMAGE_RESIZE_INIT_COEFFS", 128)
#define BLOCK_SIZE_IMAGE_RESIZE_INIT_COEFFS getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_INIT_COEFFS",128)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_INIT_COEFFS getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_INIT_COEFFS", 128)
#define GRID_SIZE_IMAGE_RESIZE_COEFFS_ACCUM getEnvVariable("GRID_SIZE_IMAGE_RESIZE_COEFFS_ACCUM", 128)
#define BLOCK_SIZE_IMAGE_RESIZE_COEFFS_ACCUM getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_COEFFS_ACCUM",128)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_COEFFS_ACCUM getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_COEFFS_ACCUM", 512)
#define GRID_SIZE_IMAGE_RESIZE_BICUBIC getEnvVariable("GRID_SIZE_IMAGE_RESIZE_BICUBIC", 128)
#define BLOCK_SIZE_IMAGE_RESIZE_BICUBIC getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_BICUBIC",1)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_BICUBIC getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_BICUBIC", 512)
#define GRID_SIZE_IMAGE_RESIZE_FILL_INTERP getEnvVariable("GRID_SIZE_IMAGE_RESIZE_FILL_INTERP", 128)
#define BLOCK_SIZE_IMAGE_RESIZE_FILL_INTERP getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_FILL_INTERP",128)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_FILL_INTERP getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_FILL_INTERP", 256)
#define GRID_SIZE_IMAGE_RESIZE_CROP_AND_RESIZE getEnvVariable("GRID_SIZE_IMAGE_RESIZE_CROP_AND_RESIZE", 128)
#define BLOCK_SIZE_IMAGE_RESIZE_CROP_AND_RESIZE getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_CROP_AND_RESIZE",128)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_CROP_AND_RESIZE getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_CROP_AND_RESIZE", 256)
#define GRID_SIZE_IMAGE_RESIZE_V2_GATHER getEnvVariable("GRID_SIZE_IMAGE_RESIZE_V2_GATHER", 128)
#define BLOCK_SIZE_IMAGE_RESIZE_V2_GATHER getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_V2_GATHER",128)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_V2_GATHER getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_V2_GATHER", 256)
#define GRID_SIZE_IMAGE_SUPPRESS_SCORES getEnvVariable("GRID_SIZE_IMAGE_SUPPRESS_SCORES", 128)
#define BLOCK_SIZE_IMAGE_SUPPRESS_SCORES getEnvVariable("BLOCK_SIZE_IMAGE_SUPPRESS_SCORES",128)
#define SHARED_MEM_SIZE_IMAGE_SUPPRESS_SCORES getEnvVariable("SHARED_MEM_SIZE_IMAGE_SUPPRESS_SCORES", 128)
#define GRID_SIZE_IMAGE_SUPPRESS_SELECT getEnvVariable("GRID_SIZE_IMAGE_SUPPRESS_SELECT", 128)
#define BLOCK_SIZE_IMAGE_SUPPRESS_SELECT getEnvVariable("BLOCK_SIZE_IMAGE_SUPPRESS_SELECT",256)
#define SHARED_MEM_SIZE_IMAGE_SUPPRESS_SELECT getEnvVariable("SHARED_MEM_SIZE_IMAGE_SUPPRESS_SELECT", 1024)
#define GRID_SIZE_IMAGE_SUPPRESS_NONMAX_OVERLAP getEnvVariable("GRID_SIZE_IMAGE_SUPPRESS_NONMAX_OVERLAP", 1)
#define BLOCK_SIZE_IMAGE_SUPPRESS_NONMAX_OVERLAP getEnvVariable("BLOCK_SIZE_IMAGE_SUPPRESS_NONMAX_OVERLAP",1)
#define SHARED_MEM_SIZE_IMAGE_SUPPRESS_NONMAX_OVERLAP getEnvVariable("SHARED_MEM_SIZE_IMAGE_SUPPRESS_NONMAX_OVERLAP", 1024)
#define GRID_SIZE_IMAGE_HELPERS_TRIPLE getEnvVariable("GRID_SIZE_IMAGE_HELPERS_TRIPLE", 256)
#define BLOCK_SIZE_IMAGE_HELPERS_TRIPLE getEnvVariable("BLOCK_SIZE_IMAGE_HELPERS_TRIPLE",256)
#define SHARED_MEM_SIZE_IMAGE_HELPERS_TRIPLE getEnvVariable("SHARED_MEM_SIZE_IMAGE_HELPERS_TRIPLE", 8192)
#define GRID_SIZE_IMAGE_HELPERS getEnvVariable("GRID_SIZE_IMAGE_HELPERS", 256)
#define BLOCK_SIZE_IMAGE_HELPERS getEnvVariable("BLOCK_SIZE_IMAGE_HELPERS",256)
#define SHARED_MEM_SIZE_GRID_SIZE_IMAGE_HELPERS getEnvVariable("SHARED_MEM_SIZE_GRID_SIZE_IMAGE_HELPERS", 8192)
#define GRID_SIZE_LRN getEnvVariable("GRID_SIZE_LRN", 256)
#define BLOCK_SIZE_LRN getEnvVariable("BLOCK_SIZE_LRN",256)
#define SHARED_MEM_SIZE_LRN getEnvVariable("SHARED_MEM_SIZE_LRN", 8192)
#define GRID_SIZE_LSTSQ_REG getEnvVariable("GRID_SIZE_LSTSQ_REG", 256)
#define BLOCK_SIZE_LSTSQ_REG getEnvVariable("BLOCK_SIZE_LSTSQ_REG",256)
#define SHARED_MEM_SIZE_LSTSQ_REG getEnvVariable("SHARED_MEM_SIZE_LSTSQ_REG", 128)
#define GRID_SIZE_LUP getEnvVariable("GRID_SIZE_LUP", 256)
#define GRID_SIZE_LUP getEnvVariable("GRID_SIZE_LUP",256)
#define SHARED_MEM_SIZE_LUP getEnvVariable("SHARED_MEM_SIZE_LUP", 512)
#define GRID_SIZE_LUP_LOW getEnvVariable("GRID_SIZE_LUP_LOW", 256)
#define BLOCK_SIZE_LUP_LOW getEnvVariable("BLOCK_SIZE_LUP_LOW",256)
#define SHARED_MEM_SIZE_LUP_LOW getEnvVariable("SHARED_MEM_SIZE_LUP_LOW", 512)
#define GRID_SIZE_MATRIX_SET_DIAG getEnvVariable("GRID_SIZE_MATRIX_SET_DIAG", 256)
#define BLOCK_SIZE_MATRIX_SET_DIAG getEnvVariable("BLOCK_SIZE_MATRIX_SET_DIAG",256)
#define SHARED_MEM_SIZE_MATRIX_SET_DIAG getEnvVariable("SHARED_MEM_SIZE_MATRIX_SET_DIAG", 512)
#define GRID_SIZE_MERGE getEnvVariable("GRID_SIZE_MERGE", 256)
#define BLOCK_SIZE_MERGE getEnvVariable("BLOCK_SIZE_MERGE",256)
#define SHARED_MEM_SIZE_MERGE getEnvVariable("SHARED_MEM_SIZE_MERGE", 512)
#define GRID_SIZE_MESHGRID getEnvVariable("GRID_SIZE_MESHGRID", 256)
#define BLOCK_SIZE_MESHGRID getEnvVariable("BLOCK_SIZE_MESHGRID",256)
#define SHARED_MEM_SIZE_MESHGRID getEnvVariable("SHARED_MEM_SIZE_MESHGRID", 1024)
#define GRID_SIZE_NTH_ELEMENT_FILL getEnvVariable("GRID_SIZE_NTH_ELEMENT_FILL", 32)
#define BLOCK_SIZE_NTH_ELEMENT_FILL getEnvVariable("BLOCK_SIZE_NTH_ELEMENT_FILL",64)
#define SHARED_MEM_SIZE_NTH_ELEMENT_FILL getEnvVariable("SHARED_MEM_SIZE_NTH_ELEMENT_FILL", 1024)
#define GRID_SIZE_ONE_HOT getEnvVariable("GRID_SIZE_ONE_HOT", 32)
#define BLOCK_SIZE_ONE_HOT getEnvVariable("BLOCK_SIZE_ONE_HOT",64)
#define SHARED_MEM_SIZE_ONE_HOT getEnvVariable("SHARED_MEM_SIZE_ONE_HOT", 1024)
#define GRID_SIZE_PAD getEnvVariable("GRID_SIZE_PAD", 32)
#define BLOCK_SIZE_PAD getEnvVariable("BLOCK_SIZE_PAD",64)
#define SHARED_MEM_SIZE_PAD getEnvVariable("SHARED_MEM_SIZE_PAD", 1024)
#define GRID_SIZE_MIRROR_PAD_LINEAR getEnvVariable("GRID_SIZE_MIRROR_PAD_LINEAR", 256)
#define BLOCK_SIZE_MIRROR_PAD_LINEAR getEnvVariable("BLOCK_SIZE_MIRROR_PAD_LINEAR",512)
#define SHARED_MEM_SIZE_MIRROR_PAD_LINEAR getEnvVariable("SHARED_MEM_SIZE_MIRROR_PAD_LINEAR", 256)
#define GRID_SIZE_MIRROR_PAD_TAD getEnvVariable("GRID_SIZE_MIRROR_PAD_TAD", 256)
#define BLOCK_SIZE_MIRROR_PAD_TAD getEnvVariable("BLOCK_SIZE_MIRROR_PAD_TAD",512)
#define SHARED_MEM_SIZE_MIRROR_PAD_TAD getEnvVariable("SHARED_MEM_SIZE_MIRROR_PAD_TAD", 256)
#define GRID_SIZE_PERCENTILE getEnvVariable("GRID_SIZE_PERCENTILE", 256)
#define BLOCK_SIZE_PERCENTILE getEnvVariable("BLOCK_SIZE_PERCENTILE",512)
#define SHARED_MEM_SIZE_PERCENTILE getEnvVariable("SHARED_MEM_SIZE_PERCENTILE", 1024)
#define GRID_SIZE_POLYGAMMA getEnvVariable("GRID_SIZE_POLYGAMMA", 256)
#define BLOCK_SIZE_POLYGAMMA getEnvVariable("BLOCK_SIZE_POLYGAMMA",512)
#define SHARED_MEM_SIZE_POLYGAMMA getEnvVariable("SHARED_MEM_SIZE_POLYGAMMA", 1024)
#define GRID_SIZE_PREFIX getEnvVariable("GRID_SIZE_PREFIX", 256)
#define BLOCK_SIZE_PREFIX getEnvVariable("BLOCK_SIZE_PREFIX",512)
#define SHARED_MEM_SIZE_PREFIX getEnvVariable("SHARED_MEM_SIZE_PREFIX", 1024)
#define GRID_SIZE_PRINT getEnvVariable("GRID_SIZE_PRINT", 1)
#define BLOCK_SIZE_PRINT getEnvVariable("BLOCK_SIZE_PRINT",1)
#define SHARED_MEM_SIZE_PRINT getEnvVariable("SHARED_MEM_SIZE_PRINT", 1024)
#define GRID_SIZE_QR getEnvVariable("GRID_SIZE_QR", 28)
#define BLOCK_SIZE_QR getEnvVariable("BLOCK_SIZE_QR",28)
#define SHARED_MEM_SIZE_QR getEnvVariable("SHARED_MEM_SIZE_QR", 28)
#define GRID_SIZE_RANDOM_GAMMA getEnvVariable("GRID_SIZE_RANDOM_GAMMA", 128)
#define BLOCK_SIZE_RANDOM_GAMMA getEnvVariable("BLOCK_SIZE_RANDOM_GAMMA",128)
#define SHARED_MEM_SIZE_RANDOM_GAMMA getEnvVariable("SHARED_MEM_SIZE_RANDOM_GAMMA", 256)
#define GRID_SIZE_RANDOM_POISSON getEnvVariable("GRID_SIZE_RANDOM_POISSON", 128)
#define BLOCK_SIZE_RANDOM_POISSON getEnvVariable("BLOCK_SIZE_RANDOM_POISSON",256)
#define SHARED_MEM_SIZE_RANDOM_POISSON getEnvVariable("SHARED_MEM_SIZE_RANDOM_POISSON", 256)
#define GRID_SIZE_RANDOM_UNIFORM getEnvVariable("GRID_SIZE_RANDOM_UNIFORM", 128)
#define BLOCK_SIZE_RANDOM_UNIFORM getEnvVariable("BLOCK_SIZE_RANDOM_UNIFORM",128)
#define SHARED_MEM_SIZE_RANDOM_UNIFORM getEnvVariable("SHARED_MEM_SIZE_RANDOM_UNIFORM", 128)
#define GRID_SIZE_RANDOM_SHUFFLE_FISHER getEnvVariable("GRID_SIZE_RANDOM_SHUFFLE_FISHER", 128)
#define BLOCK_SIZE_RANDOM_SHUFFLE_FISHER getEnvVariable("BLOCK_SIZE_RANDOM_SHUFFLE_FISHER",128)
#define SHARED_MEM_SIZE_RANDOM_SHUFFLE_FISHER getEnvVariable("SHARED_MEM_SIZE_RANDOM_SHUFFLE_FISHER", 128)
#define GRID_SIZE_RANDOM_SHUFFLE_MERGE getEnvVariable("GRID_SIZE_RANDOM_SHUFFLE_MERGE", 128)
#define BLOCK_SIZE_RANDOM_SHUFFLE_MERGE getEnvVariable("BLOCK_SIZE_RANDOM_SHUFFLE_MERGE",128)
#define SHARED_MEM_SIZE_RANDOM_SHUFFLE_MERGE getEnvVariable("SHARED_MEM_SIZE_RANDOM_SHUFFLE_MERGE", 128)
#define GRID_SIZE_RANGE getEnvVariable("GRID_SIZE_RANGE", 512)
#define BLOCK_SIZE_RANGE getEnvVariable("BLOCK_SIZE_RANGE",512)
#define SHARED_MEM_SIZE_RANGE getEnvVariable("SHARED_MEM_SIZE_RANGE", 2048)
#define GRID_SIZE_REVERSE getEnvVariable("GRID_SIZE_REVERSE", 256)
#define BLOCK_SIZE_REVERSE getEnvVariable("BLOCK_SIZE_REVERSE",512)
#define SHARED_MEM_SIZE_REVERSE getEnvVariable("SHARED_MEM_SIZE_REVERSEE", 8192)
#define GRID_SIZE_ROLL getEnvVariable("GRID_SIZE_ROLL", 1)
#define BLOCK_SIZE_ROLL getEnvVariable("BLOCK_SIZE_ROLL",1)
#define SHARED_MEM_SIZE_ROLL getEnvVariable("SHARED_MEM_SIZE_ROLL", 1024)
#define GRID_SIZE_BATCH_TO_SPACE_ND getEnvVariable("GRID_SIZE_BATCH_TO_SPACE_ND", 1)
#define BLOCK_SIZE_BATCH_TO_SPACE_ND getEnvVariable("BLOCK_SIZE_BATCH_TO_SPACE_ND",1)
#define SHARED_MEM_SIZE_BATCH_TO_SPACE_ND getEnvVariable("SHARED_MEM_SIZE_BATCH_TO_SPACE_ND", 1024)
#define GRID_SIZE_SPACE_TO_BATCH getEnvVariable("GRID_SIZE_SPACE_TO_BATCH", 1)
#define BLOCK_SIZE_SPACE_TO_BATCH getEnvVariable("BLOCK_SIZE_SPACE_TO_BATCH",1)
#define SHARED_MEM_SIZE_SPACE_TO_BATCH getEnvVariable("SHARED_MEM_SIZE_SPACE_TO_BATCH", 1024)
#define GRID_SIZE_SPACE_TO_BATCH_ND getEnvVariable("GRID_SIZE_SPACE_TO_BATCH_ND", 1)
#define BLOCK_SIZE_SPACE_TO_BATCH_ND getEnvVariable("BLOCK_SIZE_SPACE_TO_BATCH_ND",1)
#define SHARED_MEM_SIZE_SPACE_TO_BATCH_ND getEnvVariable("SHARED_MEM_SIZE_SPACE_TO_BATCH_ND", 1024)
#define GRID_SIZE_SPACE_TO_DEPTH getEnvVariable("GRID_SIZE_SPACE_TO_DEPTH", 512)
#define BLOCK_SIZE_SPACE_TO_DEPTH getEnvVariable("BLOCK_SIZE_SPACE_TO_DEPTH",512)
#define SHARED_MEM_SIZE_SPACE_TO_DEPTH getEnvVariable("SHARED_MEM_SIZE_SPACE_TO_DEPTH", 1024)
#define GRID_SIZE_SCATTER getEnvVariable("GRID_SIZE_SCATTER", 512)
#define BLOCK_SIZE_SCATTER getEnvVariable("BLOCK_SIZE_SCATTER",512)
#define SHARED_MEM_SIZE_SCATTER getEnvVariable("SHARED_MEM_SIZE_SCATTER", 1024)
#define GRID_SIZE_SCATTER_CHECK_INDICES getEnvVariable("GRID_SIZE_SCATTER_CHECK_INDICES", 512)
#define BLOCK_SIZE_SCATTER_CHECK_INDICES getEnvVariable("BLOCK_SIZE_SCATTER_CHECK_INDICES",512)
#define SHARED_MEM_SIZE_SCATTER_CHECK_INDICES getEnvVariable("SHARED_MEM_SIZE_SCATTER_CHECK_INDICES", 1024)
#define GRID_SIZE_SCATTER_ND getEnvVariable("GRID_SIZE_SCATTER_ND", 512)
#define BLOCK_SIZE_SCATTER_ND getEnvVariable("BLOCK_SIZE_SCATTER_ND",512)
#define SHARED_MEM_SIZE_SCATTER_ND getEnvVariable("SHARED_MEM_SIZE_SCATTER_ND", 1024)
#define GRID_SIZE_SCATTER_SIMPLE getEnvVariable("GRID_SIZE_SCATTER_SIMPLE", 512)
#define BLOCK_SIZE_SCATTER_SIMPLE getEnvVariable("BLOCK_SIZE_SCATTER_SIMPLE",256)
#define SHARED_MEM_SIZE_SCATTER_SIMPLE getEnvVariable("SHARED_MEM_SIZE_SCATTER_SIMPLE", SD_MAX_NUM_THREADS)
#define GRID_SIZE_SCATTER_UPDATE getEnvVariable("GRID_SIZE_SCATTER_UPDATE", 256)
#define BLOCK_SIZE_SCATTER_UPDATE getEnvVariable("BLOCK_SIZE_SCATTER_UPDATE",256)
#define SHARED_MEM_SIZE_SCATTER_UPDATE getEnvVariable("SHARED_MEM_SIZE_SCATTER_UPDATE", 1024)
#define GRID_SIZE_SEGMENT_INDICES_VALIDATE getEnvVariable("GRID_SIZE_SEGMENT_INDICES_VALIDATE", 1)
#define BLOCK_SIZE_SEGMENT_INDICES_VALIDATE getEnvVariable("BLOCK_SIZE_SEGMENT_INDICES_VALIDATE",1)
#define SHARED_MEM_SIZE_SEGMENT_INDICES_VALIDATE getEnvVariable("SHARED_MEM_SIZE_SEGMENT_INDICES_VALIDATE", 128)
#define GRID_SIZE_SEGMENT getEnvVariable("GRID_SIZE_SEGMENT", 1)
#define BLOCK_SIZE_SEGMENT getEnvVariable("BLOCK_SIZE_SEGMENT",1)
#define SHARED_MEM_SIZE_SEGMENT getEnvVariable("SHARED_MEM_SIZE_SEGMENT", 128)
#define GRID_SIZE_SEGMENT_TAD getEnvVariable("GRID_SIZE_SEGMENT_TAD", 1)
#define BLOCK_SIZE_SEGMENT_TAD getEnvVariable("BLOCK_SIZE_SEGMENT_TAD",1)
#define SHARED_MEM_SIZE_SEGMENT_TAD getEnvVariable("SHARED_MEM_SIZE_SEGMENT_TAD", 128)
#define GRID_SIZE_SEGMENT_BP getEnvVariable("GRID_SIZE_SEGMENT_BP", 1)
#define BLOCK_SIZE_SEGMENT_BP getEnvVariable("BLOCK_SIZE_SEGMENT_BP",1)
#define SHARED_MEM_SIZE_SEGMENT_BP getEnvVariable("SHARED_MEM_SIZE_SEGMENT_BP", 128)
#define GRID_SIZE_SEGMENT_BP_TAD getEnvVariable("GRID_SIZE_SEGMENT_BP_TAD", 1)
#define BLOCK_SIZE_SEGMENT_BP_TAD getEnvVariable("BLOCK_SIZE_SEGMENT_BP_TAD",1)
#define SHARED_MEM_SIZE_SEGMENT_BP_TAD getEnvVariable("SHARED_MEM_SIZE_SEGMENT_BP_TAD", 128)
#define GRID_SIZE_SEGMENT_PROD_2_LINEAR getEnvVariable("GRID_SIZE_SEGMENT_PROD_2_LINEAR", 256)
#define BLOCK_SIZE_SEGMENT_PROD_2_LINEAR getEnvVariable("BLOCK_SIZE_SEGMENT_PROD_2_LINEAR",128)
#define SHARED_MEM_SIZE_SEGMENT_PROD_2_LINEAR getEnvVariable("SHARED_MEM_SIZE_SEGMENT_PROD_2_LINEAR", 128)
#define GRID_SIZE_SEGMENT_PROD_2_TAD getEnvVariable("GRID_SIZE_SEGMENT_PROD_2_TAD", 512)
#define BLOCK_SIZE_SEGMENT_PROD_2_TAD getEnvVariable("BLOCK_SIZE_SEGMENT_PROD_TAD",128)
#define SHARED_MEM_SIZE_SEGMENT_PROD_2_TAD getEnvVariable("SHARED_MEM_SIZE_SEGMENT_PROD_2_TAD", 2048)
#define GRID_SIZE_UNSORTEDSEGMENT_PROD_2 getEnvVariable("GRID_SIZE_UNSORTEDSEGMENT_PROD_2", 128)
#define BLOCK_SIZE_UNSORTEDSEGMENT_PROD_2 getEnvVariable("BLOCK_SIZE_UNSORTEDSEGMENT_PROD_2",256)
#define SHARED_MEM_SIZE_UNSORTEDSEGMENT_PROD_2 getEnvVariable("SHARED_MEM_SIZE_UNSORTEDSEGMENT_PROD_2", 256)
#define GRID_SIZE_SOLVE getEnvVariable("GRID_SIZE_SOLVE", 128)
#define BLOCK_SIZE_SOLVE getEnvVariable("BLOCK_SIZE_SOLVE",256)
#define SHARED_MEM_SIZE_SOLVE getEnvVariable("SHARED_MEM_SIZE_SOLVE", 256)
#define GRID_SIZE_SRU_BI getEnvVariable("GRID_SIZE_SRU_BI", 128)
#define BLOCK_SIZE_SRU_BI getEnvVariable("BLOCK_SIZE_SRU_BI",256)
#define SHARED_MEM_SIZE_SRU_BI getEnvVariable("SHARED_MEM_SIZE_SRU_BI", 256)
#define GRID_SIZE_STACK getEnvVariable("GRID_SIZE_STACK", 128)
#define BLOCK_SIZE_STACK getEnvVariable("BLOCK_SIZE_STACK",256)
#define SHARED_MEM_SIZE_STACK getEnvVariable("SHARED_MEM_SIZE_STACK", 256)
#define GRID_SIZE_TOP_K_MOVER getEnvVariable("GRID_SIZE_TOP_K_MOVER", 256)
#define BLOCK_SIZE_TOP_K_MOVER getEnvVariable("BLOCK_SIZE_TOP_K_MOVER",256)
#define SHARED_MEM_SIZE_TOP_K_MOVER getEnvVariable("SHARED_MEM_SIZE_TOP_K_MOVER", 1024)
#define GRID_SIZE_TOP_K_INDICES getEnvVariable("GRID_SIZE_TOP_K_INDICES", 256)
#define BLOCK_SIZE_TOP_K_INDICES getEnvVariable("BLOCK_SIZE_TOP_K_INDICES",256)
#define SHARED_MEM_SIZE_TOP_K_INDICES getEnvVariable("SHARED_MEM_SIZE_TOP_K_INDICES", 1024)
#define GRID_SIZE_INVERT_PERMUTE getEnvVariable("GRID_SIZE_INVERT_PERMUTE", 256)
#define BLOCK_SIZE_INVERT_PERMUTE getEnvVariable("BLOCK_SIZE_INVERT_PERMUTE",256)
#define SHARED_MEM_SIZE_INVERT_PERMUTE getEnvVariable("SHARED_MEM_SIZE_INVERT_PERMUTE", 1024)
#define GRID_SIZE_TRACE getEnvVariable("GRID_SIZE_TRACE", 256)
#define BLOCK_SIZE_TRACE getEnvVariable("BLOCK_SIZE_TRACE",256)
#define SHARED_MEM_SIZE_TRACE getEnvVariable("SHARED_MEM_SIZE_TRACE", 1024)
#define GRID_SIZE_TRIU getEnvVariable("GRID_SIZE_TRIU", 256)
#define BLOCK_SIZE_TRIU getEnvVariable("BLOCK_SIZE_TRIU",256)
#define SHARED_MEM_SIZE_TRIU getEnvVariable("SHARED_MEM_SIZE_TRIU", 1024)
#define GRID_SIZE_TILE getEnvVariable("GRID_SIZE_TILE", 256)
#define BLOCK_SIZE_TILE getEnvVariable("BLOCK_SIZE_TILE",256)
#define SHARED_MEM_SIZE_TILE getEnvVariable("SHARED_MEM_SIZE_TILE", 1024)
#define GRID_SIZE_TRIANGULAR_SOLVE getEnvVariable("GRID_SIZE_TRIANGULAR_SOLVE", 128)
#define BLOCK_SIZE_TRIANGULAR_SOLVE getEnvVariable("BLOCK_SIZE_TRIANGULAR_SOLVE",256)
#define SHARED_MEM_SIZE_TRIANGULAR_SOLVE getEnvVariable("SHARED_MEM_SIZE_TRIANGULAR_SOLVE", 256)
#define GRID_SIZE_UPDATER getEnvVariable("GRID_SIZE_UPDATER", 128)
#define BLOCK_SIZE_UPDATER getEnvVariable("BLOCK_SIZE_UPDATER",256)
#define SHARED_MEM_SIZE_UPDATER getEnvVariable("SHARED_MEM_SIZE_UPDATER", 256)
#define GRID_SIZE_ZETA getEnvVariable("GRID_SIZE_ZETA", 128)
#define BLOCK_SIZE_ZETA getEnvVariable("BLOCK_SIZE_ZETA",256)
#define SHARED_MEM_SIZE_ZETA getEnvVariable("SHARED_MEM_SIZE_ZETA", 1024)
#define GRID_SIZE_AVG_POOLING getEnvVariable("GRID_SIZE_AVG_POOLING", 512)
#define BLOCK_SIZE_AVG_POOLING getEnvVariable("BLOCK_SIZE_AVG_POOLING",512)
#define SHARED_MEM_SIZE_POOLING getEnvVariable("SHARED_MEM_SIZE_POOLING", 4192)
// Grid, block, and shared memory size definitions using environment variables
#define GRID_SIZE_MMUL getEnvVariable("GRID_SIZE_MMUL", 256)
#define BLOCK_SIZE_MMUL getEnvVariable("BLOCK_SIZE_MMUL", 512)
#define SHARED_MEM_SIZE_MMUL getEnvVariable("SHARED_MEM_SIZE_MMUL", 8192)
#define GRID_SIZE_IMAGE_RESIZE_NEIGHBOR getEnvVariable("GRID_SIZE_IMAGE_RESIZE_NEIGHBOR", 256)
#define BLOCK_SIZE_IMAGE_RESIZE_NEIGHBOR getEnvVariable("BLOCK_SIZE_IMAGE_RESIZE_NEIGHBOR", 512)
#define SHARED_MEM_SIZE_IMAGE_RESIZE_NEIGHBOR getEnvVariable("SHARED_MEM_SIZE_IMAGE_RESIZE_NEIGHBOR", 8192)
#define GRID_SIZE_SWAP_UNSAFE getEnvVariable("GRID_SIZE_SWAP_UNSAFE", 256)
#define BLOCK_SIZE_SWAP_UNSAFE getEnvVariable("BLOCK_SIZE_SWAP_UNSAFE", 512)
#define SHARED_MEM_SIZE_SWAP_UNSAFE getEnvVariable("SHARED_MEM_SIZE_SWAP_UNSAFE", 8192)
#define GRID_SIZE_DIGAMMA getEnvVariable("GRID_SIZE_DIGAMMA", 256)
#define BLOCK_SIZE_DIGAMMA getEnvVariable("BLOCK_SIZE_DIGAMMA", 512)
#define SHARED_MEM_SIZE_DIGAMMA getEnvVariable("SHARED_MEM_SIZE_DIGAMMA", 1024)
#define GRID_SIZE_FILL_TRI getEnvVariable("GRID_SIZE_FILL_TRI", 256)
#define BLOCK_SIZE_FILL_TRI getEnvVariable("BLOCK_SIZE_FILL_TRI", 512)
#define SHARED_MEM_SIZE_FILL_TRI getEnvVariable("SHARED_MEM_SIZE_FILL_TRI", 1024)
#define GRID_SIZE_IDENTITY getEnvVariable("GRID_SIZE_IDENTITY", 256)
#define BLOCK_SIZE_IDENTITY getEnvVariable("GRID_SIZE_IDENTITY", 512)
#define SHARED_MEM_SIZE_IDENTITY getEnvVariable("SHARED_MEM_SIZE_IDENTITY", 1024)
#define GRID_SIZE_DYNAMIC_STITCH_TAD getEnvVariable("GRID_SIZE_DYNAMIC_STITCH_TAD", 512)
#define BLOCK_SIZE_DYNAMIC_STITCH_TAD getEnvVariable("BLOCK_SIZE_DYNAMIC_STITCH_TAD", 512)
#define SHARED_MEM_SIZE_DYNAMIC_STITCH_TAD getEnvVariable("SHARED_MEM_SIZE_DYNAMIC_STITCH_TAD", 1024)
#define GRID_SIZE_DYNAMIC_PARTITION_TAD getEnvVariable("GRID_SIZE_DYNAMIC_PARTITION_TAD", 256)
#define BLOCK_SIZE_DYNAMIC_PARTITION_TAD getEnvVariable("BLOCK_SIZE_DYNAMIC_PARTITION_TAD", 256)
#define SHARED_MEM_SIZE_DYNAMIC_PARTITION_TAD getEnvVariable("SHARED_MEM_SIZE_DYNAMIC_PARTITION_TAD", 1024)
#define GRID_SIZE_SOLVE getEnvVariable("GRID_SIZE_SOLVE", 100)
#define BLOCK_SIZE_SOLVE getEnvVariable("BLOCK_SIZE_SOLVE", 1)
#define SHARED_MEM_SIZE_SOLVE getEnvVariable("SHARED_MEM_SIZE_SOLVE", 256)
#define GRID_SIZE_LUP getEnvVariable("GRID_SIZE_LUP", 128)
#define BLOCK_SIZE_LUP getEnvVariable("BLOCK_SIZE_LUP", 256)
#define SHARED_MEM_SIZE_LUP getEnvVariable("SHARED_MEM_SIZE_LUP", 1024)
#define GRID_SIZE_SOFTMAX getEnvVariable("GRID_SIZE_SOFTMAX", 128)
#define BLOCK_SIZE_SOFTMAX getEnvVariable("BLOCK_SIZE_SOFTMAX", 256)
#define SHARED_MEM_SIZE_SOFTMAX getEnvVariable("SHARED_MEM_SIZE_SOFTMAX", 1024)
dim3 getSoftmaxDims(int numTads);
dim3 getLupDims(int batchSize);
dim3 getDynamicPartitionDims(int numThreads,int yDTypeSize);
dim3 getIdentityLaunchDims(int len,int rank);
dim3 getRepeatLaunchDims(int len,int rank);
dim3 getFillTriLaunchDims(int len,int rank);
dim3 getGemVDims(int m);
dim3 getAddBiasDims(int len,int rank) ;
dim3 getLaunchDims(const std::string& key);
dim3 getCol2imLaunchParams(sd::NDArray im,sd::NDArray col);
dim3 getim2ColLaunchParams(sd::NDArray col);
dim3 getMMulDims(int length,int sizeofDataType);
dim3 getAccumDims(int xLength);
dim3 getReduceDims(int xLength);
dim3 getReduceAllDims(int xLength);
dim3 getSortFullDims(int xLength);
dim3 getSortTadLarge(int numberTads);
dim3 getSortTadDims(int numberTads);
dim3 getFillUpSegmentsDims(int numClasses,int length);
dim3 getSegmentSumDims(int numClasses,int length);
dim3 getSequenceMaskLaunchDims(int maxIndex,sd::NDArray input);
dim3 getPoolingDims(int length,int rank);
dim3 getCol2VolDims(int length,int rank);
dim3 getVol2ColDims(int length,int rank);
dim3 getUpsamplingDims(int length,int rank);
dim3 getHistogramDims(int length,int numBins);
dim3 getAdjustDims(int numTads);
dim3 getBatchNormDims(int length);
dim3 getCompareAndBitpackDims(int length);
dim3 getCompareElem(int length);
dim3 getConcat(int length);
dim3 getBetaInc(int maxIter,int length,int dataTypeSize);
dim3 getCross(int length,int rank,int lastSize);
dim3 getDilation(int outputLength,int weightRank,int outputRank);
dim3 getGatherLinear(int numSubArrs);
dim3 getGatherNd(int outputLength,int maxRank);
dim3 getHashCodeSplit(int length,int blockSize);
dim3 getHashCodeInternal( int numBlocks);
dim3 cropAndResize(int batchSize,int imageHeight,int imageWidth,int cropHeight,int cropWidth);
dim3 imageHelper(int numTads);
dim3 lrnDims(int tadLength,int numTads, int xDTypeSize,int zDTypeSize);
dim3 lupDims(int n);
dim3 lupDimsLow(int n);
dim3 matrixSetDiagDims(int length,int rank);
dim3 mergeDims(int length);
dim3 oneHotDims(int length,int rank,int shapeSize);
dim3 padDims(int length,int rank);
dim3 polygammaDims(int length);
dim3 prefixDims(int numTads,int sizeOfDataType);
dim3 randomShuffleFisherDims(int power,int inputDataTypeSize);
dim3 randomShuffleMergeDims(int j,int length);
dim3 batchToSpaceNdLaunch(int length,int rank);
dim3 spaceToBatchLaunch(int length,int rank);
dim3 spaceToBatchNdLaunch(int length,int rank);
dim3 scatterDims(int length,int rank);
dim3 scatterDimsCheckIndices(int length,int rank);
dim3 scatterNdDims(int length,int rank);
dim3 segmentValidateIndices(int length);
dim3 segmentDims(int numClasses,int length);
dim3 segmentTad(int size);
dim3 segmentBpDims(int gradOutLen,int inputLen);
dim3 segmentBpTad(int indicesLen,int inputLen);
dim3 sruBiDims(int len,int rank);
dim3 stackDims(int length);
dim3 topkDims(int numTads);
dim3 topKIndices(int scanWidth,int xDTypeSize,int yDTypeSize);
dim3 invertPermutationDims(int length);
dim3 traceDims(int length);
dim3 triuDims(int length,int rank);
dim3 tileDims(int length,int rank);
dim3 updaterDims(int length);
dim3 zetaDims(int length);
dim3 resizeNeighborDims(int batchSize,int height,int width);
dim3 clipDims(int length);
dim3 mirrorPadLinearDims(int length);
dim3 mirrorPadTad(int length,int rank);
dim3 digammaDims(int length);
#endif //LIBND4J_LAUNCHCONTEXT_H
@@ -0,0 +1,75 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/BlockingQueue.h>
#include <execution/CallableWithArguments.h>
#include <thread>
namespace samediff {
template <typename T>
BlockingQueue<T>::BlockingQueue(int queueSize) {
_size = 0;
_available = true;
}
template <typename T>
T BlockingQueue<T>::poll() {
// locking untill there's something within queue
std::unique_lock<std::mutex> lock(_lock);
_condition.wait(lock, [&] { return this->_size.load() != 0; });
T t(std::move(_queue.front()));
_queue.pop();
_size--;
return t;
}
template <typename T>
void BlockingQueue<T>::put(const T &t) {
{
// locking before push, unlocking after
std::unique_lock<std::mutex> lock(_lock);
_queue.push(t);
_size++;
}
// notifying condition
_condition.notify_one();
}
template <typename T>
bool BlockingQueue<T>::available() {
return _available.load();
}
template <typename T>
void BlockingQueue<T>::markAvailable() {
_available = true;
}
template <typename T>
void BlockingQueue<T>::markUnavailable() {
_available = false;
}
template class BlockingQueue<CallableWithArguments *>;
} // namespace samediff
@@ -0,0 +1,215 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/CallableInterface.h>
#include <helpers/logger.h>
namespace samediff {
CallableInterface::CallableInterface() {
// initial state is available
_available = true;
_filled = false;
_finished = false;
}
bool CallableInterface::available() { return _available.load(); }
void CallableInterface::markUnavailable() { _available = false; }
void CallableInterface::markAvailable() { _available = true; }
void CallableInterface::fill(int threadID, int numThreads, FUNC_DO func) {
_function_do = std::move(func);
_branch = 0;
_num_threads = numThreads;
_thread_id = threadID;
_finished = false;
{
std::unique_lock<std::mutex> l(_ms);
_filled = true;
}
_starter.notify_one();
}
void CallableInterface::fill(int threadID, int numThreads, FUNC_1D func, int64_t startX, int64_t stopX, int64_t incX) {
_function_1d = std::move(func);
_arguments[0] = startX;
_arguments[1] = stopX;
_arguments[2] = incX;
_branch = 1;
_num_threads = numThreads;
_thread_id = threadID;
_finished = false;
{
std::unique_lock<std::mutex> l(_ms);
_filled = true;
}
_starter.notify_one();
}
void CallableInterface::fill(int threadID, int numThreads, FUNC_2D func, int64_t startX, int64_t stopX, int64_t incX,
int64_t start_y, int64_t stop_y, int64_t inc_y) {
_function_2d = std::move(func);
_arguments[0] = startX;
_arguments[1] = stopX;
_arguments[2] = incX;
_arguments[3] = start_y;
_arguments[4] = stop_y;
_arguments[5] = inc_y;
_branch = 2;
_num_threads = numThreads;
_thread_id = threadID;
_finished = false;
{
std::unique_lock<std::mutex> l(_ms);
_filled = true;
}
_starter.notify_one();
}
void CallableInterface::fill(int threadID, int numThreads, FUNC_3D func, int64_t startX, int64_t stopX, int64_t incX,
int64_t start_y, int64_t stop_y, int64_t inc_y, int64_t start_z, int64_t stop_z,
int64_t inc_z) {
_function_3d = std::move(func);
_arguments[0] = startX;
_arguments[1] = stopX;
_arguments[2] = incX;
_arguments[3] = start_y;
_arguments[4] = stop_y;
_arguments[5] = inc_y;
_arguments[6] = start_z;
_arguments[7] = stop_z;
_arguments[8] = inc_z;
_branch = 3;
_num_threads = numThreads;
_thread_id = threadID;
_finished = false;
{
std::unique_lock<std::mutex> l(_ms);
_filled = true;
}
_starter.notify_one();
}
void CallableInterface::fill(int threadID, int numThreads, int64_t *lptr, FUNC_RL func, int64_t startX, int64_t stopX,
int64_t incX) {
_function_rl = std::move(func);
_arguments[0] = startX;
_arguments[1] = stopX;
_arguments[2] = incX;
_lptr = lptr;
_branch = 4;
_num_threads = numThreads;
_thread_id = threadID;
_finished = false;
{
std::unique_lock<std::mutex> l(_ms);
_filled = true;
}
_starter.notify_one();
}
void CallableInterface::fill(int threadID, int numThreads, double *dptr, FUNC_RD func, int64_t startX, int64_t stopX,
int64_t incX) {
_function_rd = std::move(func);
_arguments[0] = startX;
_arguments[1] = stopX;
_arguments[2] = incX;
_dptr = dptr;
_branch = 5;
_num_threads = numThreads;
_thread_id = threadID;
_finished = false;
{
std::unique_lock<std::mutex> l(_ms);
_filled = true;
}
_starter.notify_one();
}
void CallableInterface::waitForTask() {
// block until task is available
std::unique_lock<std::mutex> lock(_ms);
_starter.wait(lock, [&] { return _filled.load(); });
}
void CallableInterface::waitForCompletion() {
// while (!_finished.load());
// block until finished
std::unique_lock<std::mutex> lock(_mf);
_finisher.wait(lock, [&] { return _finished.load(); });
}
void CallableInterface::finish() {
// mark as finished
{
std::unique_lock<std::mutex> l(_mf);
_finished.store(true);
}
_finisher.notify_one();
}
void CallableInterface::execute() {
// mark it as consumed
_filled = false;
// actually executing op
switch (_branch) {
case 0:
_function_do(_thread_id, _num_threads);
break;
case 1:
_function_1d(_thread_id, _arguments[0], _arguments[1], _arguments[2]);
break;
case 2:
_function_2d(_thread_id, _arguments[0], _arguments[1], _arguments[2], _arguments[3], _arguments[4],
_arguments[5]);
break;
case 3:
_function_3d(_thread_id, _arguments[0], _arguments[1], _arguments[2], _arguments[3], _arguments[4], _arguments[5],
_arguments[6], _arguments[7], _arguments[8]);
break;
case 4:
_lptr[0] = _function_rl(_thread_id, _arguments[0], _arguments[1], _arguments[2]);
break;
case 5:
_dptr[0] = _function_rd(_thread_id, _arguments[0], _arguments[1], _arguments[2]);
break;
}
// notify that thread finished the job
this->finish();
}
} // namespace samediff
@@ -0,0 +1,90 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/CallableWithArguments.h>
namespace samediff {
CallableWithArguments::CallableWithArguments(FUNC_DO func, uint64_t thread_id, uint64_t numThreads) {
_function_do = func;
_finished = false;
_threadId = thread_id;
_numThreads = numThreads;
_dimensions = 0;
}
CallableWithArguments::CallableWithArguments(FUNC_3D func, uint64_t thread_id, int64_t start_x, int64_t stop_x,
int64_t increment_x, int64_t start_y, int64_t stop_y, int64_t increment_y,
int64_t start_z, int64_t stop_z, int64_t increment_z) {
_function_3d = func;
_arguments = {start_x, stop_x, increment_x, start_y, stop_y, increment_y, start_z, stop_z, increment_z};
_finished = false;
_threadId = thread_id;
_dimensions = 3;
}
CallableWithArguments::CallableWithArguments(FUNC_1D func, uint64_t thread_id, int64_t start_x, int64_t stop_x,
int64_t increment_x) {
_function_1d = func;
_arguments = {start_x, stop_x, increment_x};
_finished = false;
_threadId = thread_id;
_dimensions = 1;
}
CallableWithArguments::CallableWithArguments(FUNC_2D func, uint64_t thread_id, int64_t start_x, int64_t stop_x,
int64_t increment_x, int64_t start_y, int64_t stop_y,
int64_t increment_y) {
_function_2d = func;
_arguments = {start_x, stop_x, increment_x, start_y, stop_y, increment_y};
_finished = false;
_threadId = thread_id;
_dimensions = 2;
}
int CallableWithArguments::dimensions() { return _dimensions; }
std::vector<int64_t>& CallableWithArguments::arguments() { return _arguments; }
bool CallableWithArguments::finished() { return _finished.load(); }
void CallableWithArguments::finish() {
std::lock_guard<std::mutex> lock(_lock);
_finished = true;
_condition.notify_one();
}
void CallableWithArguments::waitUntilFinished() {
std::unique_lock<std::mutex> lock(_lock);
_condition.wait(lock, [&] { return _finished.load(); });
}
FUNC_1D CallableWithArguments::function_1d() { return _function_1d; }
FUNC_2D CallableWithArguments::function_2d() { return _function_2d; }
FUNC_DO CallableWithArguments::function_do() { return _function_do; }
FUNC_3D CallableWithArguments::function_3d() { return _function_3d; }
uint64_t CallableWithArguments::threadId() { return _threadId; }
uint64_t CallableWithArguments::numThreads() { return _numThreads; }
} // namespace samediff
@@ -0,0 +1,49 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/ErrorReference.h>
namespace sd {
int ErrorReference::errorCode() { return _errorCode; }
const char* ErrorReference::errorMessage() {
// since we're fetching error message - error code will be assumed consumed & nullified
_errorCode = 0;
if(_errorMessage != nullptr)
return _errorMessage->c_str();
return "";
}
void ErrorReference::setErrorCode(int errorCode) { _errorCode = errorCode; }
void ErrorReference::setErrorMessage(std::string message) {
if(_errorMessage != nullptr)
delete _errorMessage;
_errorMessage = new std::string(message);
}
void ErrorReference::setErrorMessage(const char* message) {
if(_errorMessage != nullptr)
delete _errorMessage;
_errorMessage = new std::string(message);
}
} // namespace sd
@@ -0,0 +1,175 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/ThreadPool.h>
#include <helpers/logger.h>
#include <stdexcept>
namespace samediff {
// this function executed once per thread, it polls functions from queue, and executes them via wrapper
static void executionLoop_(int thread_id, BlockingQueue<CallableWithArguments *> *queue) {
while (true) {
// this method blocks until there's something within queue
auto c = queue->poll();
switch (c->dimensions()) {
case 0: {
c->function_do()(c->threadId(), c->numThreads());
c->finish();
} break;
case 1: {
auto args = c->arguments();
c->function_1d()(c->threadId(), args[0], args[1], args[2]);
c->finish();
} break;
case 2: {
auto args = c->arguments();
c->function_2d()(c->threadId(), args[0], args[1], args[2], args[3], args[4], args[5]);
c->finish();
} break;
case 3: {
auto args = c->arguments();
c->function_3d()(c->threadId(), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
args[8]);
c->finish();
} break;
default:
THROW_EXCEPTION("Don't know what to do with provided Callable");
}
}
}
static void executionLoopWithInterface_(int thread_id, CallableInterface *c) {
while (true) {
// blocking here until there's something to do
c->waitForTask();
// execute whatever we have
c->execute();
}
}
ThreadPool::ThreadPool() {
// TODO: number of threads must reflect number of cores for UMA system. In case of NUMA it should be per-device pool
// FIXME: on mobile phones this feature must NOT be used
_available = sd::Environment::getInstance().maxThreads();
_queues.resize(_available.load());
_threads.resize(_available.load());
_interfaces.resize(_available.load());
// we're not creating threadpool on aurora
// creating threads here
for (int e = 0; e < _available.load(); e++) {
_queues[e] = new BlockingQueue<CallableWithArguments *>(2);
_interfaces[e] = new CallableInterface();
_threads[e] = std::thread(executionLoopWithInterface_, e, _interfaces[e]);
_tickets.push(new Ticket());
// _threads[e] = new std::thread(executionLoop_, e, _queues[e]);
// TODO: add other platforms here as well
// now we must set affinity, and it's going to be platform-specific thing
#ifdef LINUX_BUILD
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(e, &cpuset);
int rc = pthread_setaffinity_np(_threads[e]->native_handle(), sizeof(cpu_set_t), &cpuset);
if (rc != 0) THROW_EXCEPTION("Failed to set pthread affinity");
#endif
}
//add an extra ticket to minimize the risk of running out of tickets due to race conditions
_tickets.push(new Ticket());
}
ThreadPool::~ThreadPool() {
// TODO: implement this one properly
for (size_t e = 0; e < _queues.size(); e++) {
// stop each and every thread
// release queue and thread
delete _queues[e];
_threads[e].detach();
delete _interfaces[e];
}
while (!_tickets.empty()) {
auto t = _tickets.front();
_tickets.pop();
delete t;
}
}
ThreadPool &ThreadPool::getInstance() {
static ThreadPool instance;
return instance;
}
void ThreadPool::release(int numThreads) { _available += numThreads; }
Ticket *ThreadPool::tryAcquire(int numThreads) {
if (numThreads <= 0) return nullptr;
Ticket *t = nullptr;
// we check for threads availability first
bool threaded = false;
{
// we lock before checking availability
std::unique_lock<std::mutex> lock(_lock);
//test for both _available and _tickets in order to deal with race conditions caused by the
//fact that marking threads as available AND releasing tickets does not happen atomically
if (_available >= numThreads && !_tickets.empty()) {
threaded = true;
_available -= numThreads;
// getting a ticket from the queue
t = _tickets.front();
_tickets.pop();
// ticket must contain information about number of threads for the current session
t->acquiredThreads(numThreads);
// filling ticket with executable interfaces
for (size_t e = 0, i = 0; e < _queues.size() && i < static_cast<size_t>(numThreads); e++) {
if (_interfaces[e]->available()) {
t->attach(i++, _interfaces[e]);
_interfaces[e]->markUnavailable();
}
}
}
}
// we either dispatch tasks to threads, or run single-threaded
if (threaded) {
return t;
} else {
// if there's no threads available - return nullptr
return nullptr;
}
}
void ThreadPool::release(samediff::Ticket *ticket) {
// returning ticket back to the queue
std::unique_lock<std::mutex> lock(_lock);
_tickets.push(ticket);
}
} // namespace samediff
+953
View File
@@ -0,0 +1,953 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/Threads.h>
#include <execution/ThreadPool.h>
#include <vector>
#include <thread>
#include <helpers/logger.h>
#include <math/templatemath.h>
#include <helpers/shape.h>
#ifdef _OPENMP
#include <omp.h>
#endif
namespace samediff {
int ThreadsHelper::numberOfThreads(int maxThreads, uint64_t numberOfElements) {
// let's see how many threads we actually need first
auto optimalThreads = sd::math::sd_max<uint64_t>(1, numberOfElements / 1024);
// now return the smallest value
return sd::math::sd_min<int>(optimalThreads, maxThreads);
}
Span3::Span3(int64_t startX, int64_t stopX, int64_t incX, int64_t startY, int64_t stopY, int64_t incY, int64_t startZ, int64_t stopZ, int64_t incZ) {
_startX = startX;
_startY = startY;
_startZ = startZ;
_stopX = stopX;
_stopY = stopY;
_stopZ = stopZ;
_incX = incX;
_incY = incY;
_incZ = incZ;
}
Span3 Span3::build(int loop, uint64_t threadID, uint64_t numThreads, int64_t startX, int64_t stopX, int64_t incX, int64_t startY, int64_t stopY, int64_t incY, int64_t startZ, int64_t stopZ, int64_t incZ) {
switch (loop) {
case 1: {
auto span = (stopX - startX) / numThreads;
auto s = span * threadID;
auto e = s + span;
if (threadID == numThreads - 1)
e = stopX;
return Span3(s, e, incX, startY, stopY, incY, startZ, stopZ, incZ);
}
break;
case 2: {
auto span = (stopY - startY) / numThreads;
auto s = span * threadID;
auto e = s + span;
if (threadID == numThreads - 1)
e = stopY;
return Span3(startX, stopX, incX, s, e, incY, startZ, stopZ, incZ);
}
break;
case 3: {
auto span = (stopZ - startZ) / numThreads;
auto s = span * threadID;
auto e = s + span;
if (threadID == numThreads - 1)
e = stopZ;
return Span3(startX, stopX, incX, startY, stopY, incY, s, e, incZ);
}
break;
default:
THROW_EXCEPTION("");
}
return Span3(startX, stopX, incX, startY, stopY, incY, startZ, stopZ, incZ);
}
Span::Span(int64_t startX, int64_t stopX, int64_t incX) {
_startX = startX;
_stopX = stopX;
_incX = incX;
}
Span Span::build(uint64_t threadID, uint64_t numThreads, int64_t startX, int64_t stopX, int64_t incX) {
auto span = (stopX - startX) / numThreads;
auto s = span * threadID;
auto e = s + span;
if (threadID == numThreads - 1)
e = stopX;
return Span(s, e, incX);
}
Span2::Span2(int64_t startX, int64_t stopX, int64_t incX, int64_t startY, int64_t stopY, int64_t incY) {
_startX = startX;
_startY = startY;
_stopX = stopX;
_stopY = stopY;
_incX = incX;
_incY = incY;
}
Span2 Span2::build(int loop, uint64_t threadID, uint64_t numThreads, int64_t startX, int64_t stopX, int64_t incX, int64_t startY, int64_t stopY, int64_t incY) {
switch (loop) {
case 1: {
auto span = (stopX - startX) / numThreads;
auto s = span * threadID;
auto e = s + span;
if (threadID == numThreads - 1)
e = stopX;
return Span2(s, e, incX, startY, stopY, incY);
}
break;
case 2: {
auto span = (stopY - startY) / numThreads;
auto s = span * threadID;
auto e = s + span;
if (threadID == numThreads - 1)
e = stopY;
return Span2(startX, stopX, incX, s, e, incY);
}
break;
default:
THROW_EXCEPTION("");
}
return Span2(startX, stopX, incX, 0, 0, incY);
}
int64_t Span::startX() const {
return _startX;
}
int64_t Span::stopX() const {
return _stopX;
}
int64_t Span::incX() const {
return _incX;
}
int64_t Span2::startX() const {
return _startX;
}
int64_t Span2::startY() const {
return _startY;
}
int64_t Span2::stopX() const {
return _stopX;
}
int64_t Span2::stopY() const {
return _stopY;
}
int64_t Span2::incX() const {
return _incX;
}
int64_t Span2::incY() const {
return _incY;
}
int64_t Span3::startX() const {
return _startX;
}
int64_t Span3::startY() const {
return _startY;
}
int64_t Span3::startZ() const {
return _startZ;
}
int64_t Span3::stopX() const {
return _stopX;
}
int64_t Span3::stopY() const {
return _stopY;
}
int64_t Span3::stopZ() const {
return _stopZ;
}
int64_t Span3::incX() const {
return _incX;
}
int64_t Span3::incY() const {
return _incY;
}
int64_t Span3::incZ() const {
return _incZ;
}
int ThreadsHelper::pickLoop2d(int numThreads, uint64_t itersX, uint64_t itersY) {
// if one of dimensions is definitely too small - we just pick the other one
if (itersX < static_cast<uint64_t>(numThreads) && itersY >= static_cast<uint64_t>(numThreads))
return 2;
if (itersY < static_cast<uint64_t>(numThreads) && itersX >= static_cast<uint64_t>(numThreads))
return 1;
// next step - we pick the most balanced dimension
auto remX = itersX % numThreads;
auto remY = itersY % numThreads;
auto splitY = itersY / numThreads;
// if there's no remainder left in some dimension - we're picking that dimension, because it'll be the most balanced work distribution
if (remX == 0)
return 1;
if (remY == 0)
return 2;
// if there's no loop without a remainder - we're picking one with smaller remainder
if (remX < remY)
return 1;
if (remY < remX && splitY >= 64) // we don't want too small splits over last dimension, or vectorization will fail
return 2;
// if loops are equally sized - give the preference to the first thread
return 1;
}
static int threads_(int maxThreads, uint64_t elements) {
uint64_t typeCastedMaxThreads = static_cast<uint64_t>(maxThreads);
if ( static_cast<uint64_t>(elements) == typeCastedMaxThreads) {
return maxThreads;
}
else if (elements > typeCastedMaxThreads) {
// if we have full load across thread, or at least half of threads can be utilized
auto rem = elements % typeCastedMaxThreads;
if (rem == 0 || rem >= typeCastedMaxThreads / 3)
return maxThreads;
else
return threads_(maxThreads - 1, elements);
}
else if (elements < typeCastedMaxThreads) {
return elements;
}
return 1;
}
int ThreadsHelper::numberOfThreads2d(int maxThreads, uint64_t iters_x, uint64_t iters_y) {
uint64_t typeCastedMaxThreads = static_cast<uint64_t>(maxThreads);
// in some cases there's nothing to think about, part 1
if (iters_x < typeCastedMaxThreads && iters_y < typeCastedMaxThreads)
return sd::math::sd_max<int>(iters_x, iters_y);
auto remX = iters_x % maxThreads;
auto remY = iters_y % maxThreads;
// in some cases there's nothing to think about, part 2
if ((iters_x >= typeCastedMaxThreads && remX == 0) || (iters_y >= typeCastedMaxThreads && remY == 0))
return maxThreads;
// at this point we suppose that there's no loop perfectly matches number of our threads
// so let's pick something as equal as possible
if (iters_x > typeCastedMaxThreads || iters_y > typeCastedMaxThreads)
return maxThreads;
else
return numberOfThreads2d(maxThreads - 1, iters_x, iters_y);
}
int ThreadsHelper::numberOfThreads3d(int maxThreads, uint64_t itersX, uint64_t itersY, uint64_t itersZ) {
// we don't want to run underloaded threads
if (itersX * itersY * itersZ <= 32)
return 1;
uint64_t typeCastedMaxThreads = static_cast<uint64_t>(maxThreads);
auto remX = itersX % maxThreads;
auto remY = itersY % maxThreads;
auto remZ = itersZ % maxThreads;
// if we have perfect balance across one of dimensions - just go for it
if ((itersX >= typeCastedMaxThreads && remX == 0) || (itersY >= typeCastedMaxThreads && remY == 0) || (itersZ >= typeCastedMaxThreads && remZ == 0))
return maxThreads;
int threadsX = 0, threadsY = 0, threadsZ = 0;
// now we look into possible number of
threadsX = threads_(maxThreads, itersX);
threadsY = threads_(maxThreads, itersY);
threadsZ = threads_(maxThreads, itersZ);
// we want to split as close to outer loop as possible, so checking it out first
if (threadsX >= threadsY && threadsX >= threadsZ)
return threadsX;
else if (threadsY >= threadsX && threadsY >= threadsZ)
return threadsY;
else if (threadsZ >= threadsX && threadsZ >= threadsY)
return threadsZ;
return 1;
}
int ThreadsHelper::pickLoop3d(int numThreads, uint64_t itersX, uint64_t itersY, uint64_t itersZ) {
auto remX = itersX % numThreads;
auto remY = itersY % numThreads;
auto remZ = itersZ % numThreads;
auto splitX = itersX / numThreads;
auto splitY = itersY / numThreads;
auto splitZ = itersZ / numThreads;
uint64_t typeCastedNumThreads = static_cast<uint64_t>(numThreads);
// if there's no remainder left in some dimension - we're picking that dimension, because it'll be the most balanced work distribution
if (remX == 0)
return 1;
else if (remY == 0)
return 2;
else if (remZ == 0) // TODO: we don't want too smal splits over last dimension? or we do?
return 3;
if (itersX > typeCastedNumThreads)
return 1;
else if (itersY > typeCastedNumThreads)
return 2;
else if (itersZ > typeCastedNumThreads)
return 3;
return 1;
}
#ifdef _OPENMP
std::mutex Threads::gThreadmutex;
uint64_t Threads::_nFreeThreads = sd::Environment::getInstance().maxThreads();
bool Threads::tryAcquire(int numThreads) {
std::lock_guard<std::mutex> lock(gThreadmutex);
auto nThreads = _nFreeThreads - numThreads;
//error: comparison of unsigned expression in >= 0 is always true [-Werror=type-limits]
_nFreeThreads = nThreads;
return true;
}
bool Threads::freeThreads(int numThreads) {
std::lock_guard<std::mutex> lock(gThreadmutex);
_nFreeThreads += numThreads;
// check if correct number of threads
return _nFreeThreads > static_cast<uint64_t >(sd::Environment::getInstance().maxThreads());
}
#endif
int Threads::parallel_tad(FUNC_1D function, sd::LongType start, sd::LongType stop, sd::LongType increment,
sd::LongType numThreads) {
if (start > stop)
THROW_EXCEPTION("Threads::parallel_for got start > stop");
auto delta = (stop - start);
if (numThreads > delta)
numThreads = delta;
if (numThreads == 0)
return 0;
// shortcut
if (numThreads == 1) {
function(0, start, stop, increment);
return 1;
}
#ifdef _OPENMP
if (tryAcquire(numThreads)) {
auto span = delta / numThreads;
#pragma omp parallel for schedule(guided) proc_bind(close) default(shared)
for (sd::LongType e = 0; e < numThreads; e++) {
auto start_ = span * e + start;
auto stop_ = start_ + span;
if (e == numThreads - 1)
stop_ = stop;
function(e, start_, stop_, increment);
}
freeThreads(numThreads);
return numThreads;
}
else {
// if there were no threads available - we'll execute function right within current thread
function(0, start, stop, increment);
// we tell that parallelism request declined
return 1;
}
#else
sd::Environment::getInstance().maxThreads();
auto ticket = ThreadPool::getInstance().tryAcquire(numThreads);
if (ticket != nullptr) {
// if we got our threads - we'll run our jobs here
auto span = delta / numThreads;
for (uint32_t e = 0; e < numThreads; e++) {
auto start_ = span * e + start;
auto stop_ = start_ + span;
// last thread will process tail
if (e == numThreads - 1)
stop_ = stop;
// putting the task into the queue for a given thread
ticket->enqueue(e, numThreads, function, start_, stop_, increment);
}
// block and wait till all threads finished the job
ticket->waitAndRelease();
// we tell that parallelism request succeeded
return numThreads;
}
else {
// if there were no threads available - we'll execute function right within current thread
function(0, start, stop, increment);
// we tell that parallelism request declined
return 1;
}
#endif
}
int Threads::parallel_for(FUNC_1D function, sd::LongType start, sd::LongType stop, sd::LongType increment,
sd::LongType numThreads) {
if (start > stop)
THROW_EXCEPTION("Threads::parallel_for got start > stop");
auto delta = (stop - start);
// in some cases we just fire func as is
if (delta == 0 || numThreads == 1) {
function(0, start, stop, increment);
return 1;
}
auto numElements = delta / increment;
// we decide what's optimal number of threads we need here, and execute it in parallel_tad.
numThreads = ThreadsHelper::numberOfThreads(numThreads, numElements);
return parallel_tad(function, start, stop, increment, numThreads);
}
int Threads::parallel_for(FUNC_2D function, int64_t startX, int64_t stopX, int64_t incX, int64_t startY, int64_t stopY, int64_t incY, uint64_t numThreads, bool debug) {
if (startX > stopX)
THROW_EXCEPTION("Threads::parallel_for got startX > stopX");
if (startY > stopY)
THROW_EXCEPTION("Threads::parallel_for got startY > stopY");
// number of elements per loop
auto delta_x = (stopX - startX);
auto delta_y = (stopY - startY);
// number of iterations per loop
auto itersX = delta_x / incX;
auto itersY = delta_y / incY;
// total number of iterations
auto iters_t = itersX * itersY;
// we are checking the case of number of requested threads was smaller
numThreads = ThreadsHelper::numberOfThreads2d(numThreads, itersX, itersY);
// basic shortcut for no-threading cases
if (numThreads == 1) {
function(0, startX, stopX, incX, startY, stopY, incY);
return 1;
}
// We have couple of scenarios:
// either we split workload along 1st loop, or 2nd
auto splitLoop = ThreadsHelper::pickLoop2d(numThreads, itersX, itersY);
// for debug mode we execute things inplace, without any threads
if (debug) {
for (uint64_t e = 0; e < numThreads; e++) {
auto span = Span2::build(splitLoop, e, numThreads, startX, stopX, incX, startY, stopY, incY);
function(e, span.startX(), span.stopX(), span.incX(), span.startY(), span.stopY(), span.incY());
}
// but we still mimic multithreaded execution
return numThreads;
}
else {
#ifdef _OPENMP
if (tryAcquire(numThreads)) {
#pragma omp parallel for
for (uint64_t e = 0; e < numThreads; e++) {
auto span = Span2::build(splitLoop, e, numThreads, startX, stopX, incX, startY, stopY, incY);
function(e, span.startX(), span.stopX(), span.incX(), span.startY(), span.stopY(), span.incY());
}
freeThreads(numThreads);
return numThreads;
}
else {
// if there were no threads available - we'll execute function right within current thread
function(0, startX, stopX, incX, startY, stopY, incY);
// we tell that parallelism request declined
return 1;
}
#else
auto ticket = ThreadPool::getInstance().tryAcquire(numThreads);
if (ticket != nullptr) {
for (uint64_t e = 0; e < numThreads; e++) {
auto threadId = numThreads - e - 1;
auto span = Span2::build(splitLoop, threadId, numThreads, startX, stopX, incX, startY, stopY, incY);
ticket->enqueue(e, numThreads, function, span.startX(), span.stopX(), span.incX(), span.startY(), span.stopY(), span.incY());
}
// block until all threads finish their job
ticket->waitAndRelease();
return numThreads;
}
else {
// if there were no threads available - we'll execute function right within current thread
function(0, startX, stopX, incX, startY, stopY, incY);
// we tell that parallelism request declined
return 1;
}
#endif
};
}
int Threads::parallel_for(FUNC_3D function, int64_t startX, int64_t stopX, int64_t incX, int64_t startY, int64_t stopY, int64_t incY, int64_t startZ, int64_t stopZ, int64_t incZ, uint64_t numThreads) {
if (startX > stopX)
THROW_EXCEPTION("Threads::parallel_for got startX > stopX");
if (startY > stopY)
THROW_EXCEPTION("Threads::parallel_for got startY > stopY");
if (startZ > stopZ)
THROW_EXCEPTION("Threads::parallel_for got startZ > stopZ");
auto delta_x = stopX - startX;
auto delta_y = stopY - startY;
auto delta_z = stopZ - startZ;
auto itersX = delta_x / incX;
auto itersY = delta_y / incY;
auto itersZ = delta_z / incZ;
numThreads = ThreadsHelper::numberOfThreads3d(numThreads, itersX, itersY, itersZ);
if (numThreads == 1) {
// loop is too small - executing function as is
function(0, startX, stopX, incX, startY, stopY, incY, startZ, stopZ, incZ);
return 1;
}
#ifdef _OPENMP
if (tryAcquire(numThreads)) {
auto splitLoop = ThreadsHelper::pickLoop3d(numThreads, itersX, itersY, itersZ);
#pragma omp parallel for
for (uint64_t e = 0; e < numThreads; e++) {
auto thread_id = numThreads - e - 1;
auto span = Span3::build(splitLoop, thread_id, numThreads, startX, stopX, incX, startY, stopY, incY, startZ, stopZ, incZ);
function(e, span.startX(), span.stopX(), span.incX(), span.startY(), span.stopY(), span.incY(), span.startZ(), span.stopZ(), span.incZ());
}
freeThreads(numThreads);
return numThreads;
}
else {
// if there were no threads available - we'll execute function right within current thread
function(0, startX, stopX, incX, startY, stopY, incY, startZ, stopZ, incZ);
// we tell that parallelism request declined
return 1;
}
#else
auto ticket = ThreadPool::getInstance().tryAcquire(numThreads);
if (ticket != nullptr) {
auto splitLoop = ThreadsHelper::pickLoop3d(numThreads, itersX, itersY, itersZ);
for (uint64_t e = 0; e < numThreads; e++) {
auto thread_id = numThreads - e - 1;
auto span = Span3::build(splitLoop, thread_id, numThreads, startX, stopX, incX, startY, stopY, incY, startZ, stopZ, incZ);
ticket->enqueue(e, numThreads, function, span.startX(), span.stopX(), span.incX(), span.startY(), span.stopY(), span.incY(), span.startZ(), span.stopZ(), span.incZ());
}
// block until we're done
ticket->waitAndRelease();
// we tell that parallelism request succeeded
return numThreads;
}
else {
// if there were no threads available - we'll execute function right within current thread
function(0, startX, stopX, incX, startY, stopY, incY, startZ, stopZ, incZ);
// we tell that parallelism request declined
return 1;
}
#endif
}
int Threads::parallel_do(FUNC_DO function, sd::LongType numThreads) {
if (numThreads == 1) {
function(0, numThreads);
return 1;
}
#ifdef _OPENMP
if (tryAcquire(numThreads)) {
#pragma omp parallel for
for (int e = 0; e < numThreads; e++) {
function(e, numThreads);
}
freeThreads(numThreads);
return numThreads;
}
else {
// if there's no threads available - we'll execute function sequentially one by one
for (uint64_t e = 0; e < static_cast<uint64_t >(numThreads); e++)
function(e, numThreads);
return numThreads;
}
#else
auto ticket = ThreadPool::getInstance().tryAcquire(numThreads - 1);
if (ticket != nullptr) {
// submit tasks one by one
for (sd::LongType e = 0; e < numThreads - 1; e++)
ticket->enqueue(e, numThreads, function);
function(numThreads - 1, numThreads);
ticket->waitAndRelease();
return numThreads;
}
else {
// if there's no threads available - we'll execute function sequentially one by one
for (uint64_t e = 0; e < static_cast<uint64_t>(numThreads); e++)
function(e, numThreads);
return numThreads;
}
#endif
return numThreads;
}
int64_t Threads::parallel_long(FUNC_RL function, FUNC_AL aggregator, sd::LongType start, sd::LongType stop,
sd::LongType increment, sd::LongType numThreads) {
if (start > stop)
THROW_EXCEPTION("Threads::parallel_long got start > stop");
auto delta = (stop - start);
if (delta == 0 || numThreads == 1)
return function(0, start, stop, increment);
auto numElements = delta / increment;
// we decide what's optimal number of threads we need here, and execute it
numThreads = ThreadsHelper::numberOfThreads(numThreads, numElements);
if (numThreads == 1)
return function(0, start, stop, increment);
// create temporary array
int64_t intermediatery[256];
auto span = delta / numThreads;
#ifdef _OPENMP
if (tryAcquire(numThreads)) {
#pragma omp parallel for
for (int e = 0; e < numThreads; e++) {
auto start_ = span * e + start;
auto stop_ = span * (e + 1) + start;
intermediatery[e] = function(e, start_, e == numThreads - 1 ? stop : stop_, increment);
}
freeThreads(numThreads);
}
else {
// if there were no threads available - we'll execute function right within current thread
return function(0, start, stop, increment);
}
#else
auto ticket = ThreadPool::getInstance().tryAcquire(numThreads - 1);
if (ticket == nullptr)
return function(0, start, stop, increment);
// execute threads in parallel
for (uint32_t e = 0; e < numThreads; e++) {
auto start_ = span * e + start;
auto stop_ = span * (e + 1) + start;
if (e == numThreads - 1)
intermediatery[e] = function(e, start_, stop, increment);
else
ticket->enqueue(e, numThreads, &intermediatery[e], function, start_, stop_, increment);
}
ticket->waitAndRelease();
#endif
// aggregate results in single thread
for (uint64_t e = 1; e < static_cast<uint64_t>(numThreads); e++)
intermediatery[0] = aggregator(intermediatery[0], intermediatery[e]);
// return accumulated result
return intermediatery[0];
}
double Threads::parallel_double(FUNC_RD function, FUNC_AD aggregator, int64_t start, int64_t stop, int64_t increment, uint64_t numThreads) {
if (start > stop)
THROW_EXCEPTION("Threads::parallel_long got start > stop");
auto delta = (stop - start);
if (delta == 0 || numThreads == 1)
return function(0, start, stop, increment);
auto numElements = delta / increment;
// we decide what's optimal number of threads we need here, and execute it
numThreads = ThreadsHelper::numberOfThreads(numThreads, numElements);
if (numThreads == 1)
return function(0, start, stop, increment);
// create temporary array
double intermediatery[256];
auto span = delta / numThreads;
#ifdef _OPENMP
if (tryAcquire(numThreads)) {
#pragma omp parallel for
for (uint64_t e = 0; e < numThreads; e++) {
auto start_ = span * e + start;
auto stop_ = span * (e + 1) + start;
intermediatery[e] = function(e, start_, e == numThreads - 1 ? stop : stop_, increment);
}
freeThreads(numThreads);
}
else {
// if there were no thre ads available - we'll execute function right within current thread
return function(0, start, stop, increment);
}
#else
auto ticket = ThreadPool::getInstance().tryAcquire(numThreads - 1);
if (ticket == nullptr)
return function(0, start, stop, increment);
// execute threads in parallel
for (uint32_t e = 0; e < numThreads; e++) {
auto start_ = span * e + start;
auto stop_ = span * (e + 1) + start;
if (e == numThreads - 1)
intermediatery[e] = function(e, start_, stop, increment);
else
ticket->enqueue(e, numThreads, &intermediatery[e], function, start_, stop_, increment);
}
ticket->waitAndRelease();
#endif
// aggregate results in single thread
for (uint64_t e = 1; e < numThreads; e++)
intermediatery[0] = aggregator(intermediatery[0], intermediatery[e]);
// return accumulated result
return intermediatery[0];
}
int Threads::parallel_aligned_increment(FUNC_1D function, int64_t start, int64_t stop, int64_t increment,
size_t type_size,
uint32_t req_numThreads) {
if (start > stop)
THROW_EXCEPTION("Threads::parallel_for got start > stop");
auto num_elements = (stop - start);
//this way we preserve increment starts offset
//so we will partition considering delta but not total elements
auto delta = (stop - start) / increment;
// in some cases we just fire func as is
if (delta == 0 || req_numThreads == 1) {
function(0, start, stop, increment);
return 1;
}
int numThreads = 0;
struct th_span {
sd::LongType start;
sd::LongType end;
};
#ifdef _OPENMP
constexpr int max_thread_count = 8;
#else
constexpr int max_thread_count = 1024;
#endif
th_span thread_spans[max_thread_count];
req_numThreads = req_numThreads > max_thread_count ? max_thread_count : req_numThreads;
#ifdef _OPENMP
int adjusted_numThreads = max_thread_count;
#else
int adjusted_numThreads =
ThreadsHelper::numberOfThreads(req_numThreads, (num_elements * sizeof(double)) / (200 * type_size));
#endif
if (adjusted_numThreads > delta)
adjusted_numThreads = delta;
// shortcut
if (adjusted_numThreads <= 1) {
function(0, start, stop, increment);
return 1;
}
//take span as ceil
auto spand = std::ceil((double)delta / (double)adjusted_numThreads);
numThreads = static_cast<sd::LongType>(std::ceil((double)delta / spand));
auto span = static_cast<sd::LongType>(spand);
//tail_add is additional value of the last part
//it could be negative or positive
//we will spread that value across
auto tail_add = delta - numThreads * span;
sd::LongType begin = 0;
sd::LongType end = 0;
//we will try enqueue bigger parts first
decltype(span) span1 = 0, span2 = 0;
int last = 0;
if (tail_add >= 0) {
//for span == 1 , tail_add is 0
last = tail_add;
span1 = span + 1;
span2 = span;
}
else {
last = numThreads + tail_add;// -std::abs(tail_add);
span1 = span;
span2 = span - 1;
}
for (int i = 0; i < last; i++) {
end = begin + span1 * increment;
// putting the task into the queue for a given thread
thread_spans[i].start = begin;
thread_spans[i].end = end;
begin = end;
}
for (int i = last; i < numThreads - 1; i++) {
end = begin + span2 * increment;
// putting the task into the queue for a given thread
thread_spans[i].start = begin;
thread_spans[i].end = end;
begin = end;
}
//for last one enqueue last offset as stop
//we need it in case our ((stop-start) % increment ) > 0
thread_spans[numThreads - 1].start = begin;
thread_spans[numThreads - 1].end = stop;
#ifdef _OPENMP
if (tryAcquire(numThreads)) {
#pragma omp parallel for
for (int j = 0; j < numThreads; j++) {
function(j, thread_spans[j].start, thread_spans[j].end, increment);
}
freeThreads(numThreads);
return numThreads;
}
else {
function(0, start, stop, increment);
// we tell that parallelism request declined
return 1;
}
#else
auto ticket = ThreadPool::getInstance().tryAcquire(numThreads);
if (ticket != nullptr) {
for (size_t j = 0; j < static_cast<size_t>(numThreads); j++) {
ticket->enqueue(j, numThreads, function, thread_spans[j].start, thread_spans[j].end, increment);
}
// block and wait till all threads finished the job
ticket->waitAndRelease();
// we tell that parallelism request succeeded
return numThreads;
}
else {
// if there were no threads available - we'll execute function right within current thread
function(0, start, stop, increment);
// we tell that parallelism request declined
return 1;
}
#endif
}
}
+95
View File
@@ -0,0 +1,95 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <execution/ThreadPool.h>
#include <execution/Ticket.h>
#include <helpers/logger.h>
namespace samediff {
Ticket::Ticket(const std::vector<BlockingQueue<CallableWithArguments *> *> &queues) {
_acquired = true;
_queues = queues;
}
Ticket::Ticket() {
_acquired = true;
_interfaces.resize(sd::Environment::getInstance().maxThreads());
}
bool Ticket::acquired() { return _acquired; }
void Ticket::enqueue(int thread_id, CallableWithArguments *callable) {
_queues[thread_id]->put(callable);
_callables.emplace_back(callable);
}
void Ticket::enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_DO func) {
_interfaces[thread_id]->fill(thread_id, num_threads, func);
}
void Ticket::enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_1D func, int64_t start_x, int64_t stop_x,
int64_t inc_x) {
_interfaces[thread_id]->fill(thread_id, num_threads, func, start_x, stop_x, inc_x);
}
void Ticket::enqueue(uint32_t thread_id, uint32_t num_threads, int64_t *lpt, FUNC_RL func, int64_t start_x,
int64_t stop_x, int64_t inc_x) {
_interfaces[thread_id]->fill(thread_id, num_threads, lpt, func, start_x, stop_x, inc_x);
}
void Ticket::enqueue(uint32_t thread_id, uint32_t num_threads, double *dpt, FUNC_RD func, int64_t start_x,
int64_t stop_x, int64_t inc_x) {
_interfaces[thread_id]->fill(thread_id, num_threads, dpt, func, start_x, stop_x, inc_x);
}
void Ticket::enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_2D func, int64_t start_x, int64_t stop_x,
int64_t inc_x, int64_t start_y, int64_t stop_y, int64_t inc_y) {
_interfaces[thread_id]->fill(thread_id, num_threads, std::move(func), start_x, stop_x, inc_x, start_y, stop_y, inc_y);
}
void Ticket::enqueue(uint32_t thread_id, uint32_t num_threads, FUNC_3D func, int64_t start_x, int64_t stop_x,
int64_t inc_x, int64_t start_y, int64_t stop_y, int64_t inc_y, int64_t start_z, int64_t stop_z,
int64_t inc_z) {
_interfaces[thread_id]->fill(thread_id, num_threads, func, start_x, stop_x, inc_x, start_y, stop_y, inc_y, start_z,
stop_z, inc_z);
}
void Ticket::acquiredThreads(uint32_t threads) { _acquiredThreads = threads; }
void Ticket::waitAndRelease() {
for (uint32_t e = 0; e < this->_acquiredThreads; e++) {
// block until finished
_interfaces[e]->waitForCompletion();
// mark available
_interfaces[e]->markAvailable();
// increment availability counter
ThreadPool::getInstance().release();
}
// return this ticket back to the pool
ThreadPool::getInstance().release(this);
}
void Ticket::attach(uint32_t thread_id, CallableInterface *call_interface) { _interfaces[thread_id] = call_interface; }
} // namespace samediff