Commit 0dc80b8a authored by CARDOSI Paul's avatar CARDOSI Paul
Browse files

Merge branch 'callable-wrappers' into 'master'

Add support for gpu callables on task method interface

See merge request bramas/spetabaru!23
parents b90adddb efe644a0
......@@ -4,7 +4,7 @@ FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y apt-transport-https \
&& apt-get install -y wget \
&& apt-get install -y g++-7 \
&& apt-get install -y g++-8 \
&& apt-get install -y git \
&& apt-get install -y make \
&& wget https://github.com/Kitware/CMake/releases/download/v3.15.6/cmake-3.15.6-Linux-x86_64.sh && chmod +x cmake-3.15.6-Linux-x86_64.sh \
......
......@@ -6,7 +6,7 @@ spetabaru:
script:
- mkdir build
- cd build
- VERBOSE=1 CXX=g++-7 cmake -DUSE_ADVANCE_TESTING=ON ..
- VERBOSE=1 CXX=g++-8 cmake -DUSE_ADVANCE_TESTING=ON ..
- make
- CTEST_OUTPUT_ON_FAILURE=TRUE make test
......@@ -17,7 +17,7 @@ spetabaru-debug:
script:
- mkdir build
- cd build
- VERBOSE=1 CXX=g++-7 cmake -DCMAKE_BUILD_TYPE=DEBUG -DUSE_ADVANCE_TESTING=ON ..
- VERBOSE=1 CXX=g++-8 cmake -DCMAKE_BUILD_TYPE=DEBUG -DUSE_ADVANCE_TESTING=ON ..
- make
- CTEST_OUTPUT_ON_FAILURE=TRUE make test
......@@ -49,7 +49,7 @@ spetabaru-debug-coverage:
script:
- mkdir build
- cd build
- VERBOSE=1 CXX=g++-7 cmake -DCMAKE_BUILD_TYPE=DEBUG -DSPETABARU_USE_COVERAGE=ON ..
- VERBOSE=1 CXX=g++-8 cmake -DCMAKE_BUILD_TYPE=DEBUG -DSPETABARU_USE_COVERAGE=ON ..
- make
- CTEST_OUTPUT_ON_FAILURE=TRUE make test
- lcov --directory ./ -c -o coverage-rapport.info
......
......@@ -122,6 +122,8 @@ if(NOT SPETABARU_AS_SUBPROJECT)
set(SPETABARU_CXX_FLAGS "${SPETABARU_CXX_FLAGS} -m64")
endif()
OPTION(SPETABARU_COMPILE_WITH_CUDA "Set to on to compile tasks with GPU callables" OFF)
OPTION( SPETABARU_ATTACHE_SOURCE "Set to on to add -g flag" OFF )
if( SPETABARU_ATTACHE_SOURCE )
set(SPETABARU_CXX_FLAGS "${SPETABARU_CXX_FLAGS} -g")
......@@ -136,8 +138,8 @@ endif()
#===========================================================================
# Generate config
#===========================================================================
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/Src/SPETABARUConfig.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/Src/SPETABARUConfig.h )
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/Src/Config/SpConfig.hpp.cmake
${CMAKE_CURRENT_BINARY_DIR}/Src/Config/SpConfig.hpp )
#===========================================================================
# Build lib
......
......@@ -2,12 +2,21 @@
// SPETABARU - Berenger Bramas MPCDF - 2016
// Under LGPL Licence, please you must read the LICENCE file.
///////////////////////////////////////////////////////////////////////////
#ifndef SPETABARUCONFIG_H
#define SPETABARUCONFIG_H
#ifndef SPCONFIG_H
#define SPCONFIG_H
// Define all macros (ADD-NEW-HERE)
// #cmakedefine SPETABARU_USE_X
// @SPETABARU_X@
#cmakedefine SPETABARU_COMPILE_WITH_CUDA
namespace SpConfig {
#ifdef SPETABARU_COMPILE_WITH_CUDA
inline constexpr bool CompileWithCuda = true;
#else
inline constexpr bool CompileWithCuda = false;
#endif
}
#endif
This diff is collapsed.
......@@ -117,6 +117,7 @@ public:
virtual bool hasMode(const SpDataAccessMode inMode) const = 0;
virtual small_vector<std::pair<SpDataHandle*,SpDataAccessMode>> getDataHandles() const = 0;
virtual void executeCallback() = 0;
virtual bool hasCallableOfType(const SpCallableType sct) const = 0;
virtual std::string getTaskBodyString() = 0;
void useDependences() {
......@@ -333,6 +334,10 @@ public:
target->setOriginalTask(inOriginal);
}
bool hasCallableOfType(const SpCallableType sct) const {
return target->hasCallableOfType(sct);
}
friend SpAbstractTaskWithReturn;
};
private:
......@@ -433,6 +438,10 @@ public:
void setOriginalTask(SpAbstractTask* inOriginal){// For speculation
target->setOriginalTask(inOriginal);
}
bool hasCallableOfType(const SpCallableType sct) const {
return target->hasCallableOfType(sct);
}
friend SpAbstractTaskWithReturn;
};
......
......@@ -17,23 +17,19 @@
#include <cxxabi.h>
#endif
template <class TaskFuncType, class RetType, class ... Params>
template <class RetType, class DataDependencyTupleTy, class CallableTupleTy>
class SpTask : public SpAbstractTaskWithReturn<RetType> {
using Parent = SpAbstractTask;
using ParentReturn = SpAbstractTaskWithReturn<RetType>;
using TupleParamsType = std::tuple<Params...>;
//! Number of parameters in the task function prototype
static const long int NbParams = sizeof...(Params);
static const long int NbParams = std::tuple_size<DataDependencyTupleTy>::value;
//! Internal value for undefined dependences
static constexpr long int UndefinedKey(){
return -1;
}
//! Function to call
TaskFuncType taskCallback;
//! Data handles
std::array<SpDataHandle*,NbParams> dataHandles;
//! Dependences' keys for each handle
......@@ -44,45 +40,49 @@ class SpTask : public SpAbstractTaskWithReturn<RetType> {
//! Extra handles's dependences keys
small_vector<long int> dataHandlesKeysExtra;
//! Params (inside data mode)
TupleParamsType tupleParams;
//! DataDependency objects
DataDependencyTupleTy tupleParams;
//! Callables
CallableTupleTy callables;
///////////////////////////////////////////////////////////////////////////////
/// Methods to call the task function with a conversion from handle to data
///////////////////////////////////////////////////////////////////////////////
//! Expand the tuple with the index and call getView
template <std::size_t... Is>
static RetType SpTaskCoreWrapper(TaskFuncType& taskCallback, TupleParamsType& params, std::index_sequence<Is...>){
return taskCallback( std::get<Is>(params).getView() ... );
template <class CallableTy, std::size_t... Is>
static RetType SpTaskCoreWrapper(CallableTy &callable, DataDependencyTupleTy &dataDep, std::index_sequence<Is...>){
return std::invoke(callable.getCallableRef(), std::get<Is>(dataDep).getView()...);
}
//! Dispatch use if RetType is not void (will set parent value with the return from function)
template <class SRetType>
static void executeCore(SpAbstractTaskWithReturn<SRetType>* taskObject, TaskFuncType& taskCallback, TupleParamsType& params) {
taskObject->setValue(SpTaskCoreWrapper(taskCallback, params, std::make_index_sequence<std::tuple_size<TupleParamsType>::value>{}));
template <class SRetType, class CallableTy>
static void executeCore(SpAbstractTaskWithReturn<SRetType>* taskObject, CallableTy &callable, DataDependencyTupleTy &dataDep) {
taskObject->setValue(SpTaskCoreWrapper(callable, dataDep, std::make_index_sequence<std::tuple_size<DataDependencyTupleTy>::value>{}));
}
//! Dispatch use if RetType is void (will not set parent value with the return from function)
static void executeCore(SpAbstractTaskWithReturn<void>* /*taskObject*/, TaskFuncType& taskCallback, TupleParamsType& params) {
SpTaskCoreWrapper(taskCallback, params, std::make_index_sequence<std::tuple_size<TupleParamsType>::value>{});
template <class CallableTy>
static void executeCore(SpAbstractTaskWithReturn<void>* /*taskObject*/, CallableTy &callable, DataDependencyTupleTy &dataDep) {
SpTaskCoreWrapper(callable, dataDep, std::make_index_sequence<NbParams>{});
}
//! Called by parent abstract task class
void executeCore() final {
//Equivalent to ParentReturn::setValue(SpTaskCoreWrapper(taskCallback, dataHandles.data()));
executeCore(this, taskCallback, tupleParams);
executeCore(this, std::get<0>(callables), tupleParams);
}
public:
//! Constructor from a task function
template <class TaskFuncTypeCstr, typename... T>
explicit SpTask(TaskFuncTypeCstr&& inTaskCallback, const SpTaskActivation initialActivationState,
const SpPriority& inPriority,
TupleParamsType&& inTupleParams, T... t)
template <typename... T>
explicit SpTask(const SpTaskActivation initialActivationState,
const SpPriority &inPriority,
DataDependencyTupleTy &&inDataDepTuple,
CallableTupleTy &&inCallableTuple, T... t)
: SpAbstractTaskWithReturn<RetType>(initialActivationState, inPriority),
taskCallback(std::forward<TaskFuncTypeCstr>(inTaskCallback)),
tupleParams(inTupleParams){
tupleParams(inDataDepTuple),
callables(std::move(inCallableTuple)) {
((void) t, ...);
std::fill_n(dataHandles.data(), NbParams, nullptr);
std::fill_n(dataHandlesKeys.data(), NbParams, UndefinedKey());
......@@ -90,14 +90,18 @@ public:
#ifdef __GNUG__
// if GCC then we ask for a clean type as default task name
int status;
char *demangledName = abi::__cxa_demangle(typeid(TaskFuncType).name(), 0, 0, &status);
char *demangledName = abi::__cxa_demangle(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name(), 0, 0, &status);
Parent::setTaskName(demangledName);
free(demangledName);
#else
Parent::setTaskName(typeid(TaskFuncType).name());
Parent::setTaskName(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name());
#endif
}
DataDependencyTupleTy& getDataDependencyTupleRef() {
return tupleParams;
}
//! Set the dependence at position HandleIdx in the prototype
template <long int HandleIdx>
void setDataHandle(SpDataHandle* inData, const long int inHandleKey){
......@@ -332,6 +336,14 @@ public:
return true;
}
bool hasCallableOfType([[maybe_unused]] const SpCallableType sct) const override final {
if constexpr(std::tuple_size_v<CallableTupleTy> == 1) {
return std::tuple_element_t<0, CallableTupleTy>::callable_type == sct;
} else {
return true;
}
}
std::string getTaskBodyString() override {
......@@ -348,22 +360,21 @@ public:
}
};
template <class TaskFuncType, class RetType, class ... Params>
class SpSelectTask : public SpTask<TaskFuncType, RetType, Params...>
template <class RetType, class DataDependencyTupleTy, class CallableTupleTy>
class SpSelectTask : public SpTask<RetType, DataDependencyTupleTy, CallableTupleTy>
{
using Parent = SpTask<TaskFuncType, RetType, Params...>;
using TupleParamsType = std::tuple<Params...>;
using Parent = SpTask<RetType, DataDependencyTupleTy, CallableTupleTy>;
// flag indicating if the select task is carrying surely written values over
bool isCarrSurWrittValuesOver;
public:
template <class TaskFuncTypeCstr, typename... T>
explicit SpSelectTask(TaskFuncTypeCstr&& inTaskCallback, const SpTaskActivation initialActivationState,
explicit SpSelectTask(const SpTaskActivation initialActivationState,
const SpPriority& inPriority,
TupleParamsType&& inTupleParams, bool iCSWVO)
: Parent(std::forward<TaskFuncTypeCstr>(inTaskCallback), initialActivationState, inPriority,
std::forward<TupleParamsType>(inTupleParams)), isCarrSurWrittValuesOver(iCSWVO) {}
DataDependencyTupleTy &&inDataDepTuple,
CallableTupleTy &&inCallableTuple, bool iCSWVO)
: Parent(initialActivationState, inPriority,
std::move(inDataDepTuple), std::move(inCallableTuple)), isCarrSurWrittValuesOver(iCSWVO) {}
void setEnabledDelegate(const SpTaskActivation inIsEnable) override final {
if((inIsEnable == SpTaskActivation::DISABLE && !isCarrSurWrittValuesOver)
......
......@@ -8,6 +8,7 @@
#include <type_traits>
#include <array>
#include "Config/SpConfig.hpp"
#include "SpArrayView.hpp"
#include "SpDebug.hpp"
#include "Data/SpDataDuplicator.hpp"
......@@ -286,4 +287,67 @@ struct has_getAllData : std::false_type {};
template<class T>
struct has_getAllData<T, void_t<decltype(std::declval<T>().getAllData())>> : std::true_type {};
template <SpDataAccessMode dam1, SpDataAccessMode dam2>
struct access_modes_are_equal_internal : std::conditional_t<dam1==dam2, std::true_type, std::false_type> {};
template<SpDataAccessMode dam1, typename T, typename = std::void_t<>>
struct access_modes_are_equal : std::false_type {};
template <SpDataAccessMode dam1, typename T>
struct access_modes_are_equal<dam1, T, std::void_t<decltype(T::AccessMode)>> : access_modes_are_equal_internal<dam1, T::AccessMode> {};
enum class SpCallableType {
CPU=0,
GPU
};
template <bool compileWithCuda, class T, SpCallableType ct>
class SpCallableWrapper {
private:
using CallableTy = std::remove_reference_t<T>;
CallableTy callable;
public:
static constexpr auto callable_type = ct;
template <typename T2, typename=std::enable_if_t<std::is_same<std::remove_reference_t<T2>, CallableTy>::value>>
SpCallableWrapper(T2 &&inCallable) : callable(std::forward<T2>(inCallable)) {}
CallableTy& getCallableRef() {
return callable;
}
};
template <class T, SpCallableType ct>
class SpCallableWrapper<false, T, ct> {
public:
template<typename T2>
SpCallableWrapper(T2&&) {}
};
template <class T>
auto SpCpu(T &&callable) {
return SpCallableWrapper<true, T, SpCallableType::CPU>(std::forward<T>(callable));
}
template <class T>
auto SpGpu(T &&callable) {
return SpCallableWrapper<SpConfig::CompileWithCuda, T, SpCallableType::GPU>(std::forward<T>(callable));
}
template <class T0>
struct is_instantiation_of_callable_wrapper : std::false_type {};
template <bool b, class T0, SpCallableType ct>
struct is_instantiation_of_callable_wrapper<SpCallableWrapper<b, T0, ct>> : std::true_type {};
template <class T0, SpCallableType callableType0>
struct is_instantiation_of_callable_wrapper_with_type : std::false_type {};
template <bool b, class T0, SpCallableType callableType0, SpCallableType callableType1>
struct is_instantiation_of_callable_wrapper_with_type<SpCallableWrapper<b, T0, callableType1>, callableType0> :
std::conditional_t<callableType0==callableType1, std::true_type, std::false_type> {};
template <class T, SpCallableType callableType>
inline constexpr bool is_instantiation_of_callable_wrapper_with_type_v = is_instantiation_of_callable_wrapper_with_type<T, callableType>::value;
#endif
......@@ -9,6 +9,7 @@
#include <string>
#include <sstream>
#include <cstring>
#include <functional>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
......@@ -113,12 +114,40 @@ namespace SpUtils{
exit(-1);
}
}
template <typename CallableTy, typename TupleTy, std::size_t... Is,
std::enable_if_t<std::conjunction_v<std::is_invocable<CallableTy, std::tuple_element_t<Is, std::remove_reference_t<TupleTy>>>...>, int> = 0>
static void foreach_in_tuple_impl(CallableTy &&c, TupleTy &&t, std::index_sequence<Is...>) {
if constexpr(sizeof...(Is) > 0) {
using RetTy = std::invoke_result_t<CallableTy, std::tuple_element_t<0, std::remove_reference_t<TupleTy>>>;
if constexpr(std::is_same_v<RetTy, bool>) {
(std::invoke(std::forward<CallableTy>(c), std::get<Is>(std::forward<TupleTy>(t))) || ...);
} else {
(static_cast<void>(std::invoke(std::forward<CallableTy>(c), std::get<Is>(std::forward<TupleTy>(t)))),...);
}
}
}
template <template <typename...> typename Template, typename T>
struct is_instantiation_of : std::false_type {};
template <template <typename...> typename Template, typename... Args>
struct is_instantiation_of<Template, Template<Args...> > : std::true_type {};
template <typename CallableTy, typename TupleTy, std::size_t... Is,
std::enable_if_t<std::conjunction_v<std::is_invocable<CallableTy, std::integral_constant<size_t, Is>,
std::tuple_element_t<Is, std::remove_reference_t<TupleTy>>>...>, int> = 0>
static void foreach_in_tuple_impl(CallableTy &&c, TupleTy &&t, std::index_sequence<Is...>) {
if constexpr(sizeof...(Is) > 0) {
using RetTy = std::invoke_result_t<CallableTy, std::integral_constant<size_t, 0>, std::tuple_element_t<0, std::remove_reference_t<TupleTy>>>;
if constexpr(std::is_same_v<RetTy, bool>) {
(std::invoke(std::forward<CallableTy>(c), std::integral_constant<size_t, Is>{}, std::get<Is>(std::forward<TupleTy>(t))) || ...);
} else {
(static_cast<void>(std::invoke(std::forward<CallableTy>(c), std::integral_constant<size_t, Is>{}, std::get<Is>(std::forward<TupleTy>(t)))),...);
}
}
}
template <typename CallableTy, typename TupleTy>
static void foreach_in_tuple(CallableTy &&c, TupleTy &&t) {
foreach_in_tuple_impl(std::forward<CallableTy>(c), std::forward<TupleTy>(t), std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<TupleTy>>>{});
}
}
#define spetabaru_xstr(s) spetabaru_str(s)
......
///////////////////////////////////////////////////////////////////////////
// Spetabaru - Berenger Bramas MPCDF - 2017
// Under LGPL Licence, please you must read the LICENCE file.
///////////////////////////////////////////////////////////////////////////
#include "Config/SpConfig.hpp"
#include "Utils/SpModes.hpp"
#include "Utils/SpUtils.hpp"
#include "Tasks/SpTask.hpp"
#include "Runtimes/SpRuntime.hpp"
// @NBTESTS = 5
int main(){
const int NumThreads = SpUtils::DefaultNumThreads();
SpRuntime runtime(NumThreads);
const double initVal = 1.0;
double writeVal = 0.0;
#ifdef TEST1
runtime.task(SpRead(initVal),
[]([[maybe_unused]] const double& initValParam){},
[]([[maybe_unused]] const double& initValParam){});
#endif
#ifdef TEST2
runtime.task(SpRead(initVal),
SpCpu([]([[maybe_unused]] const double& initValParam){}),
SpCpu([]([[maybe_unused]] const double& initValParam){}));
#endif
#ifdef TEST3
runtime.task(SpRead(initVal),
SpGpu([]([[maybe_unused]] const double& initValParam){})
SpGpu([]([[maybe_unused]] const double& initValParam){}));
#endif
#ifdef TEST4
if constexpr(!SpConfig::CompileWithCuda) {
runtime.task(SpRead(initVal),
SpGpu([]([[maybe_unused]] const double& initValParam){}));
} else {
static_assert(false, "Force wrongPrototypeExtended-4 to fail.");
}
#endif
#ifdef TEST5
runtime.task(SpProbability(0.25), SpRead(initVal), SpWrite(writeVal),
SpCpu([]([[maybe_unused]] const double& initValParam, double& writeValParam) {
writeValParam = 42;
}));
#endif
runtime.waitAllTasks();
runtime.stopAllThreads();
}
///////////////////////////////////////////////////////////////////////////
// Spetabaru - Berenger Bramas MPCDF - 2017
// Under LGPL Licence, please you must read the LICENCE file.
///////////////////////////////////////////////////////////////////////////
#include "UTester.hpp"
#include "utestUtils.hpp"
#include "Utils/SpModes.hpp"
#include "Utils/SpUtils.hpp"
#include "Utils/SpArrayView.hpp"
#include "Utils/SpArrayAccessor.hpp"
#include "Tasks/SpTask.hpp"
#include "Runtimes/SpRuntime.hpp"
class TestCallableWrappers : public UTester< TestCallableWrappers > {
using Parent = UTester< TestCallableWrappers >;
template <SpSpeculativeModel Spm>
void Test(){
SpRuntime<Spm> runtime;
runtime.setSpeculationTest([](const int /*inNbReadyTasks*/, const SpProbability& /*inProbability*/) -> bool{
return true;
});
int a=0;
runtime.task(SpWrite(a), [](int& param_a){
param_a++;
});
runtime.task(SpWrite(a), SpCpu([](int& param_a){
param_a++;
}));
runtime.task(SpWrite(a),
[](int& param_a){
param_a++;
},
SpGpu([](int& param_a){
param_a++;
}));
runtime.task(SpWrite(a),
SpCpu([](int& param_a){
param_a++;
}),
SpGpu([](int& param_a){
param_a++;
}));
runtime.task(SpWrite(a),
SpGpu([](int& param_a){
param_a++;
}),
[](int& param_a){
param_a++;
});
runtime.task(SpWrite(a),
SpGpu([](int& param_a){
param_a++;
}),
SpCpu([](int& param_a){
param_a++;
}));
runtime.waitAllTasks();
runtime.stopAllThreads();
UASSERTETRUE(a == 6);
}
void Test1() { Test<SpSpeculativeModel::SP_MODEL_1>(); }
void SetTests() {
Parent::AddTest(&TestCallableWrappers::Test1, "Test callable wrappers.");
}
};
// You must do this
TestClass(TestCallableWrappers)
......@@ -24,8 +24,8 @@ class MethodTest : public UTester< MethodTest > {
class C_constint{
public:
C_constint() = default;
C_constint(const C_constint&) = delete;
C_constint& operator=(const C_constint&) = delete;
C_constint(const C_constint&) = default;
C_constint& operator=(const C_constint&) = default;
C_constint(C_constint&&) = default;
C_constint& operator=(C_constint&&) = default;
......@@ -43,8 +43,8 @@ class MethodTest : public UTester< MethodTest > {
class C_constint_intref{
public:
C_constint_intref() = default;
C_constint_intref(const C_constint_intref&) = delete;
C_constint_intref& operator=(const C_constint_intref&) = delete;
C_constint_intref(const C_constint_intref&) = default;
C_constint_intref& operator=(const C_constint_intref&) = default;
C_constint_intref(C_constint_intref&&) = default;
C_constint_intref& operator=(C_constint_intref&&) = default;
......@@ -63,8 +63,8 @@ class MethodTest : public UTester< MethodTest > {
class C_intref{
public:
C_intref() = default;
C_intref(const C_intref&) = delete;
C_intref& operator=(const C_intref&) = delete;
C_intref(const C_intref&) = default;
C_intref& operator=(const C_intref&) = default;
C_intref(C_intref&&) = default;
C_intref& operator=(C_intref&&) = default;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment